4 #include <apt-pkg/cachefile.h>
5 #include <apt-pkg/cacheset.h>
6 #include <apt-pkg/cmndline.h>
7 #include <apt-pkg/pkgrecords.h>
8 #include <apt-pkg/policy.h>
9 #include <apt-pkg/progress.h>
10 #include <apt-pkg/cacheiterators.h>
11 #include <apt-pkg/configuration.h>
12 #include <apt-pkg/depcache.h>
13 #include <apt-pkg/macros.h>
14 #include <apt-pkg/pkgcache.h>
16 #include <apt-private/private-cacheset.h>
17 #include <apt-private/private-output.h>
18 #include <apt-private/private-search.h>
19 #include <apt-private/private-show.h>
31 static bool FullTextSearch(CommandLine
&CmdL
) /*{{{*/
33 pkgCacheFile CacheFile
;
34 pkgCache
*Cache
= CacheFile
.GetPkgCache();
35 pkgDepCache::Policy
*Plcy
= CacheFile
.GetPolicy();
36 if (unlikely(Cache
== NULL
|| Plcy
== NULL
))
39 // Make sure there is at least one argument
40 unsigned int const NumPatterns
= CmdL
.FileSize() -1;
42 return _error
->Error(_("You must give at least one search pattern"));
44 #define APT_FREE_PATTERNS() for (std::vector<regex_t>::iterator P = Patterns.begin(); \
45 P != Patterns.end(); ++P) { regfree(&(*P)); }
47 // Compile the regex pattern
48 std::vector
<regex_t
> Patterns
;
49 for (unsigned int I
= 0; I
!= NumPatterns
; ++I
)
52 if (regcomp(&pattern
, CmdL
.FileList
[I
+ 1], REG_EXTENDED
| REG_ICASE
| REG_NOSUB
) != 0)
55 return _error
->Error("Regex compilation error");
57 Patterns
.push_back(pattern
);
60 std::map
<std::string
, std::string
> output_map
;
62 LocalitySortedVersionSet bag
;
63 OpTextProgress
progress(*_config
);
64 progress
.OverallProgress(0, 100, 50, _("Sorting"));
65 GetLocalitySortedVersionSet(CacheFile
, &bag
, &progress
);
66 LocalitySortedVersionSet::iterator V
= bag
.begin();
68 progress
.OverallProgress(50, 100, 50, _("Full Text Search"));
69 progress
.SubProgress(bag
.size());
70 pkgRecords
records(CacheFile
);
72 std::string format
= "${color:highlight}${Package}${color:neutral}/${Origin} ${Version} ${Architecture}${ }${apt:Status}\n";
73 if (_config
->FindB("APT::Cache::ShowFull",false) == false)
74 format
+= " ${Description}\n";
76 format
+= " ${LongDescription}\n";
78 bool const NamesOnly
= _config
->FindB("APT::Cache::NamesOnly", false);
80 std::vector
<bool> PkgsDone(Cache
->Head().PackageCount
, false);
81 for ( ;V
!= bag
.end(); ++V
)
84 progress
.Progress(Done
);
87 // we want to list each package only once
88 pkgCache::PkgIterator
const P
= V
.ParentPkg();
89 if (PkgsDone
[P
->ID
] == true)
92 char const * const PkgName
= P
.Name();
93 pkgCache::DescIterator Desc
= V
.TranslatedDescription();
94 std::string LongDesc
= "";
96 pkgRecords::Parser
&parser
= records
.Lookup(Desc
.FileList());
97 LongDesc
= parser
.LongDesc();
100 bool all_found
= true;
101 for (std::vector
<regex_t
>::const_iterator pattern
= Patterns
.begin();
102 pattern
!= Patterns
.end(); ++pattern
)
104 if (regexec(&(*pattern
), PkgName
, 0, 0, 0) == 0)
106 else if (NamesOnly
== false && regexec(&(*pattern
), LongDesc
.c_str(), 0, 0, 0) == 0)
108 // search patterns are AND, so one failing fails all
112 if (all_found
== true)
114 PkgsDone
[P
->ID
] = true;
115 std::stringstream outs
;
116 ListSingleVersion(CacheFile
, records
, V
, outs
, format
);
117 output_map
.insert(std::make_pair
<std::string
, std::string
>(
118 PkgName
, outs
.str()));
124 // FIXME: SORT! and make sorting flexible (alphabetic, by pkg status)
125 // output the sorted map
126 std::map
<std::string
, std::string
>::const_iterator K
;
127 for (K
= output_map
.begin(); K
!= output_map
.end(); ++K
)
128 std::cout
<< (*K
).second
<< std::endl
;
133 // LocalitySort - Sort a version list by package file locality /*{{{*/
134 static int LocalityCompare(const void * const a
, const void * const b
)
136 pkgCache::VerFile
const * const A
= *(pkgCache::VerFile
const * const * const)a
;
137 pkgCache::VerFile
const * const B
= *(pkgCache::VerFile
const * const * const)b
;
139 if (A
== 0 && B
== 0)
146 if (A
->File
== B
->File
)
147 return A
->Offset
- B
->Offset
;
148 return A
->File
- B
->File
;
150 void LocalitySort(pkgCache::VerFile
** const begin
, unsigned long long const Count
,size_t const Size
)
152 qsort(begin
,Count
,Size
,LocalityCompare
);
154 static void LocalitySort(pkgCache::DescFile
** const begin
, unsigned long long const Count
,size_t const Size
)
156 qsort(begin
,Count
,Size
,LocalityCompare
);
159 // Search - Perform a search /*{{{*/
160 // ---------------------------------------------------------------------
161 /* This searches the package names and package descriptions for a pattern */
164 pkgCache::DescFile
*Df
;
165 pkgCache::VerIterator V
;
168 static bool Search(CommandLine
&CmdL
)
170 bool const ShowFull
= _config
->FindB("APT::Cache::ShowFull",false);
171 unsigned int const NumPatterns
= CmdL
.FileSize() -1;
173 pkgCacheFile CacheFile
;
174 pkgCache
*Cache
= CacheFile
.GetPkgCache();
175 pkgDepCache::Policy
*Plcy
= CacheFile
.GetPolicy();
176 if (unlikely(Cache
== NULL
|| Plcy
== NULL
))
179 // Make sure there is at least one argument
181 return _error
->Error(_("You must give at least one search pattern"));
183 // Compile the regex pattern
184 regex_t
*Patterns
= new regex_t
[NumPatterns
];
185 memset(Patterns
,0,sizeof(*Patterns
)*NumPatterns
);
186 for (unsigned I
= 0; I
!= NumPatterns
; I
++)
188 if (regcomp(&Patterns
[I
],CmdL
.FileList
[I
+1],REG_EXTENDED
| REG_ICASE
|
192 regfree(&Patterns
[I
]);
193 return _error
->Error("Regex compilation error");
197 if (_error
->PendingError() == true)
199 for (unsigned I
= 0; I
!= NumPatterns
; I
++)
200 regfree(&Patterns
[I
]);
204 size_t const descCount
= Cache
->HeaderP
->GroupCount
+ 1;
205 ExDescFile
*DFList
= new ExDescFile
[descCount
];
206 memset(DFList
,0,sizeof(*DFList
) * descCount
);
208 bool *PatternMatch
= new bool[descCount
* NumPatterns
];
209 memset(PatternMatch
,false,sizeof(*PatternMatch
) * descCount
* NumPatterns
);
211 // Map versions that we want to write out onto the VerList array.
212 bool const NamesOnly
= _config
->FindB("APT::Cache::NamesOnly",false);
213 for (pkgCache::GrpIterator G
= Cache
->GrpBegin(); G
.end() == false; ++G
)
215 size_t const PatternOffset
= G
->ID
* NumPatterns
;
216 size_t unmatched
= 0, matched
= 0;
217 for (unsigned I
= 0; I
< NumPatterns
; ++I
)
219 if (PatternMatch
[PatternOffset
+ I
] == true)
221 else if (regexec(&Patterns
[I
],G
.Name(),0,0,0) == 0)
222 PatternMatch
[PatternOffset
+ I
] = true;
227 // already dealt with this package?
228 if (matched
== NumPatterns
)
231 // Doing names only, drop any that don't match..
232 if (NamesOnly
== true && unmatched
== NumPatterns
)
235 // Find the proper version to use
236 pkgCache::PkgIterator P
= G
.FindPreferredPkg();
239 pkgCache::VerIterator V
= Plcy
->GetCandidateVer(P
);
240 if (V
.end() == false)
242 pkgCache::DescIterator
const D
= V
.TranslatedDescription();
243 //FIXME: packages without a description can't be found
246 DFList
[G
->ID
].Df
= D
.FileList();
248 DFList
[G
->ID
].ID
= G
->ID
;
251 if (unmatched
== NumPatterns
)
254 // Include all the packages that provide matching names too
255 for (pkgCache::PrvIterator Prv
= P
.ProvidesList() ; Prv
.end() == false; ++Prv
)
257 pkgCache::VerIterator V
= Plcy
->GetCandidateVer(Prv
.OwnerPkg());
261 unsigned long id
= Prv
.OwnerPkg().Group()->ID
;
262 pkgCache::DescIterator
const D
= V
.TranslatedDescription();
263 //FIXME: packages without a description can't be found
266 DFList
[id
].Df
= D
.FileList();
270 size_t const PrvPatternOffset
= id
* NumPatterns
;
271 for (unsigned I
= 0; I
< NumPatterns
; ++I
)
272 PatternMatch
[PrvPatternOffset
+ I
] |= PatternMatch
[PatternOffset
+ I
];
276 LocalitySort(&DFList
->Df
, Cache
->HeaderP
->GroupCount
, sizeof(*DFList
));
278 // Create the text record parser
279 pkgRecords
Recs(*Cache
);
280 // Iterate over all the version records and check them
281 for (ExDescFile
*J
= DFList
; J
->Df
!= 0; ++J
)
283 pkgRecords::Parser
&P
= Recs
.Lookup(pkgCache::DescFileIterator(*Cache
,J
->Df
));
284 size_t const PatternOffset
= J
->ID
* NumPatterns
;
286 if (NamesOnly
== false)
288 std::string
const LongDesc
= P
.LongDesc();
289 for (unsigned I
= 0; I
< NumPatterns
; ++I
)
291 if (PatternMatch
[PatternOffset
+ I
] == true)
293 else if (regexec(&Patterns
[I
],LongDesc
.c_str(),0,0,0) == 0)
294 PatternMatch
[PatternOffset
+ I
] = true;
298 bool matchedAll
= true;
299 for (unsigned I
= 0; I
< NumPatterns
; ++I
)
300 if (PatternMatch
[PatternOffset
+ I
] == false)
306 if (matchedAll
== true)
308 if (ShowFull
== true)
309 DisplayRecordV1(CacheFile
, J
->V
, std::cout
);
311 printf("%s - %s\n",P
.Name().c_str(),P
.ShortDesc().c_str());
316 delete [] PatternMatch
;
317 for (unsigned I
= 0; I
!= NumPatterns
; I
++)
318 regfree(&Patterns
[I
]);
321 return _error
->Error("Write to stdout failed");
325 bool DoSearch(CommandLine
&CmdL
) /*{{{*/
327 int const ShowVersion
= _config
->FindI("APT::Cache::Search::Version", 1);
328 if (ShowVersion
<= 1)
330 return FullTextSearch(CmdL
);