]>
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 | bool ShowSrcPackage(CommandLine &CmdL) /*{{{*/ | |
303 | { | |
304 | pkgCacheFile CacheFile; | |
305 | pkgSourceList *List = CacheFile.GetSourceList(); | |
306 | if (unlikely(List == NULL)) | |
307 | return false; | |
308 | ||
309 | // Create the text record parsers | |
310 | pkgSrcRecords SrcRecs(*List); | |
311 | if (_error->PendingError() == true) | |
312 | return false; | |
313 | ||
314 | bool found = false; | |
315 | for (const char **I = CmdL.FileList + 1; *I != 0; I++) | |
316 | { | |
317 | SrcRecs.Restart(); | |
318 | ||
319 | pkgSrcRecords::Parser *Parse; | |
320 | bool found_this = false; | |
321 | while ((Parse = SrcRecs.Find(*I,false)) != 0) { | |
322 | // SrcRecs.Find() will find both binary and source names | |
323 | if (_config->FindB("APT::Cache::Only-Source", false) == true) | |
324 | if (Parse->Package() != *I) | |
325 | continue; | |
326 | std::cout << Parse->AsStr() << std::endl;; | |
327 | found = true; | |
328 | found_this = true; | |
329 | } | |
330 | if (found_this == false) { | |
331 | _error->Warning(_("Unable to locate package %s"),*I); | |
332 | continue; | |
333 | } | |
334 | } | |
335 | if (found == false) | |
336 | _error->Notice(_("No packages found")); | |
337 | return true; | |
338 | } | |
339 | /*}}}*/ | |
340 | // Policy - Show the results of the preferences file /*{{{*/ | |
341 | bool Policy(CommandLine &CmdL) | |
342 | { | |
343 | pkgCacheFile CacheFile; | |
344 | pkgCache *Cache = CacheFile.GetPkgCache(); | |
345 | pkgPolicy *Plcy = CacheFile.GetPolicy(); | |
346 | pkgSourceList *SrcList = CacheFile.GetSourceList(); | |
347 | if (unlikely(Cache == NULL || Plcy == NULL || SrcList == NULL)) | |
348 | return false; | |
349 | ||
350 | bool OldPolicy = _config->FindI("APT::Policy", 1) < 1; | |
351 | ||
352 | // Print out all of the package files | |
353 | if (CmdL.FileList[1] == 0) | |
354 | { | |
355 | std::cout << _("Package files:") << std::endl; | |
356 | for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F.end() == false; ++F) | |
357 | { | |
358 | if (F.Flagged(pkgCache::Flag::NoPackages)) | |
359 | continue; | |
360 | // Locate the associated index files so we can derive a description | |
361 | pkgIndexFile *Indx; | |
362 | if (SrcList->FindIndex(F,Indx) == false && | |
363 | _system->FindIndex(F,Indx) == false) | |
364 | return _error->Error(_("Cache is out of sync, can't x-ref a package file")); | |
365 | ||
366 | printf("%4i %s\n", | |
367 | Plcy->GetPriority(F),Indx->Describe(true).c_str()); | |
368 | ||
369 | // Print the reference information for the package | |
370 | std::string Str = F.RelStr(); | |
371 | if (Str.empty() == false) | |
372 | printf(" release %s\n",F.RelStr().c_str()); | |
373 | if (F.Site() != 0 && F.Site()[0] != 0) | |
374 | printf(" origin %s\n",F.Site()); | |
375 | } | |
376 | ||
377 | // Show any packages have explicit pins | |
378 | std::cout << _("Pinned packages:") << std::endl; | |
379 | pkgCache::PkgIterator I = Cache->PkgBegin(); | |
380 | for (;I.end() != true; ++I) | |
381 | { | |
382 | // Old code for debugging | |
383 | if (OldPolicy) | |
384 | { | |
385 | if (Plcy->GetPriority(I) == 0) | |
386 | continue; | |
387 | ||
388 | // Print the package name and the version we are forcing to | |
389 | std::cout << " " << I.FullName(true) << " -> "; | |
390 | ||
391 | pkgCache::VerIterator V = Plcy->GetMatch(I); | |
392 | if (V.end() == true) | |
393 | std::cout << _("(not found)") << std::endl; | |
394 | else | |
395 | std::cout << V.VerStr() << std::endl; | |
396 | ||
397 | continue; | |
398 | } | |
399 | // New code | |
400 | for (pkgCache::VerIterator V = I.VersionList(); !V.end(); ++V) { | |
401 | auto Prio = Plcy->GetPriority(V, false); | |
402 | if (Prio == 0) | |
403 | continue; | |
404 | ||
405 | std::cout << " "; | |
406 | // Print the package name and the version we are forcing to | |
407 | ioprintf(std::cout, _("%s -> %s with priority %d\n"), I.FullName(true).c_str(), V.VerStr(), Prio); | |
408 | } | |
409 | } | |
410 | return true; | |
411 | } | |
412 | ||
413 | char const * const msgInstalled = _(" Installed: "); | |
414 | char const * const msgCandidate = _(" Candidate: "); | |
415 | short const InstalledLessCandidate = | |
416 | mbstowcs(NULL, msgInstalled, 0) - mbstowcs(NULL, msgCandidate, 0); | |
417 | short const deepInstalled = | |
418 | (InstalledLessCandidate < 0 ? (InstalledLessCandidate*-1) : 0) - 1; | |
419 | short const deepCandidate = | |
420 | (InstalledLessCandidate > 0 ? (InstalledLessCandidate) : 0) - 1; | |
421 | ||
422 | // Print out detailed information for each package | |
423 | APT::CacheSetHelper helper(true, GlobalError::NOTICE); | |
424 | APT::PackageList pkgset = APT::PackageList::FromCommandLine(CacheFile, CmdL.FileList + 1, helper); | |
425 | for (APT::PackageList::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg) | |
426 | { | |
427 | std::cout << Pkg.FullName(true) << ":" << std::endl; | |
428 | ||
429 | // Installed version | |
430 | std::cout << msgInstalled << OutputInDepth(deepInstalled, " "); | |
431 | if (Pkg->CurrentVer == 0) | |
432 | std::cout << _("(none)") << std::endl; | |
433 | else | |
434 | std::cout << Pkg.CurrentVer().VerStr() << std::endl; | |
435 | ||
436 | // Candidate Version | |
437 | std::cout << msgCandidate << OutputInDepth(deepCandidate, " "); | |
438 | pkgCache::VerIterator V = Plcy->GetCandidateVer(Pkg); | |
439 | if (V.end() == true) | |
440 | std::cout << _("(none)") << std::endl; | |
441 | else | |
442 | std::cout << V.VerStr() << std::endl; | |
443 | ||
444 | // Pinned version | |
445 | if (OldPolicy && Plcy->GetPriority(Pkg) != 0) | |
446 | { | |
447 | std::cout << _(" Package pin: "); | |
448 | V = Plcy->GetMatch(Pkg); | |
449 | if (V.end() == true) | |
450 | std::cout << _("(not found)") << std::endl; | |
451 | else | |
452 | std::cout << V.VerStr() << std::endl; | |
453 | } | |
454 | ||
455 | // Show the priority tables | |
456 | std::cout << _(" Version table:") << std::endl; | |
457 | for (V = Pkg.VersionList(); V.end() == false; ++V) | |
458 | { | |
459 | if (Pkg.CurrentVer() == V) | |
460 | std::cout << " *** " << V.VerStr(); | |
461 | else | |
462 | std::cout << " " << V.VerStr(); | |
463 | if (_config->FindI("APT::Policy", 1) < 1) | |
464 | std::cout << " " << Plcy->GetPriority(Pkg) << std::endl; | |
465 | else | |
466 | std::cout << " " << Plcy->GetPriority(V) << std::endl; | |
467 | for (pkgCache::VerFileIterator VF = V.FileList(); VF.end() == false; ++VF) | |
468 | { | |
469 | // Locate the associated index files so we can derive a description | |
470 | pkgIndexFile *Indx; | |
471 | if (SrcList->FindIndex(VF.File(),Indx) == false && | |
472 | _system->FindIndex(VF.File(),Indx) == false) | |
473 | return _error->Error(_("Cache is out of sync, can't x-ref a package file")); | |
474 | printf(" %4i %s\n",Plcy->GetPriority(VF.File()), | |
475 | Indx->Describe(true).c_str()); | |
476 | } | |
477 | } | |
478 | } | |
479 | return true; | |
480 | } | |
481 | /*}}}*/ |