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 pkgRecords::Parser
&parser
= records
.Lookup(Desc
.FileList());
95 std::string
const LongDesc
= parser
.LongDesc();
97 bool all_found
= true;
98 for (std::vector
<regex_t
>::const_iterator pattern
= Patterns
.begin();
99 pattern
!= Patterns
.end(); ++pattern
)
101 if (regexec(&(*pattern
), PkgName
, 0, 0, 0) == 0)
103 else if (NamesOnly
== false && regexec(&(*pattern
), LongDesc
.c_str(), 0, 0, 0) == 0)
105 // search patterns are AND, so one failing fails all
109 if (all_found
== true)
111 PkgsDone
[P
->ID
] = true;
112 std::stringstream outs
;
113 ListSingleVersion(CacheFile
, records
, V
, outs
, format
);
114 output_map
.insert(std::make_pair
<std::string
, std::string
>(
115 PkgName
, outs
.str()));
121 // FIXME: SORT! and make sorting flexible (alphabetic, by pkg status)
122 // output the sorted map
123 std::map
<std::string
, std::string
>::const_iterator K
;
124 for (K
= output_map
.begin(); K
!= output_map
.end(); ++K
)
125 std::cout
<< (*K
).second
<< std::endl
;
130 // LocalitySort - Sort a version list by package file locality /*{{{*/
131 static int LocalityCompare(const void * const a
, const void * const b
)
133 pkgCache::VerFile
const * const A
= *(pkgCache::VerFile
const * const * const)a
;
134 pkgCache::VerFile
const * const B
= *(pkgCache::VerFile
const * const * const)b
;
136 if (A
== 0 && B
== 0)
143 if (A
->File
== B
->File
)
144 return A
->Offset
- B
->Offset
;
145 return A
->File
- B
->File
;
147 void LocalitySort(pkgCache::VerFile
** const begin
, unsigned long long const Count
,size_t const Size
)
149 qsort(begin
,Count
,Size
,LocalityCompare
);
151 static void LocalitySort(pkgCache::DescFile
** const begin
, unsigned long long const Count
,size_t const Size
)
153 qsort(begin
,Count
,Size
,LocalityCompare
);
156 // Search - Perform a search /*{{{*/
157 // ---------------------------------------------------------------------
158 /* This searches the package names and package descriptions for a pattern */
161 pkgCache::DescFile
*Df
;
162 pkgCache::VerIterator V
;
165 static bool Search(CommandLine
&CmdL
)
167 bool const ShowFull
= _config
->FindB("APT::Cache::ShowFull",false);
168 unsigned int const NumPatterns
= CmdL
.FileSize() -1;
170 pkgCacheFile CacheFile
;
171 pkgCache
*Cache
= CacheFile
.GetPkgCache();
172 pkgDepCache::Policy
*Plcy
= CacheFile
.GetPolicy();
173 if (unlikely(Cache
== NULL
|| Plcy
== NULL
))
176 // Make sure there is at least one argument
178 return _error
->Error(_("You must give at least one search pattern"));
180 // Compile the regex pattern
181 regex_t
*Patterns
= new regex_t
[NumPatterns
];
182 memset(Patterns
,0,sizeof(*Patterns
)*NumPatterns
);
183 for (unsigned I
= 0; I
!= NumPatterns
; I
++)
185 if (regcomp(&Patterns
[I
],CmdL
.FileList
[I
+1],REG_EXTENDED
| REG_ICASE
|
189 regfree(&Patterns
[I
]);
190 return _error
->Error("Regex compilation error");
194 if (_error
->PendingError() == true)
196 for (unsigned I
= 0; I
!= NumPatterns
; I
++)
197 regfree(&Patterns
[I
]);
201 size_t const descCount
= Cache
->HeaderP
->GroupCount
+ 1;
202 ExDescFile
*DFList
= new ExDescFile
[descCount
];
203 memset(DFList
,0,sizeof(*DFList
) * descCount
);
205 bool *PatternMatch
= new bool[descCount
* NumPatterns
];
206 memset(PatternMatch
,false,sizeof(*PatternMatch
) * descCount
* NumPatterns
);
208 // Map versions that we want to write out onto the VerList array.
209 bool const NamesOnly
= _config
->FindB("APT::Cache::NamesOnly",false);
210 for (pkgCache::GrpIterator G
= Cache
->GrpBegin(); G
.end() == false; ++G
)
212 size_t const PatternOffset
= G
->ID
* NumPatterns
;
213 size_t unmatched
= 0, matched
= 0;
214 for (unsigned I
= 0; I
< NumPatterns
; ++I
)
216 if (PatternMatch
[PatternOffset
+ I
] == true)
218 else if (regexec(&Patterns
[I
],G
.Name(),0,0,0) == 0)
219 PatternMatch
[PatternOffset
+ I
] = true;
224 // already dealt with this package?
225 if (matched
== NumPatterns
)
228 // Doing names only, drop any that don't match..
229 if (NamesOnly
== true && unmatched
== NumPatterns
)
232 // Find the proper version to use
233 pkgCache::PkgIterator P
= G
.FindPreferredPkg();
236 pkgCache::VerIterator V
= Plcy
->GetCandidateVer(P
);
237 if (V
.end() == false)
239 pkgCache::DescIterator
const D
= V
.TranslatedDescription();
240 //FIXME: packages without a description can't be found
243 DFList
[G
->ID
].Df
= D
.FileList();
245 DFList
[G
->ID
].ID
= G
->ID
;
248 if (unmatched
== NumPatterns
)
251 // Include all the packages that provide matching names too
252 for (pkgCache::PrvIterator Prv
= P
.ProvidesList() ; Prv
.end() == false; ++Prv
)
254 pkgCache::VerIterator V
= Plcy
->GetCandidateVer(Prv
.OwnerPkg());
258 unsigned long id
= Prv
.OwnerPkg().Group()->ID
;
259 pkgCache::DescIterator
const D
= V
.TranslatedDescription();
260 //FIXME: packages without a description can't be found
263 DFList
[id
].Df
= D
.FileList();
267 size_t const PrvPatternOffset
= id
* NumPatterns
;
268 for (unsigned I
= 0; I
< NumPatterns
; ++I
)
269 PatternMatch
[PrvPatternOffset
+ I
] |= PatternMatch
[PatternOffset
+ I
];
273 LocalitySort(&DFList
->Df
, Cache
->HeaderP
->GroupCount
, sizeof(*DFList
));
275 // Create the text record parser
276 pkgRecords
Recs(*Cache
);
277 // Iterate over all the version records and check them
278 for (ExDescFile
*J
= DFList
; J
->Df
!= 0; ++J
)
280 pkgRecords::Parser
&P
= Recs
.Lookup(pkgCache::DescFileIterator(*Cache
,J
->Df
));
281 size_t const PatternOffset
= J
->ID
* NumPatterns
;
283 if (NamesOnly
== false)
285 std::string
const LongDesc
= P
.LongDesc();
286 for (unsigned I
= 0; I
< NumPatterns
; ++I
)
288 if (PatternMatch
[PatternOffset
+ I
] == true)
290 else if (regexec(&Patterns
[I
],LongDesc
.c_str(),0,0,0) == 0)
291 PatternMatch
[PatternOffset
+ I
] = true;
295 bool matchedAll
= true;
296 for (unsigned I
= 0; I
< NumPatterns
; ++I
)
297 if (PatternMatch
[PatternOffset
+ I
] == false)
303 if (matchedAll
== true)
305 if (ShowFull
== true)
306 DisplayRecordV1(CacheFile
, J
->V
, std::cout
);
308 printf("%s - %s\n",P
.Name().c_str(),P
.ShortDesc().c_str());
313 delete [] PatternMatch
;
314 for (unsigned I
= 0; I
!= NumPatterns
; I
++)
315 regfree(&Patterns
[I
]);
318 return _error
->Error("Write to stdout failed");
322 bool DoSearch(CommandLine
&CmdL
) /*{{{*/
324 int const ShowVersion
= _config
->FindI("APT::Cache::Search::Version", 1);
325 if (ShowVersion
<= 1)
327 return FullTextSearch(CmdL
);