]>
Commit | Line | Data |
---|---|---|
1 | // Includes /*{{{*/ | |
2 | #include <config.h> | |
3 | ||
4 | #include <apt-pkg/cachefile.h> | |
5 | #include <apt-pkg/cacheset.h> | |
6 | #include <apt-pkg/cmndline.h> | |
7 | #include <apt-pkg/error.h> | |
8 | #include <apt-pkg/fileutl.h> | |
9 | #include <apt-pkg/indexfile.h> | |
10 | #include <apt-pkg/pkgrecords.h> | |
11 | #include <apt-pkg/pkgsystem.h> | |
12 | #include <apt-pkg/sourcelist.h> | |
13 | #include <apt-pkg/strutl.h> | |
14 | #include <apt-pkg/tagfile.h> | |
15 | #include <apt-pkg/cacheiterators.h> | |
16 | #include <apt-pkg/configuration.h> | |
17 | #include <apt-pkg/depcache.h> | |
18 | #include <apt-pkg/macros.h> | |
19 | #include <apt-pkg/pkgcache.h> | |
20 | #include <apt-pkg/policy.h> | |
21 | ||
22 | #include <apt-private/private-cacheset.h> | |
23 | #include <apt-private/private-output.h> | |
24 | #include <apt-private/private-show.h> | |
25 | ||
26 | #include <stdio.h> | |
27 | #include <ostream> | |
28 | #include <string> | |
29 | ||
30 | #include <apti18n.h> | |
31 | /*}}}*/ | |
32 | ||
33 | static bool OpenPackagesFile(pkgCacheFile &CacheFile, pkgCache::VerIterator const &V,/*{{{*/ | |
34 | FileFd &PkgF, pkgCache::VerFileIterator &Vf) | |
35 | { | |
36 | pkgCache const * const Cache = CacheFile.GetPkgCache(); | |
37 | if (unlikely(Cache == NULL)) | |
38 | return false; | |
39 | ||
40 | // Find an appropriate file | |
41 | Vf = V.FileList(); | |
42 | for (; Vf.end() == false; ++Vf) | |
43 | if ((Vf.File()->Flags & pkgCache::Flag::NotSource) == 0) | |
44 | break; | |
45 | if (Vf.end() == true) | |
46 | Vf = V.FileList(); | |
47 | ||
48 | // Check and load the package list file | |
49 | pkgCache::PkgFileIterator I = Vf.File(); | |
50 | if (I.IsOk() == false) | |
51 | return _error->Error(_("Package file %s is out of sync."),I.FileName()); | |
52 | ||
53 | // Read the record | |
54 | return PkgF.Open(I.FileName(), FileFd::ReadOnly, FileFd::Extension); | |
55 | } | |
56 | /*}}}*/ | |
57 | static APT_PURE unsigned char const* skipDescriptionFields(unsigned char const * DescP)/*{{{*/ | |
58 | { | |
59 | char const * const TagName = "\nDescription"; | |
60 | size_t const TagLen = strlen(TagName); | |
61 | while ((DescP = (unsigned char*)strchr((char*)DescP, '\n')) != NULL) | |
62 | { | |
63 | if (DescP[1] == ' ') | |
64 | DescP += 2; | |
65 | else if (strncmp((char*)DescP, TagName, TagLen) == 0) | |
66 | DescP += TagLen; | |
67 | else | |
68 | break; | |
69 | } | |
70 | if (DescP != NULL) | |
71 | ++DescP; | |
72 | return DescP; | |
73 | } | |
74 | /*}}}*/ | |
75 | bool DisplayRecordV1(pkgCacheFile &CacheFile, pkgCache::VerIterator const &V,/*{{{*/ | |
76 | std::ostream &out) | |
77 | { | |
78 | FileFd PkgF; | |
79 | pkgCache::VerFileIterator Vf; | |
80 | if (OpenPackagesFile(CacheFile, V, PkgF, Vf) == false) | |
81 | return false; | |
82 | ||
83 | pkgCache * const Cache = CacheFile.GetPkgCache(); | |
84 | if (unlikely(Cache == NULL)) | |
85 | return false; | |
86 | ||
87 | // Read the record (and ensure that it ends with a newline and NUL) | |
88 | unsigned char *Buffer = new unsigned char[Cache->HeaderP->MaxVerFileSize+2]; | |
89 | Buffer[Vf->Size] = '\n'; | |
90 | Buffer[Vf->Size+1] = '\0'; | |
91 | if (PkgF.Seek(Vf->Offset) == false || | |
92 | PkgF.Read(Buffer,Vf->Size) == false) | |
93 | { | |
94 | delete [] Buffer; | |
95 | return false; | |
96 | } | |
97 | ||
98 | // Get a pointer to start of Description field | |
99 | const unsigned char *DescP = (unsigned char*)strstr((char*)Buffer, "\nDescription"); | |
100 | if (DescP != NULL) | |
101 | ++DescP; | |
102 | else | |
103 | DescP = Buffer + Vf->Size; | |
104 | ||
105 | // Write all but Description | |
106 | size_t const length = DescP - Buffer; | |
107 | if (length != 0 && FileFd::Write(STDOUT_FILENO, Buffer, length) == false) | |
108 | { | |
109 | delete [] Buffer; | |
110 | return false; | |
111 | } | |
112 | ||
113 | // Show the right description | |
114 | pkgRecords Recs(*Cache); | |
115 | pkgCache::DescIterator Desc = V.TranslatedDescription(); | |
116 | if (Desc.end() == false) | |
117 | { | |
118 | pkgRecords::Parser &P = Recs.Lookup(Desc.FileList()); | |
119 | out << "Description" << ( (strcmp(Desc.LanguageCode(),"") != 0) ? "-" : "" ) << Desc.LanguageCode() << ": " << P.LongDesc(); | |
120 | out << std::endl << "Description-md5: " << Desc.md5() << std::endl; | |
121 | ||
122 | // Find the first field after the description (if there is any) | |
123 | DescP = skipDescriptionFields(DescP); | |
124 | } | |
125 | // else we have no translation, so we found a lonely Description-md5 -> don't skip it | |
126 | ||
127 | // write the rest of the buffer, but skip mixed in Descriptions* fields | |
128 | while (DescP != NULL) | |
129 | { | |
130 | const unsigned char * const Start = DescP; | |
131 | const unsigned char *End = (unsigned char*)strstr((char*)DescP, "\nDescription"); | |
132 | if (End == NULL) | |
133 | { | |
134 | End = &Buffer[Vf->Size]; | |
135 | DescP = NULL; | |
136 | } | |
137 | else | |
138 | { | |
139 | ++End; // get the newline into the output | |
140 | DescP = skipDescriptionFields(End + strlen("Description")); | |
141 | } | |
142 | size_t const length = End - Start; | |
143 | if (length != 0 && FileFd::Write(STDOUT_FILENO, Start, length) == false) | |
144 | { | |
145 | delete [] Buffer; | |
146 | return false; | |
147 | } | |
148 | } | |
149 | // write a final newline after the last field | |
150 | out << std::endl; | |
151 | ||
152 | delete [] Buffer; | |
153 | return true; | |
154 | } | |
155 | /*}}}*/ | |
156 | static bool DisplayRecordV2(pkgCacheFile &CacheFile, pkgCache::VerIterator const &V,/*{{{*/ | |
157 | std::ostream &out) | |
158 | { | |
159 | FileFd PkgF; | |
160 | pkgCache::VerFileIterator Vf; | |
161 | if (OpenPackagesFile(CacheFile, V, PkgF, Vf) == false) | |
162 | return false; | |
163 | ||
164 | // Check and load the package list file | |
165 | pkgCache::PkgFileIterator I = Vf.File(); | |
166 | if (I.IsOk() == false) | |
167 | return _error->Error(_("Package file %s is out of sync."),I.FileName()); | |
168 | ||
169 | // find matching sources.list metaindex | |
170 | pkgSourceList *SrcList = CacheFile.GetSourceList(); | |
171 | pkgIndexFile *Index; | |
172 | if (SrcList->FindIndex(I, Index) == false && | |
173 | _system->FindIndex(I, Index) == false) | |
174 | return _error->Error("Can not find indexfile for Package %s (%s)", | |
175 | V.ParentPkg().Name(), V.VerStr()); | |
176 | std::string source_index_file = Index->Describe(true); | |
177 | ||
178 | // Read the record | |
179 | pkgTagSection Tags; | |
180 | pkgTagFile TagF(&PkgF); | |
181 | ||
182 | if (TagF.Jump(Tags, V.FileList()->Offset) == false) | |
183 | return _error->Error("Internal Error, Unable to parse a package record"); | |
184 | ||
185 | // make size nice | |
186 | std::string installed_size; | |
187 | if (Tags.FindI("Installed-Size") > 0) | |
188 | strprintf(installed_size, "%sB", SizeToStr(Tags.FindI("Installed-Size")*1024).c_str()); | |
189 | else | |
190 | installed_size = _("unknown"); | |
191 | std::string package_size; | |
192 | if (Tags.FindI("Size") > 0) | |
193 | strprintf(package_size, "%sB", SizeToStr(Tags.FindI("Size")).c_str()); | |
194 | else | |
195 | package_size = _("unknown"); | |
196 | ||
197 | const char *manual_installed = nullptr; | |
198 | if (V.ParentPkg().CurrentVer() == V) | |
199 | { | |
200 | pkgDepCache *depCache = CacheFile.GetDepCache(); | |
201 | if (unlikely(depCache == nullptr)) | |
202 | return false; | |
203 | pkgDepCache::StateCache &state = (*depCache)[V.ParentPkg()]; | |
204 | manual_installed = !(state.Flags & pkgCache::Flag::Auto) ? "yes" : "no"; | |
205 | } | |
206 | ||
207 | // FIXME: add verbose that does not do the removal of the tags? | |
208 | std::vector<pkgTagSection::Tag> RW; | |
209 | // delete, apt-cache show has this info and most users do not care | |
210 | RW.push_back(pkgTagSection::Tag::Remove("MD5sum")); | |
211 | RW.push_back(pkgTagSection::Tag::Remove("SHA1")); | |
212 | RW.push_back(pkgTagSection::Tag::Remove("SHA256")); | |
213 | RW.push_back(pkgTagSection::Tag::Remove("SHA512")); | |
214 | RW.push_back(pkgTagSection::Tag::Remove("Filename")); | |
215 | RW.push_back(pkgTagSection::Tag::Remove("Multi-Arch")); | |
216 | RW.push_back(pkgTagSection::Tag::Remove("Architecture")); | |
217 | RW.push_back(pkgTagSection::Tag::Remove("Conffiles")); | |
218 | // we use the translated description | |
219 | RW.push_back(pkgTagSection::Tag::Remove("Description")); | |
220 | RW.push_back(pkgTagSection::Tag::Remove("Description-md5")); | |
221 | // improve | |
222 | RW.push_back(pkgTagSection::Tag::Rewrite("Package", V.ParentPkg().FullName(true))); | |
223 | RW.push_back(pkgTagSection::Tag::Rewrite("Installed-Size", installed_size)); | |
224 | RW.push_back(pkgTagSection::Tag::Remove("Size")); | |
225 | RW.push_back(pkgTagSection::Tag::Rewrite("Download-Size", package_size)); | |
226 | // add | |
227 | if (manual_installed != nullptr) | |
228 | RW.push_back(pkgTagSection::Tag::Rewrite("APT-Manual-Installed", manual_installed)); | |
229 | RW.push_back(pkgTagSection::Tag::Rewrite("APT-Sources", source_index_file)); | |
230 | ||
231 | FileFd stdoutfd; | |
232 | if (stdoutfd.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly, false) == false || | |
233 | Tags.Write(stdoutfd, TFRewritePackageOrder, RW) == false || stdoutfd.Close() == false) | |
234 | return _error->Error("Internal Error, Unable to parse a package record"); | |
235 | ||
236 | // write the description | |
237 | pkgCache * const Cache = CacheFile.GetPkgCache(); | |
238 | if (unlikely(Cache == NULL)) | |
239 | return false; | |
240 | pkgRecords Recs(*Cache); | |
241 | // FIXME: show (optionally) all available translations(?) | |
242 | pkgCache::DescIterator Desc = V.TranslatedDescription(); | |
243 | if (Desc.end() == false) | |
244 | { | |
245 | pkgRecords::Parser &P = Recs.Lookup(Desc.FileList()); | |
246 | out << "Description: " << P.LongDesc(); | |
247 | } | |
248 | ||
249 | // write a final newline (after the description) | |
250 | out << std::endl << std::endl; | |
251 | ||
252 | return true; | |
253 | } | |
254 | /*}}}*/ | |
255 | bool ShowPackage(CommandLine &CmdL) /*{{{*/ | |
256 | { | |
257 | pkgCacheFile CacheFile; | |
258 | CacheSetHelperVirtuals helper(true, GlobalError::NOTICE); | |
259 | APT::CacheSetHelper::VerSelector const select = _config->FindB("APT::Cache::AllVersions", true) ? | |
260 | APT::CacheSetHelper::ALL : APT::CacheSetHelper::CANDIDATE; | |
261 | APT::VersionList const verset = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1, select, helper); | |
262 | int const ShowVersion = _config->FindI("APT::Cache::Show::Version", 1); | |
263 | for (APT::VersionList::const_iterator Ver = verset.begin(); Ver != verset.end(); ++Ver) | |
264 | if (ShowVersion <= 1) | |
265 | { | |
266 | if (DisplayRecordV1(CacheFile, Ver, std::cout) == false) | |
267 | return false; | |
268 | } | |
269 | else | |
270 | if (DisplayRecordV2(CacheFile, Ver, c1out) == false) | |
271 | return false; | |
272 | ||
273 | if (select == APT::CacheSetHelper::CANDIDATE) | |
274 | { | |
275 | APT::VersionList const verset_all = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1, APT::CacheSetHelper::ALL, helper); | |
276 | int const records = verset_all.size() - verset.size(); | |
277 | if (records > 0) | |
278 | _error->Notice(P_("There is %i additional record. Please use the '-a' switch to see it", "There are %i additional records. Please use the '-a' switch to see them.", records), records); | |
279 | } | |
280 | ||
281 | if (_config->FindB("APT::Cache::ShowVirtuals", false) == true) | |
282 | for (APT::PackageSet::const_iterator Pkg = helper.virtualPkgs.begin(); | |
283 | Pkg != helper.virtualPkgs.end(); ++Pkg) | |
284 | { | |
285 | c1out << "Package: " << Pkg.FullName(true) << std::endl; | |
286 | c1out << "State: " << _("not a real package (virtual)") << std::endl; | |
287 | // FIXME: show providers, see private-cacheset.h | |
288 | // CacheSetHelperAPTGet::showVirtualPackageErrors() | |
289 | } | |
290 | ||
291 | if (verset.empty() == true) | |
292 | { | |
293 | if (helper.virtualPkgs.empty() == true) | |
294 | return _error->Error(_("No packages found")); | |
295 | else | |
296 | _error->Notice(_("No packages found")); | |
297 | } | |
298 | ||
299 | return true; | |
300 | } | |
301 | /*}}}*/ | |
302 | static std::string Sha1FromString(std::string const &input) /*{{{*/ | |
303 | { | |
304 | // XXX: move to hashes.h: HashString::FromString() ? | |
305 | SHA1Summation sha1; | |
306 | sha1.Add(input.c_str(), input.length()); | |
307 | return sha1.Result().Value(); | |
308 | } | |
309 | /*}}}*/ | |
310 | bool ShowSrcPackage(CommandLine &CmdL) /*{{{*/ | |
311 | { | |
312 | pkgCacheFile CacheFile; | |
313 | pkgSourceList *List = CacheFile.GetSourceList(); | |
314 | if (unlikely(List == NULL)) | |
315 | return false; | |
316 | ||
317 | // Create the text record parsers | |
318 | pkgSrcRecords SrcRecs(*List); | |
319 | if (_error->PendingError() == true) | |
320 | return false; | |
321 | ||
322 | bool found = false; | |
323 | // avoid showing identical records | |
324 | std::set<std::string> seen; | |
325 | for (const char **I = CmdL.FileList + 1; *I != 0; I++) | |
326 | { | |
327 | SrcRecs.Restart(); | |
328 | ||
329 | pkgSrcRecords::Parser *Parse; | |
330 | bool found_this = false; | |
331 | while ((Parse = SrcRecs.Find(*I,false)) != 0) { | |
332 | // SrcRecs.Find() will find both binary and source names | |
333 | if (_config->FindB("APT::Cache::Only-Source", false) == true) | |
334 | if (Parse->Package() != *I) | |
335 | continue; | |
336 | std::string sha1str = Sha1FromString(Parse->AsStr()); | |
337 | if (std::find(seen.begin(), seen.end(), sha1str) == seen.end()) | |
338 | { | |
339 | std::cout << Parse->AsStr() << std::endl;; | |
340 | found = true; | |
341 | found_this = true; | |
342 | seen.insert(sha1str); | |
343 | } | |
344 | } | |
345 | if (found_this == false) { | |
346 | _error->Warning(_("Unable to locate package %s"),*I); | |
347 | continue; | |
348 | } | |
349 | } | |
350 | if (found == false) | |
351 | _error->Notice(_("No packages found")); | |
352 | return true; | |
353 | } | |
354 | /*}}}*/ | |
355 | // Policy - Show the results of the preferences file /*{{{*/ | |
356 | bool Policy(CommandLine &CmdL) | |
357 | { | |
358 | pkgCacheFile CacheFile; | |
359 | pkgCache *Cache = CacheFile.GetPkgCache(); | |
360 | pkgPolicy *Plcy = CacheFile.GetPolicy(); | |
361 | pkgSourceList *SrcList = CacheFile.GetSourceList(); | |
362 | if (unlikely(Cache == NULL || Plcy == NULL || SrcList == NULL)) | |
363 | return false; | |
364 | ||
365 | bool OldPolicy = _config->FindI("APT::Policy", 1) < 1; | |
366 | ||
367 | // Print out all of the package files | |
368 | if (CmdL.FileList[1] == 0) | |
369 | { | |
370 | std::cout << _("Package files:") << std::endl; | |
371 | for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F.end() == false; ++F) | |
372 | { | |
373 | if (F.Flagged(pkgCache::Flag::NoPackages)) | |
374 | continue; | |
375 | // Locate the associated index files so we can derive a description | |
376 | pkgIndexFile *Indx; | |
377 | if (SrcList->FindIndex(F,Indx) == false && | |
378 | _system->FindIndex(F,Indx) == false) | |
379 | return _error->Error(_("Cache is out of sync, can't x-ref a package file")); | |
380 | ||
381 | printf("%4i %s\n", | |
382 | Plcy->GetPriority(F),Indx->Describe(true).c_str()); | |
383 | ||
384 | // Print the reference information for the package | |
385 | std::string Str = F.RelStr(); | |
386 | if (Str.empty() == false) | |
387 | printf(" release %s\n",F.RelStr().c_str()); | |
388 | if (F.Site() != 0 && F.Site()[0] != 0) | |
389 | printf(" origin %s\n",F.Site()); | |
390 | } | |
391 | ||
392 | // Show any packages have explicit pins | |
393 | std::cout << _("Pinned packages:") << std::endl; | |
394 | pkgCache::PkgIterator I = Cache->PkgBegin(); | |
395 | for (;I.end() != true; ++I) | |
396 | { | |
397 | // Old code for debugging | |
398 | if (OldPolicy) | |
399 | { | |
400 | if (Plcy->GetPriority(I) == 0) | |
401 | continue; | |
402 | ||
403 | // Print the package name and the version we are forcing to | |
404 | std::cout << " " << I.FullName(true) << " -> "; | |
405 | ||
406 | pkgCache::VerIterator V = Plcy->GetMatch(I); | |
407 | if (V.end() == true) | |
408 | std::cout << _("(not found)") << std::endl; | |
409 | else | |
410 | std::cout << V.VerStr() << std::endl; | |
411 | ||
412 | continue; | |
413 | } | |
414 | // New code | |
415 | for (pkgCache::VerIterator V = I.VersionList(); !V.end(); ++V) { | |
416 | auto Prio = Plcy->GetPriority(V, false); | |
417 | if (Prio == 0) | |
418 | continue; | |
419 | ||
420 | std::cout << " "; | |
421 | // Print the package name and the version we are forcing to | |
422 | ioprintf(std::cout, _("%s -> %s with priority %d\n"), I.FullName(true).c_str(), V.VerStr(), Prio); | |
423 | } | |
424 | } | |
425 | return true; | |
426 | } | |
427 | ||
428 | char const * const msgInstalled = _(" Installed: "); | |
429 | char const * const msgCandidate = _(" Candidate: "); | |
430 | short const InstalledLessCandidate = | |
431 | mbstowcs(NULL, msgInstalled, 0) - mbstowcs(NULL, msgCandidate, 0); | |
432 | short const deepInstalled = | |
433 | (InstalledLessCandidate < 0 ? (InstalledLessCandidate*-1) : 0) - 1; | |
434 | short const deepCandidate = | |
435 | (InstalledLessCandidate > 0 ? (InstalledLessCandidate) : 0) - 1; | |
436 | ||
437 | // Print out detailed information for each package | |
438 | APT::CacheSetHelper helper(true, GlobalError::NOTICE); | |
439 | APT::PackageList pkgset = APT::PackageList::FromCommandLine(CacheFile, CmdL.FileList + 1, helper); | |
440 | for (APT::PackageList::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg) | |
441 | { | |
442 | std::cout << Pkg.FullName(true) << ":" << std::endl; | |
443 | ||
444 | // Installed version | |
445 | std::cout << msgInstalled << OutputInDepth(deepInstalled, " "); | |
446 | if (Pkg->CurrentVer == 0) | |
447 | std::cout << _("(none)") << std::endl; | |
448 | else | |
449 | std::cout << Pkg.CurrentVer().VerStr() << std::endl; | |
450 | ||
451 | // Candidate Version | |
452 | std::cout << msgCandidate << OutputInDepth(deepCandidate, " "); | |
453 | pkgCache::VerIterator V = Plcy->GetCandidateVer(Pkg); | |
454 | if (V.end() == true) | |
455 | std::cout << _("(none)") << std::endl; | |
456 | else | |
457 | std::cout << V.VerStr() << std::endl; | |
458 | ||
459 | // Pinned version | |
460 | if (OldPolicy && Plcy->GetPriority(Pkg) != 0) | |
461 | { | |
462 | std::cout << _(" Package pin: "); | |
463 | V = Plcy->GetMatch(Pkg); | |
464 | if (V.end() == true) | |
465 | std::cout << _("(not found)") << std::endl; | |
466 | else | |
467 | std::cout << V.VerStr() << std::endl; | |
468 | } | |
469 | ||
470 | // Show the priority tables | |
471 | std::cout << _(" Version table:") << std::endl; | |
472 | for (V = Pkg.VersionList(); V.end() == false; ++V) | |
473 | { | |
474 | if (Pkg.CurrentVer() == V) | |
475 | std::cout << " *** " << V.VerStr(); | |
476 | else | |
477 | std::cout << " " << V.VerStr(); | |
478 | if (_config->FindI("APT::Policy", 1) < 1) | |
479 | std::cout << " " << Plcy->GetPriority(Pkg) << std::endl; | |
480 | else | |
481 | std::cout << " " << Plcy->GetPriority(V) << std::endl; | |
482 | for (pkgCache::VerFileIterator VF = V.FileList(); VF.end() == false; ++VF) | |
483 | { | |
484 | // Locate the associated index files so we can derive a description | |
485 | pkgIndexFile *Indx; | |
486 | if (SrcList->FindIndex(VF.File(),Indx) == false && | |
487 | _system->FindIndex(VF.File(),Indx) == false) | |
488 | return _error->Error(_("Cache is out of sync, can't x-ref a package file")); | |
489 | printf(" %4i %s\n",Plcy->GetPriority(VF.File()), | |
490 | Indx->Describe(true).c_str()); | |
491 | } | |
492 | } | |
493 | } | |
494 | return true; | |
495 | } | |
496 | /*}}}*/ |