]>
Commit | Line | Data |
---|---|---|
ee0167c4 | 1 | // Includes /*{{{*/ |
453b82a3 DK |
2 | #include <config.h> |
3 | ||
b9179170 | 4 | #include <apt-pkg/cachefile.h> |
b9179170 | 5 | #include <apt-pkg/cacheset.h> |
b9179170 | 6 | #include <apt-pkg/cmndline.h> |
453b82a3 | 7 | #include <apt-pkg/error.h> |
b9179170 | 8 | #include <apt-pkg/fileutl.h> |
453b82a3 | 9 | #include <apt-pkg/indexfile.h> |
b9179170 | 10 | #include <apt-pkg/pkgrecords.h> |
b9179170 | 11 | #include <apt-pkg/pkgsystem.h> |
453b82a3 DK |
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> | |
b9179170 | 20 | |
453b82a3 DK |
21 | #include <apt-private/private-cacheset.h> |
22 | #include <apt-private/private-output.h> | |
23 | #include <apt-private/private-show.h> | |
24 | ||
25 | #include <stdio.h> | |
26 | #include <ostream> | |
27 | #include <string> | |
b9179170 | 28 | |
453b82a3 | 29 | #include <apti18n.h> |
ee0167c4 | 30 | /*}}}*/ |
b9179170 | 31 | |
501cd23e DK |
32 | static bool OpenPackagesFile(pkgCacheFile &CacheFile, pkgCache::VerIterator const &V,/*{{{*/ |
33 | FileFd &PkgF, pkgCache::VerFileIterator &Vf) | |
b9179170 | 34 | { |
501cd23e | 35 | pkgCache const * const Cache = CacheFile.GetPkgCache(); |
b9179170 MV |
36 | if (unlikely(Cache == NULL)) |
37 | return false; | |
38 | ||
39 | // Find an appropriate file | |
501cd23e | 40 | Vf = V.FileList(); |
b9179170 MV |
41 | for (; Vf.end() == false; ++Vf) |
42 | if ((Vf.File()->Flags & pkgCache::Flag::NotSource) == 0) | |
43 | break; | |
44 | if (Vf.end() == true) | |
45 | Vf = V.FileList(); | |
46 | ||
47 | // Check and load the package list file | |
48 | pkgCache::PkgFileIterator I = Vf.File(); | |
49 | if (I.IsOk() == false) | |
50 | return _error->Error(_("Package file %s is out of sync."),I.FileName()); | |
51 | ||
501cd23e DK |
52 | // Read the record |
53 | return PkgF.Open(I.FileName(), FileFd::ReadOnly, FileFd::Extension); | |
54 | } | |
55 | /*}}}*/ | |
56 | static APT_PURE unsigned char const* skipDescriptionFields(unsigned char const * DescP)/*{{{*/ | |
57 | { | |
58 | char const * const TagName = "\nDescription"; | |
59 | size_t const TagLen = strlen(TagName); | |
60 | while ((DescP = (unsigned char*)strchr((char*)DescP, '\n')) != NULL) | |
61 | { | |
62 | if (DescP[1] == ' ') | |
63 | DescP += 2; | |
64 | else if (strncmp((char*)DescP, TagName, TagLen) == 0) | |
65 | DescP += TagLen; | |
66 | else | |
67 | break; | |
68 | } | |
69 | if (DescP != NULL) | |
70 | ++DescP; | |
71 | return DescP; | |
72 | } | |
73 | /*}}}*/ | |
74 | bool DisplayRecordV1(pkgCacheFile &CacheFile, pkgCache::VerIterator const &V,/*{{{*/ | |
75 | std::ostream &out) | |
76 | { | |
77 | FileFd PkgF; | |
78 | pkgCache::VerFileIterator Vf; | |
79 | if (OpenPackagesFile(CacheFile, V, PkgF, Vf) == false) | |
80 | return false; | |
81 | ||
82 | pkgCache * const Cache = CacheFile.GetPkgCache(); | |
83 | if (unlikely(Cache == NULL)) | |
84 | return false; | |
85 | ||
86 | // Read the record (and ensure that it ends with a newline and NUL) | |
87 | unsigned char *Buffer = new unsigned char[Cache->HeaderP->MaxVerFileSize+2]; | |
88 | Buffer[Vf->Size] = '\n'; | |
89 | Buffer[Vf->Size+1] = '\0'; | |
90 | if (PkgF.Seek(Vf->Offset) == false || | |
91 | PkgF.Read(Buffer,Vf->Size) == false) | |
92 | { | |
93 | delete [] Buffer; | |
94 | return false; | |
95 | } | |
96 | ||
97 | // Get a pointer to start of Description field | |
98 | const unsigned char *DescP = (unsigned char*)strstr((char*)Buffer, "\nDescription"); | |
99 | if (DescP != NULL) | |
100 | ++DescP; | |
101 | else | |
102 | DescP = Buffer + Vf->Size; | |
103 | ||
104 | // Write all but Description | |
105 | size_t const length = DescP - Buffer; | |
106 | if (length != 0 && FileFd::Write(STDOUT_FILENO, Buffer, length) == false) | |
107 | { | |
108 | delete [] Buffer; | |
109 | return false; | |
110 | } | |
111 | ||
112 | // Show the right description | |
113 | pkgRecords Recs(*Cache); | |
114 | pkgCache::DescIterator Desc = V.TranslatedDescription(); | |
115 | if (Desc.end() == false) | |
116 | { | |
117 | pkgRecords::Parser &P = Recs.Lookup(Desc.FileList()); | |
118 | out << "Description" << ( (strcmp(Desc.LanguageCode(),"") != 0) ? "-" : "" ) << Desc.LanguageCode() << ": " << P.LongDesc(); | |
119 | out << std::endl << "Description-md5: " << Desc.md5() << std::endl; | |
120 | ||
121 | // Find the first field after the description (if there is any) | |
122 | DescP = skipDescriptionFields(DescP); | |
123 | } | |
124 | // else we have no translation, so we found a lonely Description-md5 -> don't skip it | |
125 | ||
126 | // write the rest of the buffer, but skip mixed in Descriptions* fields | |
127 | while (DescP != NULL) | |
128 | { | |
129 | const unsigned char * const Start = DescP; | |
130 | const unsigned char *End = (unsigned char*)strstr((char*)DescP, "\nDescription"); | |
131 | if (End == NULL) | |
132 | { | |
133 | End = &Buffer[Vf->Size]; | |
134 | DescP = NULL; | |
135 | } | |
136 | else | |
137 | { | |
138 | ++End; // get the newline into the output | |
139 | DescP = skipDescriptionFields(End + strlen("Description")); | |
140 | } | |
141 | size_t const length = End - Start; | |
142 | if (length != 0 && FileFd::Write(STDOUT_FILENO, Start, length) == false) | |
143 | { | |
144 | delete [] Buffer; | |
145 | return false; | |
146 | } | |
147 | } | |
148 | // write a final newline after the last field | |
149 | out << std::endl; | |
150 | ||
151 | delete [] Buffer; | |
152 | return true; | |
153 | } | |
154 | /*}}}*/ | |
155 | static bool DisplayRecordV2(pkgCacheFile &CacheFile, pkgCache::VerIterator const &V,/*{{{*/ | |
156 | std::ostream &out) | |
157 | { | |
158 | FileFd PkgF; | |
159 | pkgCache::VerFileIterator Vf; | |
160 | if (OpenPackagesFile(CacheFile, V, PkgF, Vf) == false) | |
161 | return false; | |
162 | ||
163 | // Check and load the package list file | |
164 | pkgCache::PkgFileIterator I = Vf.File(); | |
165 | if (I.IsOk() == false) | |
166 | return _error->Error(_("Package file %s is out of sync."),I.FileName()); | |
167 | ||
1179953a MV |
168 | // find matching sources.list metaindex |
169 | pkgSourceList *SrcList = CacheFile.GetSourceList(); | |
170 | pkgIndexFile *Index; | |
171 | if (SrcList->FindIndex(I, Index) == false && | |
172 | _system->FindIndex(I, Index) == false) | |
173 | return _error->Error("Can not find indexfile for Package %s (%s)", | |
174 | V.ParentPkg().Name(), V.VerStr()); | |
175 | std::string source_index_file = Index->Describe(true); | |
176 | ||
b9179170 | 177 | // Read the record |
b9179170 MV |
178 | pkgTagSection Tags; |
179 | pkgTagFile TagF(&PkgF); | |
9e51c0b6 MV |
180 | |
181 | if (TagF.Jump(Tags, V.FileList()->Offset) == false) | |
182 | return _error->Error("Internal Error, Unable to parse a package record"); | |
183 | ||
184 | // make size nice | |
185 | std::string installed_size; | |
186 | if (Tags.FindI("Installed-Size") > 0) | |
17622532 | 187 | strprintf(installed_size, "%sB", SizeToStr(Tags.FindI("Installed-Size")*1024).c_str()); |
9e51c0b6 MV |
188 | else |
189 | installed_size = _("unknown"); | |
190 | std::string package_size; | |
191 | if (Tags.FindI("Size") > 0) | |
17622532 | 192 | strprintf(package_size, "%sB", SizeToStr(Tags.FindI("Size")).c_str()); |
9e51c0b6 MV |
193 | else |
194 | package_size = _("unknown"); | |
195 | ||
501cd23e DK |
196 | const char *manual_installed = nullptr; |
197 | if (V.ParentPkg().CurrentVer() == V) | |
198 | { | |
199 | pkgDepCache *depCache = CacheFile.GetDepCache(); | |
200 | if (unlikely(depCache == nullptr)) | |
201 | return false; | |
202 | pkgDepCache::StateCache &state = (*depCache)[V.ParentPkg()]; | |
85d7c0eb | 203 | manual_installed = !(state.Flags & pkgCache::Flag::Auto) ? "yes" : "no"; |
501cd23e | 204 | } |
17622532 MV |
205 | |
206 | // FIXME: add verbose that does not do the removal of the tags? | |
88593886 DK |
207 | std::vector<pkgTagSection::Tag> RW; |
208 | // delete, apt-cache show has this info and most users do not care | |
209 | RW.push_back(pkgTagSection::Tag::Remove("MD5sum")); | |
210 | RW.push_back(pkgTagSection::Tag::Remove("SHA1")); | |
211 | RW.push_back(pkgTagSection::Tag::Remove("SHA256")); | |
212 | RW.push_back(pkgTagSection::Tag::Remove("SHA512")); | |
213 | RW.push_back(pkgTagSection::Tag::Remove("Filename")); | |
214 | RW.push_back(pkgTagSection::Tag::Remove("Multi-Arch")); | |
215 | RW.push_back(pkgTagSection::Tag::Remove("Architecture")); | |
216 | RW.push_back(pkgTagSection::Tag::Remove("Conffiles")); | |
217 | // we use the translated description | |
218 | RW.push_back(pkgTagSection::Tag::Remove("Description")); | |
219 | RW.push_back(pkgTagSection::Tag::Remove("Description-md5")); | |
220 | // improve | |
221 | RW.push_back(pkgTagSection::Tag::Rewrite("Installed-Size", installed_size)); | |
222 | RW.push_back(pkgTagSection::Tag::Remove("Size")); | |
223 | RW.push_back(pkgTagSection::Tag::Rewrite("Download-Size", package_size)); | |
224 | // add | |
501cd23e DK |
225 | if (manual_installed != nullptr) |
226 | RW.push_back(pkgTagSection::Tag::Rewrite("APT-Manual-Installed", manual_installed)); | |
88593886 | 227 | RW.push_back(pkgTagSection::Tag::Rewrite("APT-Sources", source_index_file)); |
85d7c0eb | 228 | |
88593886 DK |
229 | FileFd stdoutfd; |
230 | if (stdoutfd.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly, false) == false || | |
231 | Tags.Write(stdoutfd, TFRewritePackageOrder, RW) == false || stdoutfd.Close() == false) | |
9e51c0b6 | 232 | return _error->Error("Internal Error, Unable to parse a package record"); |
b9179170 MV |
233 | |
234 | // write the description | |
501cd23e DK |
235 | pkgCache * const Cache = CacheFile.GetPkgCache(); |
236 | if (unlikely(Cache == NULL)) | |
237 | return false; | |
b9179170 | 238 | pkgRecords Recs(*Cache); |
2a79257e | 239 | // FIXME: show (optionally) all available translations(?) |
b9179170 MV |
240 | pkgCache::DescIterator Desc = V.TranslatedDescription(); |
241 | if (Desc.end() == false) | |
242 | { | |
243 | pkgRecords::Parser &P = Recs.Lookup(Desc.FileList()); | |
2a79257e | 244 | out << "Description: " << P.LongDesc(); |
b9179170 MV |
245 | } |
246 | ||
247 | // write a final newline (after the description) | |
248 | out << std::endl << std::endl; | |
249 | ||
250 | return true; | |
251 | } | |
252 | /*}}}*/ | |
ee0167c4 | 253 | bool ShowPackage(CommandLine &CmdL) /*{{{*/ |
b9179170 MV |
254 | { |
255 | pkgCacheFile CacheFile; | |
256 | CacheSetHelperVirtuals helper(true, GlobalError::NOTICE); | |
501cd23e | 257 | APT::CacheSetHelper::VerSelector const select = _config->FindB("APT::Cache::AllVersions", true) ? |
e6f0c9bc | 258 | APT::CacheSetHelper::ALL : APT::CacheSetHelper::CANDIDATE; |
b9179170 | 259 | APT::VersionList const verset = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1, select, helper); |
501cd23e | 260 | int const ShowVersion = _config->FindI("APT::Cache::Show::Version", 1); |
b9179170 | 261 | for (APT::VersionList::const_iterator Ver = verset.begin(); Ver != verset.end(); ++Ver) |
501cd23e DK |
262 | if (ShowVersion <= 1) |
263 | { | |
264 | if (DisplayRecordV1(CacheFile, Ver, std::cout) == false) | |
265 | return false; | |
266 | } | |
267 | else | |
268 | if (DisplayRecordV2(CacheFile, Ver, c1out) == false) | |
269 | return false; | |
b9179170 | 270 | |
e6f0c9bc | 271 | if (select == APT::CacheSetHelper::CANDIDATE) |
06293aa7 | 272 | { |
e6f0c9bc | 273 | APT::VersionList const verset_all = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1, APT::CacheSetHelper::ALL, helper); |
6298ff8b DK |
274 | int const records = verset_all.size() - verset.size(); |
275 | if (records > 0) | |
276 | _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); | |
06293aa7 MV |
277 | } |
278 | ||
501cd23e DK |
279 | if (_config->FindB("APT::Cache::ShowVirtuals", false) == true) |
280 | for (APT::PackageSet::const_iterator Pkg = helper.virtualPkgs.begin(); | |
281 | Pkg != helper.virtualPkgs.end(); ++Pkg) | |
282 | { | |
283 | c1out << "Package: " << Pkg.FullName(true) << std::endl; | |
284 | c1out << "State: " << _("not a real package (virtual)") << std::endl; | |
285 | // FIXME: show providers, see private-cacheset.h | |
286 | // CacheSetHelperAPTGet::showVirtualPackageErrors() | |
287 | } | |
b9179170 MV |
288 | |
289 | if (verset.empty() == true) | |
290 | { | |
291 | if (helper.virtualPkgs.empty() == true) | |
292 | return _error->Error(_("No packages found")); | |
293 | else | |
294 | _error->Notice(_("No packages found")); | |
295 | } | |
296 | ||
297 | return true; | |
298 | } | |
299 | /*}}}*/ |