]> git.saurik.com Git - apt.git/blame - apt-pkg/deb/deblistparser.cc
Optimize VersionHash() to not need temporary copy of input
[apt.git] / apt-pkg / deb / deblistparser.cc
CommitLineData
f55a958f
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
b3d44315 3// $Id: deblistparser.cc,v 1.29.2.5 2004/01/06 01:43:44 mdz Exp $
f55a958f
AL
4/* ######################################################################
5
6 Package Cache Generator - Generator for the cache structure.
7
8 This builds the cache structure from the abstract package list parser.
9
10 ##################################################################### */
11 /*}}}*/
12// Include Files /*{{{*/
ea542140
DK
13#include <config.h>
14
094a497d
AL
15#include <apt-pkg/deblistparser.h>
16#include <apt-pkg/error.h>
17#include <apt-pkg/configuration.h>
424ff669 18#include <apt-pkg/cachefilter.h>
45df0ad2 19#include <apt-pkg/aptconfiguration.h>
cdcc6d34 20#include <apt-pkg/strutl.h>
204fbdcc 21#include <apt-pkg/crc-16.h>
a52f938b 22#include <apt-pkg/md5.h>
453b82a3
DK
23#include <apt-pkg/pkgcache.h>
24#include <apt-pkg/cacheiterators.h>
25#include <apt-pkg/tagfile.h>
5664bb20 26#include <apt-pkg/tagfile-keys.h>
5c0d3668 27#include <apt-pkg/macros.h>
9c14e3d6 28
453b82a3
DK
29#include <stddef.h>
30#include <string.h>
31#include <algorithm>
32#include <string>
33#include <vector>
e7b470ee 34#include <ctype.h>
f55a958f
AL
35 /*}}}*/
36
8f3ba4e8 37using std::string;
eff0c22e 38using APT::StringView;
8f3ba4e8 39
f77ea39c 40static const debListParser::WordList PrioList[] = {
84de0cea
MV
41 {"required",pkgCache::State::Required},
42 {"important",pkgCache::State::Important},
43 {"standard",pkgCache::State::Standard},
44 {"optional",pkgCache::State::Optional},
45 {"extra",pkgCache::State::Extra},
3b4045fc 46 {"", 0}};
b2e465d6 47
f55a958f
AL
48// ListParser::debListParser - Constructor /*{{{*/
49// ---------------------------------------------------------------------
5dd4c8b8
DK
50/* Provide an architecture and only this one and "all" will be accepted
51 in Step(), if no Architecture is given we will accept every arch
52 we would accept in general with checkArchitecture() */
7f8c0eed 53debListParser::debListParser(FileFd *File) :
a9b724ee 54 pkgCacheListParser(), Tags(File)
c9443c01 55{
a9b724ee
DK
56 // this dance allows an empty value to override the default
57 if (_config->Exists("pkgCacheGen::ForceEssential"))
58 {
59 forceEssential = _config->FindVector("pkgCacheGen::ForceEssential");
60 if (forceEssential.empty() == false && _config->Find("pkgCacheGen::ForceEssential").empty())
61 forceEssential.emplace_back("apt");
62 }
63 else
64 forceEssential.emplace_back("apt");
65 forceImportant = _config->FindVector("pkgCacheGen::ForceImportant");
0149949b
AL
66}
67 /*}}}*/
f55a958f
AL
68// ListParser::Package - Return the package name /*{{{*/
69// ---------------------------------------------------------------------
70/* This is to return the name of the package this section describes */
5bf15716 71string debListParser::Package() {
5664bb20 72 string Result = Section.Find(pkgTagSection::Key::Package).to_string();
fffa3b57
JAK
73
74 // Normalize mixed case package names to lower case, like dpkg does
75 // See Bug#807012 for details
76 std::transform(Result.begin(), Result.end(), Result.begin(), tolower_ascii);
77
33dd02e3
DK
78 if(unlikely(Result.empty() == true))
79 _error->Error("Encountered a section with no Package: header");
80 return Result;
5bf15716
DK
81}
82 /*}}}*/
83// ListParser::Architecture - Return the package arch /*{{{*/
84// ---------------------------------------------------------------------
28166356 85/* This will return the Architecture of the package this section describes */
8efd5947 86APT::StringView debListParser::Architecture() {
5664bb20 87 auto const Arch = Section.Find(pkgTagSection::Key::Architecture);
8efd5947 88 return Arch.empty() ? "none" : Arch;
f55a958f
AL
89}
90 /*}}}*/
857e9c13
DK
91// ListParser::ArchitectureAll /*{{{*/
92// ---------------------------------------------------------------------
93/* */
94bool debListParser::ArchitectureAll() {
5664bb20 95 return Section.Find(pkgTagSection::Key::Architecture) == "all";
f55a958f
AL
96}
97 /*}}}*/
98// ListParser::Version - Return the version string /*{{{*/
99// ---------------------------------------------------------------------
100/* This is to return the string describing the version in debian form,
101 epoch:upstream-release. If this returns the blank string then the
102 entry is assumed to only describe package properties */
8efd5947 103APT::StringView debListParser::Version()
f55a958f 104{
5664bb20 105 return Section.Find(pkgTagSection::Key::Version);
f55a958f
AL
106}
107 /*}}}*/
e33dbfe5 108unsigned char debListParser::ParseMultiArch(bool const showErrors) /*{{{*/
0149949b 109{
e33dbfe5 110 unsigned char MA;
5664bb20 111 auto const MultiArch = Section.Find(pkgTagSection::Key::Multi_Arch);
19fb04e8 112 if (MultiArch.empty() == true || MultiArch == "no")
22df31be 113 MA = pkgCache::Version::No;
28166356 114 else if (MultiArch == "same") {
ca238ede 115 if (ArchitectureAll() == true)
25396fb0 116 {
e33dbfe5
DK
117 if (showErrors == true)
118 _error->Warning("Architecture: all package '%s' can't be Multi-Arch: same",
119 Section.FindS("Package").c_str());
22df31be 120 MA = pkgCache::Version::No;
25396fb0 121 }
28166356 122 else
e33dbfe5 123 MA = pkgCache::Version::Same;
28166356
DK
124 }
125 else if (MultiArch == "foreign")
e33dbfe5 126 MA = pkgCache::Version::Foreign;
28166356 127 else if (MultiArch == "allowed")
e33dbfe5 128 MA = pkgCache::Version::Allowed;
28166356
DK
129 else
130 {
e33dbfe5
DK
131 if (showErrors == true)
132 _error->Warning("Unknown Multi-Arch type '%s' for package '%s'",
eff0c22e 133 MultiArch.to_string().c_str(), Section.FindS("Package").c_str());
22df31be 134 MA = pkgCache::Version::No;
25396fb0
DK
135 }
136
ca238ede 137 if (ArchitectureAll() == true)
e33dbfe5 138 MA |= pkgCache::Version::All;
ca238ede 139
e33dbfe5
DK
140 return MA;
141}
142 /*}}}*/
143// ListParser::NewVersion - Fill in the version structure /*{{{*/
144// ---------------------------------------------------------------------
145/* */
146bool debListParser::NewVersion(pkgCache::VerIterator &Ver)
147{
78a5476f
DK
148 const char *Start;
149 const char *Stop;
150
e33dbfe5 151 // Parse the section
5664bb20 152 if (Section.Find(pkgTagSection::Key::Section,Start,Stop) == true)
78a5476f
DK
153 {
154 map_stringitem_t const idx = StoreString(pkgCacheGenerator::SECTION, Start, Stop - Start);
155 Ver->Section = idx;
156 }
a221efc3 157 // Parse the source package name
06a8e0dc 158 pkgCache::GrpIterator G = Ver.ParentPkg().Group();
a221efc3
DK
159 Ver->SourcePkgName = G->Name;
160 Ver->SourceVerStr = Ver->VerStr;
5664bb20 161 if (Section.Find(pkgTagSection::Key::Source,Start,Stop) == true)
a221efc3
DK
162 {
163 const char * const Space = (const char * const) memchr(Start, ' ', Stop - Start);
164 pkgCache::VerIterator V;
165
166 if (Space != NULL)
167 {
a221efc3
DK
168 const char * const Open = (const char * const) memchr(Space, '(', Stop - Space);
169 if (likely(Open != NULL))
170 {
171 const char * const Close = (const char * const) memchr(Open, ')', Stop - Open);
172 if (likely(Close != NULL))
173 {
8efd5947 174 APT::StringView const version(Open + 1, (Close - Open) - 1);
a221efc3
DK
175 if (version != Ver.VerStr())
176 {
30b45652 177 map_stringitem_t const idx = StoreString(pkgCacheGenerator::VERSIONNUMBER, version);
06a8e0dc 178 G = Ver.ParentPkg().Group();
a221efc3
DK
179 Ver->SourceVerStr = idx;
180 }
181 }
182 }
d17f2b1b 183 Stop = Space;
a221efc3
DK
184 }
185
8efd5947 186 APT::StringView const pkgname(Start, Stop - Start);
a221efc3
DK
187 if (pkgname != G.Name())
188 {
189 for (pkgCache::PkgIterator P = G.PackageList(); P.end() == false; P = G.NextPkg(P))
190 {
191 for (V = P.VersionList(); V.end() == false; ++V)
192 {
193 if (pkgname == V.SourcePkgName())
194 {
195 Ver->SourcePkgName = V->SourcePkgName;
196 break;
197 }
198 }
199 if (V.end() == false)
200 break;
201 }
202 if (V.end() == true)
203 {
204 map_stringitem_t const idx = StoreString(pkgCacheGenerator::PKGNAME, pkgname);
06a8e0dc 205 G = Ver.ParentPkg().Group();
a221efc3
DK
206 Ver->SourcePkgName = idx;
207 }
208 }
209 }
78a5476f 210
e33dbfe5 211 Ver->MultiArch = ParseMultiArch(true);
0149949b 212 // Archive Size
5664bb20 213 Ver->Size = Section.FindULL(pkgTagSection::Key::Size);
0149949b 214 // Unpacked Size (in K)
5664bb20 215 Ver->InstalledSize = Section.FindULL(pkgTagSection::Key::Installed_Size);
0149949b
AL
216 Ver->InstalledSize *= 1024;
217
218 // Priority
5664bb20 219 if (Section.Find(pkgTagSection::Key::Priority,Start,Stop) == true)
78a5476f 220 {
eff0c22e 221 if (GrabWord(StringView(Start,Stop-Start),PrioList,Ver->Priority) == false)
421c8d10 222 Ver->Priority = pkgCache::State::Extra;
0149949b 223 }
dcb79bae 224
5664bb20 225 if (ParseDepends(Ver,pkgTagSection::Key::Pre_Depends,pkgCache::Dep::PreDepends) == false)
dcb79bae 226 return false;
5664bb20 227 if (ParseDepends(Ver,pkgTagSection::Key::Depends,pkgCache::Dep::Depends) == false)
dcb79bae 228 return false;
5664bb20 229 if (ParseDepends(Ver,pkgTagSection::Key::Conflicts,pkgCache::Dep::Conflicts) == false)
dcb79bae 230 return false;
5664bb20 231 if (ParseDepends(Ver,pkgTagSection::Key::Breaks,pkgCache::Dep::DpkgBreaks) == false)
308c7d30 232 return false;
5664bb20 233 if (ParseDepends(Ver,pkgTagSection::Key::Recommends,pkgCache::Dep::Recommends) == false)
fd23676e 234 return false;
5664bb20 235 if (ParseDepends(Ver,pkgTagSection::Key::Suggests,pkgCache::Dep::Suggests) == false)
fd23676e 236 return false;
5664bb20 237 if (ParseDepends(Ver,pkgTagSection::Key::Replaces,pkgCache::Dep::Replaces) == false)
dcb79bae 238 return false;
5664bb20 239 if (ParseDepends(Ver,pkgTagSection::Key::Enhances,pkgCache::Dep::Enhances) == false)
f8ae7e8b 240 return false;
b2e465d6 241 // Obsolete.
5664bb20 242 if (ParseDepends(Ver,pkgTagSection::Key::Optional,pkgCache::Dep::Suggests) == false)
b2e465d6
AL
243 return false;
244
dcb79bae
AL
245 if (ParseProvides(Ver) == false)
246 return false;
0149949b 247
f55a958f
AL
248 return true;
249}
250 /*}}}*/
ffe3c68e
DK
251// ListParser::AvailableDescriptionLanguages /*{{{*/
252std::vector<std::string> debListParser::AvailableDescriptionLanguages()
a52f938b 253{
ffe3c68e
DK
254 std::vector<std::string> const understood = APT::Configuration::getLanguages();
255 std::vector<std::string> avail;
4a666d28
JAK
256 static constexpr int prefixLen = 12;
257 static constexpr int avgLanguageLen = 5;
258 std::string tagname;
259
260 tagname.reserve(prefixLen + avgLanguageLen);
261 tagname.assign("Description-");
ffe3c68e
DK
262 if (Section.Exists("Description") == true)
263 avail.push_back("");
264 for (std::vector<std::string>::const_iterator lang = understood.begin(); lang != understood.end(); ++lang)
265 {
4a666d28
JAK
266 tagname.resize(prefixLen);
267 tagname.append(*lang);
268 if (Section.Exists(tagname) == true)
ffe3c68e
DK
269 avail.push_back(*lang);
270 }
271 return avail;
a52f938b 272}
ffe3c68e
DK
273 /*}}}*/
274// ListParser::Description_md5 - Return the description_md5 MD5SumValue /*{{{*/
a52f938b 275// ---------------------------------------------------------------------
770c32ec
MV
276/* This is to return the md5 string to allow the check if it is the right
277 description. If no Description-md5 is found in the section it will be
278 calculated.
279 */
a52f938b
OS
280MD5SumValue debListParser::Description_md5()
281{
5664bb20 282 StringView const value = Section.Find(pkgTagSection::Key::Description_md5);
fc691496 283 if (value.empty() == true)
770c32ec 284 {
5664bb20 285 StringView const desc = Section.Find(pkgTagSection::Key::Description);
99359751
DK
286 if (desc == "\n")
287 return MD5SumValue();
288
a52f938b 289 MD5Summation md5;
eff0c22e
JAK
290 md5.Add(desc.data(), desc.size());
291 md5.Add("\n");
a52f938b 292 return md5.Result();
fc691496
DK
293 }
294 else if (likely(value.size() == 32))
295 {
50b7dbc5
JAK
296 MD5SumValue sumvalue;
297 if (sumvalue.Set(value))
298 return sumvalue;
299
eff0c22e 300 _error->Error("Malformed Description-md5 line; includes invalid character '%.*s'", (int)value.length(), value.data());
fc691496
DK
301 return MD5SumValue();
302 }
eff0c22e 303 _error->Error("Malformed Description-md5 line; doesn't have the required length (32 != %d) '%.*s'", (int)value.size(), (int)value.length(), value.data());
fc691496 304 return MD5SumValue();
a52f938b
OS
305}
306 /*}}}*/
f55a958f
AL
307// ListParser::UsePackage - Update a package structure /*{{{*/
308// ---------------------------------------------------------------------
309/* This is called to update the package with any new information
310 that might be found in the section */
32b9a14c
DK
311bool debListParser::UsePackage(pkgCache::PkgIterator &Pkg,
312 pkgCache::VerIterator &Ver)
f55a958f 313{
5dd4c8b8 314 string const static myArch = _config->Find("APT::Architecture");
e5a91f7e
DK
315 // Possible values are: "all", "native", "installed" and "none"
316 // The "installed" mode is handled by ParseStatus(), See #544481 and friends.
317 string const static essential = _config->Find("pkgCacheGen::Essential", "all");
318 if (essential == "all" ||
319 (essential == "native" && Pkg->Arch != 0 && myArch == Pkg.Arch()))
5664bb20 320 if (Section.FindFlag(pkgTagSection::Key::Essential,Pkg->Flags,pkgCache::Flag::Essential) == false)
5dd4c8b8 321 return false;
5664bb20 322 if (Section.FindFlag(pkgTagSection::Key::Important,Pkg->Flags,pkgCache::Flag::Important) == false)
f55a958f 323 return false;
138d4b3d 324
a9b724ee 325 if (std::find(forceEssential.begin(), forceEssential.end(), Pkg.Name()) != forceEssential.end())
a552f37e
DK
326 {
327 if ((essential == "native" && Pkg->Arch != 0 && myArch == Pkg.Arch()) ||
328 essential == "all")
329 Pkg->Flags |= pkgCache::Flag::Essential | pkgCache::Flag::Important;
330 else
331 Pkg->Flags |= pkgCache::Flag::Important;
332 }
a9b724ee
DK
333 else if (std::find(forceImportant.begin(), forceImportant.end(), Pkg.Name()) != forceImportant.end())
334 Pkg->Flags |= pkgCache::Flag::Important;
a552f37e 335
f55a958f
AL
336 if (ParseStatus(Pkg,Ver) == false)
337 return false;
338 return true;
339}
340 /*}}}*/
204fbdcc
AL
341// ListParser::VersionHash - Compute a unique hash for this version /*{{{*/
342// ---------------------------------------------------------------------
343/* */
344unsigned short debListParser::VersionHash()
345{
5664bb20
JAK
346 static constexpr pkgTagSection::Key Sections[] ={
347 pkgTagSection::Key::Installed_Size,
348 pkgTagSection::Key::Depends,
349 pkgTagSection::Key::Pre_Depends,
350// pkgTagSection::Key::Suggests,
351// pkgTagSection::Key::Recommends",
352 pkgTagSection::Key::Conflicts,
353 pkgTagSection::Key::Breaks,
354 pkgTagSection::Key::Replaces};
204fbdcc 355 unsigned long Result = INIT_FCS;
5664bb20 356 for (auto I : Sections)
204fbdcc
AL
357 {
358 const char *Start;
359 const char *End;
708e2f1f 360 if (Section.Find(I,Start,End) == false)
204fbdcc
AL
361 continue;
362
363 /* Strip out any spaces from the text, this undoes dpkgs reformatting
421c8d10 364 of certain fields. dpkg also has the rather interesting notion of
f903069c
JAK
365 reformatting depends operators < -> <=, so we drop all = from the
366 string to make that not matter. */
367 for (; Start != End; ++Start)
421c8d10 368 {
f903069c 369 if (isspace_ascii(*Start) != 0 || *Start == '=')
d9682cf8 370 continue;
f903069c 371 Result = AddCRC16Byte(Result, tolower_ascii_unsafe(*Start));
421c8d10 372 }
418a471f 373
f903069c 374
204fbdcc
AL
375 }
376
377 return Result;
378}
379 /*}}}*/
1c73b0fc 380// StatusListParser::ParseStatus - Parse the status field /*{{{*/
f55a958f
AL
381// ---------------------------------------------------------------------
382/* Status lines are of the form,
383 Status: want flag status
384 want = unknown, install, hold, deinstall, purge
9227645d
GJ
385 flag = ok, reinstreq
386 status = not-installed, config-files, half-installed, unpacked,
387 half-configured, triggers-awaited, triggers-pending, installed
f55a958f 388 */
b830f576
DK
389bool debListParser::ParseStatus(pkgCache::PkgIterator &,
390 pkgCache::VerIterator &)
1c73b0fc
JAK
391{
392 return true;
393}
394bool debStatusListParser::ParseStatus(pkgCache::PkgIterator &Pkg,
395 pkgCache::VerIterator &Ver)
f55a958f
AL
396{
397 const char *Start;
398 const char *Stop;
5664bb20 399 if (Section.Find(pkgTagSection::Key::Status,Start,Stop) == false)
f55a958f 400 return true;
6bc703c2
DK
401
402 // UsePackage() is responsible for setting the flag in the default case
403 bool const static essential = _config->Find("pkgCacheGen::Essential", "") == "installed";
404 if (essential == true &&
5664bb20 405 Section.FindFlag(pkgTagSection::Key::Essential,Pkg->Flags,pkgCache::Flag::Essential) == false)
6bc703c2
DK
406 return false;
407
f55a958f
AL
408 // Isolate the first word
409 const char *I = Start;
410 for(; I < Stop && *I != ' '; I++);
411 if (I >= Stop || *I != ' ')
412 return _error->Error("Malformed Status line");
413
414 // Process the want field
6c139d6e
AL
415 WordList WantList[] = {{"unknown",pkgCache::State::Unknown},
416 {"install",pkgCache::State::Install},
417 {"hold",pkgCache::State::Hold},
418 {"deinstall",pkgCache::State::DeInstall},
b2e465d6 419 {"purge",pkgCache::State::Purge},
3b4045fc 420 {"", 0}};
eff0c22e 421 if (GrabWord(StringView(Start,I-Start),WantList,Pkg->SelectedState) == false)
f55a958f
AL
422 return _error->Error("Malformed 1st word in the Status line");
423
424 // Isloate the next word
425 I++;
426 Start = I;
427 for(; I < Stop && *I != ' '; I++);
428 if (I >= Stop || *I != ' ')
429 return _error->Error("Malformed status line, no 2nd word");
430
431 // Process the flag field
6c139d6e
AL
432 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
433 {"reinstreq",pkgCache::State::ReInstReq},
434 {"hold",pkgCache::State::HoldInst},
b2e465d6 435 {"hold-reinstreq",pkgCache::State::HoldReInstReq},
3b4045fc 436 {"", 0}};
eff0c22e 437 if (GrabWord(StringView(Start,I-Start),FlagList,Pkg->InstState) == false)
f55a958f
AL
438 return _error->Error("Malformed 2nd word in the Status line");
439
440 // Isloate the last word
441 I++;
442 Start = I;
443 for(; I < Stop && *I != ' '; I++);
444 if (I != Stop)
445 return _error->Error("Malformed Status line, no 3rd word");
446
447 // Process the flag field
6c139d6e 448 WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled},
9227645d
GJ
449 {"config-files",pkgCache::State::ConfigFiles},
450 {"half-installed",pkgCache::State::HalfInstalled},
6c139d6e
AL
451 {"unpacked",pkgCache::State::UnPacked},
452 {"half-configured",pkgCache::State::HalfConfigured},
9d06bc80
MV
453 {"triggers-awaited",pkgCache::State::TriggersAwaited},
454 {"triggers-pending",pkgCache::State::TriggersPending},
9227645d 455 {"installed",pkgCache::State::Installed},
3b4045fc 456 {"", 0}};
eff0c22e 457 if (GrabWord(StringView(Start,I-Start),StatusList,Pkg->CurrentState) == false)
f55a958f
AL
458 return _error->Error("Malformed 3rd word in the Status line");
459
460 /* A Status line marks the package as indicating the current
461 version as well. Only if it is actually installed.. Otherwise
462 the interesting dpkg handling of the status file creates bogus
463 entries. */
6c139d6e
AL
464 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
465 Pkg->CurrentState == pkgCache::State::ConfigFiles))
f55a958f
AL
466 {
467 if (Ver.end() == true)
468 _error->Warning("Encountered status field in a non-version description");
469 else
470 Pkg->CurrentVer = Ver.Index();
471 }
472
dcb79bae
AL
473 return true;
474}
a1826878 475
b2e465d6
AL
476const char *debListParser::ConvertRelation(const char *I,unsigned int &Op)
477{
478 // Determine the operator
479 switch (*I)
480 {
481 case '<':
482 I++;
483 if (*I == '=')
484 {
485 I++;
486 Op = pkgCache::Dep::LessEq;
487 break;
488 }
489
490 if (*I == '<')
491 {
492 I++;
493 Op = pkgCache::Dep::Less;
494 break;
495 }
496
497 // < is the same as <= and << is really Cs < for some reason
498 Op = pkgCache::Dep::LessEq;
499 break;
500
501 case '>':
502 I++;
503 if (*I == '=')
504 {
505 I++;
506 Op = pkgCache::Dep::GreaterEq;
507 break;
508 }
509
510 if (*I == '>')
511 {
512 I++;
513 Op = pkgCache::Dep::Greater;
514 break;
515 }
516
517 // > is the same as >= and >> is really Cs > for some reason
518 Op = pkgCache::Dep::GreaterEq;
519 break;
520
521 case '=':
522 Op = pkgCache::Dep::Equals;
523 I++;
524 break;
525
526 // HACK around bad package definitions
527 default:
528 Op = pkgCache::Dep::Equals;
529 break;
530 }
531 return I;
532}
a1826878
AL
533 /*}}}*/
534// ListParser::ParseDepends - Parse a dependency element /*{{{*/
535// ---------------------------------------------------------------------
536/* This parses the dependency elements out of a standard string in place,
537 bit by bit. */
565ded7b
JS
538const char *debListParser::ParseDepends(const char *Start,const char *Stop,
539 std::string &Package,std::string &Ver,unsigned int &Op)
540 { return ParseDepends(Start, Stop, Package, Ver, Op, false, true, false); }
541const char *debListParser::ParseDepends(const char *Start,const char *Stop,
542 std::string &Package,std::string &Ver,unsigned int &Op,
543 bool const &ParseArchFlags)
544 { return ParseDepends(Start, Stop, Package, Ver, Op, ParseArchFlags, true, false); }
545const char *debListParser::ParseDepends(const char *Start,const char *Stop,
546 std::string &Package,std::string &Ver,unsigned int &Op,
547 bool const &ParseArchFlags, bool const &StripMultiArch)
548 { return ParseDepends(Start, Stop, Package, Ver, Op, ParseArchFlags, StripMultiArch, false); }
dcb79bae
AL
549const char *debListParser::ParseDepends(const char *Start,const char *Stop,
550 string &Package,string &Ver,
41c81fd8 551 unsigned int &Op, bool const &ParseArchFlags,
565ded7b
JS
552 bool const &StripMultiArch,
553 bool const &ParseRestrictionsList)
eff0c22e
JAK
554{
555 StringView PackageView;
556 StringView VerView;
557
558 auto res = ParseDepends(Start, Stop, PackageView, VerView, Op, (bool)ParseArchFlags,
559 (bool) StripMultiArch, (bool) ParseRestrictionsList);
560 Package = PackageView.to_string();
561 Ver = VerView.to_string();
562
563 return res;
564}
565const char *debListParser::ParseDepends(const char *Start,const char *Stop,
566 StringView &Package,StringView &Ver,
567 unsigned int &Op, bool ParseArchFlags,
568 bool StripMultiArch,
569 bool ParseRestrictionsList)
dcb79bae
AL
570{
571 // Strip off leading space
74dedb4a 572 for (;Start != Stop && isspace_ascii(*Start) != 0; ++Start);
dcb79bae
AL
573
574 // Parse off the package name
575 const char *I = Start;
74dedb4a 576 for (;I != Stop && isspace_ascii(*I) == 0 && *I != '(' && *I != ')' &&
565ded7b
JS
577 *I != ',' && *I != '|' && *I != '[' && *I != ']' &&
578 *I != '<' && *I != '>'; ++I);
dcb79bae
AL
579
580 // Malformed, no '('
581 if (I != Stop && *I == ')')
582 return 0;
583
584 if (I == Start)
585 return 0;
586
587 // Stash the package name
eff0c22e 588 Package = StringView(Start, I - Start);
41c81fd8
DK
589
590 // We don't want to confuse library users which can't handle MultiArch
591 if (StripMultiArch == true) {
0e0bff78 592 string const arch = _config->Find("APT::Architecture");
41c81fd8 593 size_t const found = Package.rfind(':');
eff0c22e 594 if (found != StringView::npos &&
57590d3c
JAK
595 (Package.substr(found) == ":any" ||
596 Package.substr(found) == ":native" ||
597 Package.substr(found +1) == arch))
41c81fd8
DK
598 Package = Package.substr(0,found);
599 }
600
dcb79bae 601 // Skip white space to the '('
74dedb4a 602 for (;I != Stop && isspace_ascii(*I) != 0 ; I++);
dcb79bae
AL
603
604 // Parse a version
605 if (I != Stop && *I == '(')
606 {
607 // Skip the '('
74dedb4a 608 for (I++; I != Stop && isspace_ascii(*I) != 0 ; I++);
dcb79bae
AL
609 if (I + 3 >= Stop)
610 return 0;
b2e465d6 611 I = ConvertRelation(I,Op);
dcb79bae
AL
612
613 // Skip whitespace
74dedb4a 614 for (;I != Stop && isspace_ascii(*I) != 0; I++);
dcb79bae 615 Start = I;
404528bd
DK
616 I = (const char*) memchr(I, ')', Stop - I);
617 if (I == NULL || Start == I)
618 return 0;
dcb79bae 619
02f000a9
AL
620 // Skip trailing whitespace
621 const char *End = I;
74dedb4a 622 for (; End > Start && isspace_ascii(End[-1]); End--);
02f000a9 623
eff0c22e 624 Ver = StringView(Start,End-Start);
dcb79bae
AL
625 I++;
626 }
627 else
628 {
eff0c22e 629 Ver = StringView();
6c139d6e 630 Op = pkgCache::Dep::NoOp;
dcb79bae
AL
631 }
632
633 // Skip whitespace
74dedb4a 634 for (;I != Stop && isspace_ascii(*I) != 0; I++);
b2e465d6 635
14704162 636 if (unlikely(ParseArchFlags == true))
b2e465d6 637 {
0e0bff78 638 string const arch = _config->Find("APT::Architecture");
424ff669 639 APT::CacheFilter::PackageArchitectureMatchesSpecification matchesArch(arch, false);
9e10ad8a 640
b2e465d6
AL
641 // Parse an architecture
642 if (I != Stop && *I == '[')
643 {
cd9694bf 644 ++I;
b2e465d6 645 // malformed
cd9694bf
DK
646 if (unlikely(I == Stop))
647 return 0;
648
649 const char *End = I;
650 bool Found = false;
651 bool NegArch = false;
652 while (I != Stop)
b2e465d6 653 {
cd9694bf 654 // look for whitespace or ending ']'
74dedb4a 655 for (;End != Stop && !isspace_ascii(*End) && *End != ']'; ++End);
cd9694bf
DK
656
657 if (unlikely(End == Stop))
b2e465d6 658 return 0;
9e10ad8a
AL
659
660 if (*I == '!')
cd9694bf 661 {
9e10ad8a 662 NegArch = true;
cd9694bf
DK
663 ++I;
664 }
9e10ad8a 665
8efd5947 666 std::string const arch(I, End);
424ff669 667 if (arch.empty() == false && matchesArch(arch.c_str()) == true)
cd9694bf 668 {
424ff669 669 Found = true;
cd9694bf
DK
670 if (I[-1] != '!')
671 NegArch = false;
672 // we found a match, so fast-forward to the end of the wildcards
673 for (; End != Stop && *End != ']'; ++End);
674 }
675
b2e465d6
AL
676 if (*End++ == ']') {
677 I = End;
678 break;
679 }
cd9694bf 680
b2e465d6 681 I = End;
74dedb4a 682 for (;I != Stop && isspace_ascii(*I) != 0; I++);
cd9694bf 683 }
9e10ad8a 684
cd9694bf 685 if (NegArch == true)
9e10ad8a 686 Found = !Found;
cd9694bf
DK
687
688 if (Found == false)
b2e465d6
AL
689 Package = ""; /* not for this arch */
690 }
cd9694bf 691
b2e465d6 692 // Skip whitespace
74dedb4a 693 for (;I != Stop && isspace_ascii(*I) != 0; I++);
b2e465d6
AL
694 }
695
14704162 696 if (unlikely(ParseRestrictionsList == true))
565ded7b 697 {
ac81c0f9 698 // Parse a restrictions formula which is in disjunctive normal form:
699 // (foo AND bar) OR (blub AND bla)
700
701 std::vector<string> const profiles = APT::Configuration::getBuildProfiles();
702
703 // if the next character is a restriction list, then by default the
704 // dependency does not apply and the conditions have to be checked
705 // if the next character is not a restriction list, then by default the
706 // dependency applies
707 bool applies1 = (*I != '<');
708 while (I != Stop)
565ded7b 709 {
ac81c0f9 710 if (*I != '<')
711 break;
712
565ded7b
JS
713 ++I;
714 // malformed
715 if (unlikely(I == Stop))
716 return 0;
717
565ded7b 718 const char *End = I;
565ded7b 719
ac81c0f9 720 // if of the prior restriction list is already fulfilled, then
721 // we can just skip to the end of the current list
722 if (applies1) {
723 for (;End != Stop && *End != '>'; ++End);
724 I = ++End;
725 // skip whitespace
74dedb4a 726 for (;I != Stop && isspace_ascii(*I) != 0; I++);
ac81c0f9 727 } else {
728 bool applies2 = true;
729 // all the conditions inside a restriction list have to be
730 // met so once we find one that is not met, we can skip to
731 // the end of this list
732 while (I != Stop)
565ded7b 733 {
ac81c0f9 734 // look for whitespace or ending '>'
735 // End now points to the character after the current term
74dedb4a 736 for (;End != Stop && !isspace_ascii(*End) && *End != '>'; ++End);
565ded7b 737
ac81c0f9 738 if (unlikely(End == Stop))
739 return 0;
565ded7b 740
ac81c0f9 741 bool NegRestriction = false;
742 if (*I == '!')
743 {
744 NegRestriction = true;
745 ++I;
746 }
747
8efd5947 748 std::string const restriction(I, End);
565ded7b 749 if (restriction.empty() == false && profiles.empty() == false &&
ac81c0f9 750 std::find(profiles.begin(), profiles.end(), restriction) != profiles.end())
565ded7b 751 {
ac81c0f9 752 if (NegRestriction) {
753 applies2 = false;
754 // since one of the terms does not apply we don't have to check the others
755 for (; End != Stop && *End != '>'; ++End);
756 }
757 } else {
758 if (!NegRestriction) {
759 applies2 = false;
760 // since one of the terms does not apply we don't have to check the others
761 for (; End != Stop && *End != '>'; ++End);
762 }
763 }
764
765 if (*End++ == '>') {
766 I = End;
767 // skip whitespace
74dedb4a 768 for (;I != Stop && isspace_ascii(*I) != 0; I++);
ac81c0f9 769 break;
565ded7b 770 }
565ded7b 771
565ded7b 772 I = End;
ac81c0f9 773 // skip whitespace
74dedb4a 774 for (;I != Stop && isspace_ascii(*I) != 0; I++);
ac81c0f9 775 }
776 if (applies2) {
777 applies1 = true;
565ded7b 778 }
565ded7b 779 }
565ded7b
JS
780 }
781
ac81c0f9 782 if (applies1 == false) {
783 Package = ""; //not for this restriction
784 }
565ded7b
JS
785 }
786
dcb79bae 787 if (I != Stop && *I == '|')
6c139d6e 788 Op |= pkgCache::Dep::Or;
dcb79bae
AL
789
790 if (I == Stop || *I == ',' || *I == '|')
791 {
792 if (I != Stop)
74dedb4a 793 for (I++; I != Stop && isspace_ascii(*I) != 0; I++);
dcb79bae
AL
794 return I;
795 }
796
797 return 0;
798}
799 /*}}}*/
800// ListParser::ParseDepends - Parse a dependency list /*{{{*/
801// ---------------------------------------------------------------------
802/* This is the higher level depends parser. It takes a tag and generates
803 a complete depends tree for the given version. */
32b9a14c 804bool debListParser::ParseDepends(pkgCache::VerIterator &Ver,
5664bb20 805 pkgTagSection::Key Key,unsigned int Type)
dcb79bae
AL
806{
807 const char *Start;
808 const char *Stop;
5664bb20 809 if (Section.Find(Key,Start,Stop) == false || Start == Stop)
dcb79bae 810 return true;
306eacf6 811
28166356 812 string const pkgArch = Ver.Arch();
dcb79bae 813
8efa2a3b 814 while (1)
dcb79bae 815 {
eff0c22e
JAK
816 StringView Package;
817 StringView Version;
0f485ee5
TG
818 unsigned int Op;
819
565ded7b 820 Start = ParseDepends(Start, Stop, Package, Version, Op, false, false, false);
dcb79bae 821 if (Start == 0)
5664bb20 822 return _error->Error("Problem parsing dependency %zu",static_cast<size_t>(Key)); // TODO
0f485ee5 823 size_t const found = Package.rfind(':');
306eacf6 824
f6ce7ffc 825 if (found == string::npos)
306eacf6 826 {
bb0f6a34 827 if (NewDepends(Ver,Package,pkgArch,Version,Op,Type) == false)
c919ad6e 828 return false;
306eacf6 829 }
57590d3c 830 else if (Package.substr(found) == ":any")
f6ce7ffc
DK
831 {
832 if (NewDepends(Ver,Package,"any",Version,Op,Type) == false)
833 return false;
834 }
bb0f6a34 835 else
0f485ee5 836 {
0f485ee5 837 // Such dependencies are not supposed to be accepted …
bb0f6a34 838 // … but this is probably the best thing to do anyway
3addaba1 839 if (Package.substr(found + 1) == "native")
8efd5947
DK
840 {
841 std::string const Pkg = Package.substr(0, found).to_string() + ':' + Ver.Cache()->NativeArch();
842 if (NewDepends(Ver, Pkg, "any", Version, Op | pkgCache::Dep::ArchSpecific, Type) == false)
843 return false;
844 }
845 else if (NewDepends(Ver, Package, "any", Version, Op | pkgCache::Dep::ArchSpecific, Type) == false)
0f485ee5
TG
846 return false;
847 }
bb0f6a34 848
8efa2a3b
AL
849 if (Start == Stop)
850 break;
dcb79bae
AL
851 }
852 return true;
853}
854 /*}}}*/
855// ListParser::ParseProvides - Parse the provides list /*{{{*/
856// ---------------------------------------------------------------------
857/* */
32b9a14c 858bool debListParser::ParseProvides(pkgCache::VerIterator &Ver)
dcb79bae 859{
3addaba1
DK
860 /* it is unlikely, but while parsing dependencies, we might have already
861 picked up multi-arch implicit provides which we do not want to duplicate here */
862 bool hasProvidesAlready = false;
863 std::string const spzName = Ver.ParentPkg().FullName(false);
864 {
865 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
866 {
867 if (Prv.IsMultiArchImplicit() == false || (Prv->Flags & pkgCache::Flag::ArchSpecific) == 0)
868 continue;
869 if (spzName != Prv.OwnerPkg().FullName(false))
870 continue;
871 hasProvidesAlready = true;
872 break;
873 }
874 }
875
384f17b4 876 string const Arch = Ver.Arch();
dcb79bae
AL
877 const char *Start;
878 const char *Stop;
5664bb20 879 if (Section.Find(pkgTagSection::Key::Provides,Start,Stop) == true)
dcb79bae 880 {
eff0c22e
JAK
881 StringView Package;
882 StringView Version;
67e0766f
DK
883 unsigned int Op;
884
3addaba1 885 do
67e0766f 886 {
3addaba1 887 Start = ParseDepends(Start,Stop,Package,Version,Op, false, false, false);
b209edfa 888 const size_t archfound = Package.rfind(':');
67e0766f
DK
889 if (Start == 0)
890 return _error->Error("Problem parsing Provides line");
eff0c22e
JAK
891 if (unlikely(Op != pkgCache::Dep::NoOp && Op != pkgCache::Dep::Equals)) {
892 _error->Warning("Ignoring Provides line with non-equal DepCompareOp for package %s", Package.to_string().c_str());
b209edfa 893 } else if (archfound != string::npos) {
eff0c22e 894 StringView spzArch = Package.substr(archfound + 1);
3addaba1
DK
895 if (spzArch != "any")
896 {
897 if (NewProvides(Ver, Package.substr(0, archfound), spzArch, Version, pkgCache::Flag::MultiArchImplicit | pkgCache::Flag::ArchSpecific) == false)
898 return false;
899 }
900 if (NewProvides(Ver, Package, "any", Version, pkgCache::Flag::ArchSpecific) == false)
b209edfa 901 return false;
04340db3 902 } else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign) {
384f17b4 903 if (APT::Configuration::checkArchitecture(Arch))
62428dbc 904 {
384f17b4
DK
905 if (NewProvidesAllArch(Ver, Package, Version, 0) == false)
906 return false;
62428dbc
DK
907 }
908 else if (NewProvides(Ver, Package, Arch, Version, 0) == false)
909 return false;
67e0766f 910 } else {
3addaba1
DK
911 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
912 {
eff0c22e 913 if (NewProvides(Ver, Package.to_string().append(":any"), "any", Version, pkgCache::Flag::MultiArchImplicit) == false)
3addaba1
DK
914 return false;
915 }
8c7af4d4 916 if (NewProvides(Ver, Package, Arch, Version, 0) == false)
67e0766f
DK
917 return false;
918 }
3addaba1
DK
919 if (archfound == std::string::npos)
920 {
eff0c22e
JAK
921 string spzName = Package.to_string();
922 spzName.push_back(':');
923 spzName.append(Ver.ParentPkg().Arch());
3addaba1
DK
924 pkgCache::PkgIterator const spzPkg = Ver.Cache()->FindPkg(spzName, "any");
925 if (spzPkg.end() == false)
926 {
927 if (NewProvides(Ver, spzName, "any", Version, pkgCache::Flag::MultiArchImplicit | pkgCache::Flag::ArchSpecific) == false)
928 return false;
929 }
930 }
931 } while (Start != Stop);
67e0766f 932 }
dcb79bae 933
384f17b4 934 if (APT::Configuration::checkArchitecture(Arch))
4d174dc8 935 {
384f17b4
DK
936 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
937 {
938 string const Package = string(Ver.ParentPkg().Name()).append(":").append("any");
3addaba1
DK
939 if (NewProvides(Ver, Package, "any", Ver.VerStr(), pkgCache::Flag::MultiArchImplicit) == false)
940 return false;
384f17b4
DK
941 }
942 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
3addaba1
DK
943 {
944 if (NewProvidesAllArch(Ver, Ver.ParentPkg().Name(), Ver.VerStr(), pkgCache::Flag::MultiArchImplicit) == false)
945 return false;
946 }
dcb79bae 947 }
dcb79bae 948
3addaba1
DK
949 if (hasProvidesAlready == false)
950 {
951 pkgCache::PkgIterator const spzPkg = Ver.Cache()->FindPkg(spzName, "any");
952 if (spzPkg.end() == false)
953 {
954 if (NewProvides(Ver, spzName, "any", Ver.VerStr(), pkgCache::Flag::MultiArchImplicit | pkgCache::Flag::ArchSpecific) == false)
955 return false;
956 }
957 }
60dcec6d
DK
958 return true;
959}
960 /*}}}*/
f55a958f
AL
961// ListParser::GrabWord - Matches a word and returns /*{{{*/
962// ---------------------------------------------------------------------
963/* Looks for a word in a list of words - for ParseStatus */
3b4045fc 964bool debListParser::GrabWord(StringView Word, WordList const *List, unsigned char &Out)
f55a958f 965{
3b4045fc 966 for (unsigned int C = 0; List[C].Str.empty() == false; C++)
f55a958f 967 {
3b4045fc
DK
968 if (Word.length() == List[C].Str.length() &&
969 strncasecmp(Word.data(), List[C].Str.data(), Word.length()) == 0)
f55a958f
AL
970 {
971 Out = List[C].Val;
972 return true;
973 }
974 }
975 return false;
976}
977 /*}}}*/
978// ListParser::Step - Move to the next section in the file /*{{{*/
979// ---------------------------------------------------------------------
1e3f4083 980/* This has to be careful to only process the correct architecture */
f55a958f
AL
981bool debListParser::Step()
982{
dcb79bae 983 iOffset = Tags.Offset();
7f8c0eed 984 return Tags.Step(Section);
f55a958f
AL
985}
986 /*}}}*/
b2e465d6
AL
987// ListParser::GetPrio - Convert the priority from a string /*{{{*/
988// ---------------------------------------------------------------------
989/* */
990unsigned char debListParser::GetPrio(string Str)
991{
992 unsigned char Out;
993 if (GrabWord(Str,PrioList,Out) == false)
994 Out = pkgCache::State::Extra;
995
996 return Out;
997}
998 /*}}}*/
68134bda
DK
999bool debListParser::SameVersion(unsigned short const Hash, /*{{{*/
1000 pkgCache::VerIterator const &Ver)
1001{
c9443c01 1002 if (pkgCacheListParser::SameVersion(Hash, Ver) == false)
68134bda
DK
1003 return false;
1004 // status file has no (Download)Size, but all others are fair game
1005 // status file is parsed last, so the first version we encounter is
1006 // probably also the version we have downloaded
5664bb20 1007 unsigned long long const Size = Section.FindULL(pkgTagSection::Key::Size);
e7edb2fe 1008 if (Size != 0 && Ver->Size != 0 && Size != Ver->Size)
68134bda
DK
1009 return false;
1010 // available everywhere, but easier to check here than to include in VersionHash
1011 unsigned char MultiArch = ParseMultiArch(false);
1012 if (MultiArch != Ver->MultiArch)
1013 return false;
1014 // for all practical proposes (we can check): same version
1015 return true;
1016}
1017 /*}}}*/
0d29b9d4
MV
1018
1019debDebFileParser::debDebFileParser(FileFd *File, std::string const &DebFile)
7f8c0eed 1020 : debListParser(File), DebFile(DebFile)
0d29b9d4
MV
1021{
1022}
1023
1024bool debDebFileParser::UsePackage(pkgCache::PkgIterator &Pkg,
1025 pkgCache::VerIterator &Ver)
1026{
1027 bool res = debListParser::UsePackage(Pkg, Ver);
1028 // we use the full file path as a provides so that the file is found
1029 // by its name
5465192b 1030 if(NewProvides(Ver, DebFile, Pkg.Cache()->NativeArch(), Ver.VerStr(), 0) == false)
0d29b9d4
MV
1031 return false;
1032 return res;
1033}
1034
862bafea 1035debListParser::~debListParser() {}