private-search.cc 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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. pkgRecords::Parser &parser = records.Lookup(Desc.FileList());
  80. std::string const LongDesc = parser.LongDesc();
  81. bool all_found = true;
  82. for (std::vector<regex_t>::const_iterator pattern = Patterns.begin();
  83. pattern != Patterns.end(); ++pattern)
  84. {
  85. if (regexec(&(*pattern), PkgName, 0, 0, 0) == 0)
  86. continue;
  87. else if (NamesOnly == false && regexec(&(*pattern), LongDesc.c_str(), 0, 0, 0) == 0)
  88. continue;
  89. // search patterns are AND, so one failing fails all
  90. all_found = false;
  91. break;
  92. }
  93. if (all_found == true)
  94. {
  95. PkgsDone[P->ID] = true;
  96. std::stringstream outs;
  97. ListSingleVersion(CacheFile, records, V, outs, format);
  98. output_map.insert(std::make_pair<std::string, std::string>(
  99. PkgName, outs.str()));
  100. }
  101. }
  102. APT_FREE_PATTERNS();
  103. progress.Done();
  104. // FIXME: SORT! and make sorting flexible (alphabetic, by pkg status)
  105. // output the sorted map
  106. std::map<std::string, std::string>::const_iterator K;
  107. for (K = output_map.begin(); K != output_map.end(); ++K)
  108. std::cout << (*K).second << std::endl;
  109. return true;
  110. }
  111. /*}}}*/
  112. // LocalitySort - Sort a version list by package file locality /*{{{*/
  113. static int LocalityCompare(const void * const a, const void * const b)
  114. {
  115. pkgCache::VerFile const * const A = *(pkgCache::VerFile const * const * const)a;
  116. pkgCache::VerFile const * const B = *(pkgCache::VerFile const * const * const)b;
  117. if (A == 0 && B == 0)
  118. return 0;
  119. if (A == 0)
  120. return 1;
  121. if (B == 0)
  122. return -1;
  123. if (A->File == B->File)
  124. return A->Offset - B->Offset;
  125. return A->File - B->File;
  126. }
  127. void LocalitySort(pkgCache::VerFile ** const begin, unsigned long long const Count,size_t const Size)
  128. {
  129. qsort(begin,Count,Size,LocalityCompare);
  130. }
  131. static void LocalitySort(pkgCache::DescFile ** const begin, unsigned long long const Count,size_t const Size)
  132. {
  133. qsort(begin,Count,Size,LocalityCompare);
  134. }
  135. /*}}}*/
  136. // Search - Perform a search /*{{{*/
  137. // ---------------------------------------------------------------------
  138. /* This searches the package names and package descriptions for a pattern */
  139. struct ExDescFile
  140. {
  141. pkgCache::DescFile *Df;
  142. pkgCache::VerIterator V;
  143. map_id_t ID;
  144. };
  145. static bool Search(CommandLine &CmdL)
  146. {
  147. bool const ShowFull = _config->FindB("APT::Cache::ShowFull",false);
  148. unsigned int const NumPatterns = CmdL.FileSize() -1;
  149. pkgCacheFile CacheFile;
  150. pkgCache *Cache = CacheFile.GetPkgCache();
  151. pkgDepCache::Policy *Plcy = CacheFile.GetPolicy();
  152. if (unlikely(Cache == NULL || Plcy == NULL))
  153. return false;
  154. // Make sure there is at least one argument
  155. if (NumPatterns < 1)
  156. return _error->Error(_("You must give at least one search pattern"));
  157. // Compile the regex pattern
  158. regex_t *Patterns = new regex_t[NumPatterns];
  159. memset(Patterns,0,sizeof(*Patterns)*NumPatterns);
  160. for (unsigned I = 0; I != NumPatterns; I++)
  161. {
  162. if (regcomp(&Patterns[I],CmdL.FileList[I+1],REG_EXTENDED | REG_ICASE |
  163. REG_NOSUB) != 0)
  164. {
  165. for (; I != 0; I--)
  166. regfree(&Patterns[I]);
  167. return _error->Error("Regex compilation error");
  168. }
  169. }
  170. if (_error->PendingError() == true)
  171. {
  172. for (unsigned I = 0; I != NumPatterns; I++)
  173. regfree(&Patterns[I]);
  174. return false;
  175. }
  176. size_t const descCount = Cache->HeaderP->GroupCount + 1;
  177. ExDescFile *DFList = new ExDescFile[descCount];
  178. memset(DFList,0,sizeof(*DFList) * descCount);
  179. bool *PatternMatch = new bool[descCount * NumPatterns];
  180. memset(PatternMatch,false,sizeof(*PatternMatch) * descCount * NumPatterns);
  181. // Map versions that we want to write out onto the VerList array.
  182. bool const NamesOnly = _config->FindB("APT::Cache::NamesOnly",false);
  183. for (pkgCache::GrpIterator G = Cache->GrpBegin(); G.end() == false; ++G)
  184. {
  185. size_t const PatternOffset = G->ID * NumPatterns;
  186. size_t unmatched = 0, matched = 0;
  187. for (unsigned I = 0; I < NumPatterns; ++I)
  188. {
  189. if (PatternMatch[PatternOffset + I] == true)
  190. ++matched;
  191. else if (regexec(&Patterns[I],G.Name(),0,0,0) == 0)
  192. PatternMatch[PatternOffset + I] = true;
  193. else
  194. ++unmatched;
  195. }
  196. // already dealt with this package?
  197. if (matched == NumPatterns)
  198. continue;
  199. // Doing names only, drop any that don't match..
  200. if (NamesOnly == true && unmatched == NumPatterns)
  201. continue;
  202. // Find the proper version to use
  203. pkgCache::PkgIterator P = G.FindPreferredPkg();
  204. if (P.end() == true)
  205. continue;
  206. pkgCache::VerIterator V = Plcy->GetCandidateVer(P);
  207. if (V.end() == false)
  208. {
  209. pkgCache::DescIterator const D = V.TranslatedDescription();
  210. //FIXME: packages without a description can't be found
  211. if (D.end() == true)
  212. continue;
  213. DFList[G->ID].Df = D.FileList();
  214. DFList[G->ID].V = V;
  215. DFList[G->ID].ID = G->ID;
  216. }
  217. if (unmatched == NumPatterns)
  218. continue;
  219. // Include all the packages that provide matching names too
  220. for (pkgCache::PrvIterator Prv = P.ProvidesList() ; Prv.end() == false; ++Prv)
  221. {
  222. pkgCache::VerIterator V = Plcy->GetCandidateVer(Prv.OwnerPkg());
  223. if (V.end() == true)
  224. continue;
  225. unsigned long id = Prv.OwnerPkg().Group()->ID;
  226. pkgCache::DescIterator const D = V.TranslatedDescription();
  227. //FIXME: packages without a description can't be found
  228. if (D.end() == true)
  229. continue;
  230. DFList[id].Df = D.FileList();
  231. DFList[id].V = V;
  232. DFList[id].ID = id;
  233. size_t const PrvPatternOffset = id * NumPatterns;
  234. for (unsigned I = 0; I < NumPatterns; ++I)
  235. PatternMatch[PrvPatternOffset + I] |= PatternMatch[PatternOffset + I];
  236. }
  237. }
  238. LocalitySort(&DFList->Df, Cache->HeaderP->GroupCount, sizeof(*DFList));
  239. // Create the text record parser
  240. pkgRecords Recs(*Cache);
  241. // Iterate over all the version records and check them
  242. for (ExDescFile *J = DFList; J->Df != 0; ++J)
  243. {
  244. pkgRecords::Parser &P = Recs.Lookup(pkgCache::DescFileIterator(*Cache,J->Df));
  245. size_t const PatternOffset = J->ID * NumPatterns;
  246. if (NamesOnly == false)
  247. {
  248. std::string const LongDesc = P.LongDesc();
  249. for (unsigned I = 0; I < NumPatterns; ++I)
  250. {
  251. if (PatternMatch[PatternOffset + I] == true)
  252. continue;
  253. else if (regexec(&Patterns[I],LongDesc.c_str(),0,0,0) == 0)
  254. PatternMatch[PatternOffset + I] = true;
  255. }
  256. }
  257. bool matchedAll = true;
  258. for (unsigned I = 0; I < NumPatterns; ++I)
  259. if (PatternMatch[PatternOffset + I] == false)
  260. {
  261. matchedAll = false;
  262. break;
  263. }
  264. if (matchedAll == true)
  265. {
  266. if (ShowFull == true)
  267. DisplayRecordV1(CacheFile, J->V, std::cout);
  268. else
  269. printf("%s - %s\n",P.Name().c_str(),P.ShortDesc().c_str());
  270. }
  271. }
  272. delete [] DFList;
  273. delete [] PatternMatch;
  274. for (unsigned I = 0; I != NumPatterns; I++)
  275. regfree(&Patterns[I]);
  276. delete [] Patterns;
  277. if (ferror(stdout))
  278. return _error->Error("Write to stdout failed");
  279. return true;
  280. }
  281. /*}}}*/
  282. bool DoSearch(CommandLine &CmdL) /*{{{*/
  283. {
  284. int const ShowVersion = _config->FindI("APT::Cache::Search::Version", 1);
  285. if (ShowVersion <= 1)
  286. return Search(CmdL);
  287. return FullTextSearch(CmdL);
  288. }