private-search.cc 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // Includes /*{{{*/
  2. #include <config.h>
  3. #include <apt-pkg/cachefile.h>
  4. #include <apt-pkg/cacheset.h>
  5. #include <apt-pkg/cmndline.h>
  6. #include <apt-pkg/pkgrecords.h>
  7. #include <apt-pkg/policy.h>
  8. #include <apt-pkg/progress.h>
  9. #include <apt-pkg/cacheiterators.h>
  10. #include <apt-pkg/configuration.h>
  11. #include <apt-pkg/depcache.h>
  12. #include <apt-pkg/macros.h>
  13. #include <apt-pkg/pkgcache.h>
  14. #include <apt-private/private-cacheset.h>
  15. #include <apt-private/private-output.h>
  16. #include <apt-private/private-search.h>
  17. #include <apt-private/private-show.h>
  18. #include <string.h>
  19. #include <iostream>
  20. #include <sstream>
  21. #include <map>
  22. #include <string>
  23. #include <utility>
  24. #include <apti18n.h>
  25. /*}}}*/
  26. static bool FullTextSearch(CommandLine &CmdL) /*{{{*/
  27. {
  28. pkgCacheFile CacheFile;
  29. pkgCache *Cache = CacheFile.GetPkgCache();
  30. pkgDepCache::Policy *Plcy = CacheFile.GetPolicy();
  31. if (unlikely(Cache == NULL || Plcy == NULL))
  32. return false;
  33. // Make sure there is at least one argument
  34. unsigned int const NumPatterns = CmdL.FileSize() -1;
  35. if (NumPatterns < 1)
  36. return _error->Error(_("You must give at least one search pattern"));
  37. #define APT_FREE_PATTERNS() for (std::vector<regex_t>::iterator P = Patterns.begin(); \
  38. P != Patterns.end(); ++P) { regfree(&(*P)); }
  39. // Compile the regex pattern
  40. std::vector<regex_t> Patterns;
  41. for (unsigned int I = 0; I != NumPatterns; ++I)
  42. {
  43. regex_t pattern;
  44. if (regcomp(&pattern, CmdL.FileList[I + 1], REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0)
  45. {
  46. APT_FREE_PATTERNS();
  47. return _error->Error("Regex compilation error");
  48. }
  49. Patterns.push_back(pattern);
  50. }
  51. std::map<std::string, std::string> output_map;
  52. LocalitySortedVersionSet bag;
  53. OpTextProgress progress(*_config);
  54. progress.OverallProgress(0, 100, 50, _("Sorting"));
  55. GetLocalitySortedVersionSet(CacheFile, &bag, &progress);
  56. LocalitySortedVersionSet::iterator V = bag.begin();
  57. progress.OverallProgress(50, 100, 50, _("Full Text Search"));
  58. progress.SubProgress(bag.size());
  59. pkgRecords records(CacheFile);
  60. std::string format = "${color:highlight}${Package}${color:neutral}/${Origin} ${Version} ${Architecture}${ }${apt:Status}\n";
  61. if (_config->FindB("APT::Cache::ShowFull",false) == false)
  62. format += " ${Description}\n";
  63. else
  64. format += " ${LongDescription}\n";
  65. bool const NamesOnly = _config->FindB("APT::Cache::NamesOnly", false);
  66. int Done = 0;
  67. std::vector<bool> PkgsDone(Cache->Head().PackageCount, false);
  68. for ( ;V != bag.end(); ++V)
  69. {
  70. if (Done%500 == 0)
  71. progress.Progress(Done);
  72. ++Done;
  73. // we want to list each package only once
  74. pkgCache::PkgIterator const P = V.ParentPkg();
  75. if (PkgsDone[P->ID] == true)
  76. continue;
  77. char const * const PkgName = P.Name();
  78. pkgCache::DescIterator Desc = V.TranslatedDescription();
  79. std::string LongDesc = "";
  80. if (Desc.end() == false)
  81. {
  82. pkgRecords::Parser &parser = records.Lookup(Desc.FileList());
  83. LongDesc = parser.LongDesc();
  84. }
  85. bool all_found = true;
  86. for (std::vector<regex_t>::const_iterator pattern = Patterns.begin();
  87. pattern != Patterns.end(); ++pattern)
  88. {
  89. if (regexec(&(*pattern), PkgName, 0, 0, 0) == 0)
  90. continue;
  91. else if (NamesOnly == false && regexec(&(*pattern), LongDesc.c_str(), 0, 0, 0) == 0)
  92. continue;
  93. // search patterns are AND, so one failing fails all
  94. all_found = false;
  95. break;
  96. }
  97. if (all_found == true)
  98. {
  99. PkgsDone[P->ID] = true;
  100. std::stringstream outs;
  101. ListSingleVersion(CacheFile, records, V, outs, format);
  102. output_map.insert(std::make_pair<std::string, std::string>(
  103. PkgName, outs.str()));
  104. }
  105. }
  106. APT_FREE_PATTERNS();
  107. progress.Done();
  108. // FIXME: SORT! and make sorting flexible (alphabetic, by pkg status)
  109. // output the sorted map
  110. std::map<std::string, std::string>::const_iterator K;
  111. for (K = output_map.begin(); K != output_map.end(); ++K)
  112. std::cout << (*K).second << std::endl;
  113. return true;
  114. }
  115. /*}}}*/
  116. // LocalitySort - Sort a version list by package file locality /*{{{*/
  117. static int LocalityCompare(const void * const a, const void * const b)
  118. {
  119. pkgCache::VerFile const * const A = *(pkgCache::VerFile const * const * const)a;
  120. pkgCache::VerFile const * const B = *(pkgCache::VerFile const * const * const)b;
  121. if (A == 0 && B == 0)
  122. return 0;
  123. if (A == 0)
  124. return 1;
  125. if (B == 0)
  126. return -1;
  127. if (A->File == B->File)
  128. return A->Offset - B->Offset;
  129. return A->File - B->File;
  130. }
  131. void LocalitySort(pkgCache::VerFile ** const begin, unsigned long long const Count,size_t const Size)
  132. {
  133. qsort(begin,Count,Size,LocalityCompare);
  134. }
  135. static void LocalitySort(pkgCache::DescFile ** const begin, unsigned long long const Count,size_t const Size)
  136. {
  137. qsort(begin,Count,Size,LocalityCompare);
  138. }
  139. /*}}}*/
  140. // Search - Perform a search /*{{{*/
  141. // ---------------------------------------------------------------------
  142. /* This searches the package names and package descriptions for a pattern */
  143. struct ExDescFile
  144. {
  145. pkgCache::DescFile *Df;
  146. pkgCache::VerIterator V;
  147. map_id_t ID;
  148. };
  149. static bool Search(CommandLine &CmdL)
  150. {
  151. bool const ShowFull = _config->FindB("APT::Cache::ShowFull",false);
  152. unsigned int const NumPatterns = CmdL.FileSize() -1;
  153. pkgCacheFile CacheFile;
  154. pkgCache *Cache = CacheFile.GetPkgCache();
  155. pkgDepCache::Policy *Plcy = CacheFile.GetPolicy();
  156. if (unlikely(Cache == NULL || Plcy == NULL))
  157. return false;
  158. // Make sure there is at least one argument
  159. if (NumPatterns < 1)
  160. return _error->Error(_("You must give at least one search pattern"));
  161. // Compile the regex pattern
  162. regex_t *Patterns = new regex_t[NumPatterns];
  163. memset(Patterns,0,sizeof(*Patterns)*NumPatterns);
  164. for (unsigned I = 0; I != NumPatterns; I++)
  165. {
  166. if (regcomp(&Patterns[I],CmdL.FileList[I+1],REG_EXTENDED | REG_ICASE |
  167. REG_NOSUB) != 0)
  168. {
  169. for (; I != 0; I--)
  170. regfree(&Patterns[I]);
  171. return _error->Error("Regex compilation error");
  172. }
  173. }
  174. if (_error->PendingError() == true)
  175. {
  176. for (unsigned I = 0; I != NumPatterns; I++)
  177. regfree(&Patterns[I]);
  178. return false;
  179. }
  180. size_t const descCount = Cache->HeaderP->GroupCount + 1;
  181. ExDescFile *DFList = new ExDescFile[descCount];
  182. memset(DFList,0,sizeof(*DFList) * descCount);
  183. bool *PatternMatch = new bool[descCount * NumPatterns];
  184. memset(PatternMatch,false,sizeof(*PatternMatch) * descCount * NumPatterns);
  185. // Map versions that we want to write out onto the VerList array.
  186. bool const NamesOnly = _config->FindB("APT::Cache::NamesOnly",false);
  187. for (pkgCache::GrpIterator G = Cache->GrpBegin(); G.end() == false; ++G)
  188. {
  189. size_t const PatternOffset = G->ID * NumPatterns;
  190. size_t unmatched = 0, matched = 0;
  191. for (unsigned I = 0; I < NumPatterns; ++I)
  192. {
  193. if (PatternMatch[PatternOffset + I] == true)
  194. ++matched;
  195. else if (regexec(&Patterns[I],G.Name(),0,0,0) == 0)
  196. PatternMatch[PatternOffset + I] = true;
  197. else
  198. ++unmatched;
  199. }
  200. // already dealt with this package?
  201. if (matched == NumPatterns)
  202. continue;
  203. // Doing names only, drop any that don't match..
  204. if (NamesOnly == true && unmatched == NumPatterns)
  205. continue;
  206. // Find the proper version to use
  207. pkgCache::PkgIterator P = G.FindPreferredPkg();
  208. if (P.end() == true)
  209. continue;
  210. pkgCache::VerIterator V = Plcy->GetCandidateVer(P);
  211. if (V.end() == false)
  212. {
  213. pkgCache::DescIterator const D = V.TranslatedDescription();
  214. //FIXME: packages without a description can't be found
  215. if (D.end() == true)
  216. continue;
  217. DFList[G->ID].Df = D.FileList();
  218. DFList[G->ID].V = V;
  219. DFList[G->ID].ID = G->ID;
  220. }
  221. if (unmatched == NumPatterns)
  222. continue;
  223. // Include all the packages that provide matching names too
  224. for (pkgCache::PrvIterator Prv = P.ProvidesList() ; Prv.end() == false; ++Prv)
  225. {
  226. pkgCache::VerIterator V = Plcy->GetCandidateVer(Prv.OwnerPkg());
  227. if (V.end() == true)
  228. continue;
  229. unsigned long id = Prv.OwnerPkg().Group()->ID;
  230. pkgCache::DescIterator const D = V.TranslatedDescription();
  231. //FIXME: packages without a description can't be found
  232. if (D.end() == true)
  233. continue;
  234. DFList[id].Df = D.FileList();
  235. DFList[id].V = V;
  236. DFList[id].ID = id;
  237. size_t const PrvPatternOffset = id * NumPatterns;
  238. for (unsigned I = 0; I < NumPatterns; ++I)
  239. PatternMatch[PrvPatternOffset + I] |= PatternMatch[PatternOffset + I];
  240. }
  241. }
  242. LocalitySort(&DFList->Df, Cache->HeaderP->GroupCount, sizeof(*DFList));
  243. // Create the text record parser
  244. pkgRecords Recs(*Cache);
  245. // Iterate over all the version records and check them
  246. for (ExDescFile *J = DFList; J->Df != 0; ++J)
  247. {
  248. pkgRecords::Parser &P = Recs.Lookup(pkgCache::DescFileIterator(*Cache,J->Df));
  249. size_t const PatternOffset = J->ID * NumPatterns;
  250. if (NamesOnly == false)
  251. {
  252. std::string const LongDesc = P.LongDesc();
  253. for (unsigned I = 0; I < NumPatterns; ++I)
  254. {
  255. if (PatternMatch[PatternOffset + I] == true)
  256. continue;
  257. else if (regexec(&Patterns[I],LongDesc.c_str(),0,0,0) == 0)
  258. PatternMatch[PatternOffset + I] = true;
  259. }
  260. }
  261. bool matchedAll = true;
  262. for (unsigned I = 0; I < NumPatterns; ++I)
  263. if (PatternMatch[PatternOffset + I] == false)
  264. {
  265. matchedAll = false;
  266. break;
  267. }
  268. if (matchedAll == true)
  269. {
  270. if (ShowFull == true)
  271. DisplayRecordV1(CacheFile, J->V, std::cout);
  272. else
  273. printf("%s - %s\n",P.Name().c_str(),P.ShortDesc().c_str());
  274. }
  275. }
  276. delete [] DFList;
  277. delete [] PatternMatch;
  278. for (unsigned I = 0; I != NumPatterns; I++)
  279. regfree(&Patterns[I]);
  280. delete [] Patterns;
  281. if (ferror(stdout))
  282. return _error->Error("Write to stdout failed");
  283. return true;
  284. }
  285. /*}}}*/
  286. bool DoSearch(CommandLine &CmdL) /*{{{*/
  287. {
  288. int const ShowVersion = _config->FindI("APT::Cache::Search::Version", 1);
  289. if (ShowVersion <= 1)
  290. return Search(CmdL);
  291. return FullTextSearch(CmdL);
  292. }