private-search.cc 10.0 KB

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