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