]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/deb/deblistparser.cc
Introduce support for the Enhances field. (Closes: #137583)
[apt.git] / apt-pkg / deb / deblistparser.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: deblistparser.cc,v 1.29.2.5 2004/01/06 01:43:44 mdz Exp $
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 /*{{{*/
13#include <apt-pkg/deblistparser.h>
14#include <apt-pkg/error.h>
15#include <apt-pkg/configuration.h>
16#include <apt-pkg/strutl.h>
17#include <apt-pkg/crc-16.h>
18#include <apt-pkg/md5.h>
19
20#include <ctype.h>
21
22#include <system.h>
23 /*}}}*/
24
25static debListParser::WordList PrioList[] = {{"important",pkgCache::State::Important},
26 {"required",pkgCache::State::Required},
27 {"standard",pkgCache::State::Standard},
28 {"optional",pkgCache::State::Optional},
29 {"extra",pkgCache::State::Extra},
30 {}};
31
32// ListParser::debListParser - Constructor /*{{{*/
33// ---------------------------------------------------------------------
34/* */
35debListParser::debListParser(FileFd *File) : Tags(File)
36{
37 Arch = _config->Find("APT::architecture");
38}
39 /*}}}*/
40// ListParser::UniqFindTagWrite - Find the tag and write a unq string /*{{{*/
41// ---------------------------------------------------------------------
42/* */
43unsigned long debListParser::UniqFindTagWrite(const char *Tag)
44{
45 const char *Start;
46 const char *Stop;
47 if (Section.Find(Tag,Start,Stop) == false)
48 return 0;
49 return WriteUniqString(Start,Stop - Start);
50}
51 /*}}}*/
52// ListParser::Package - Return the package name /*{{{*/
53// ---------------------------------------------------------------------
54/* This is to return the name of the package this section describes */
55string debListParser::Package()
56{
57 string Result = Section.FindS("Package");
58 if (Result.empty() == true)
59 _error->Error("Encountered a section with no Package: header");
60 return Result;
61}
62 /*}}}*/
63// ListParser::Version - Return the version string /*{{{*/
64// ---------------------------------------------------------------------
65/* This is to return the string describing the version in debian form,
66 epoch:upstream-release. If this returns the blank string then the
67 entry is assumed to only describe package properties */
68string debListParser::Version()
69{
70 return Section.FindS("Version");
71}
72 /*}}}*/
73// ListParser::NewVersion - Fill in the version structure /*{{{*/
74// ---------------------------------------------------------------------
75/* */
76bool debListParser::NewVersion(pkgCache::VerIterator Ver)
77{
78 // Parse the section
79 Ver->Section = UniqFindTagWrite("Section");
80 Ver->Arch = UniqFindTagWrite("Architecture");
81
82 // Archive Size
83 Ver->Size = (unsigned)Section.FindI("Size");
84
85 // Unpacked Size (in K)
86 Ver->InstalledSize = (unsigned)Section.FindI("Installed-Size");
87 Ver->InstalledSize *= 1024;
88
89 // Priority
90 const char *Start;
91 const char *Stop;
92 if (Section.Find("Priority",Start,Stop) == true)
93 {
94 if (GrabWord(string(Start,Stop-Start),PrioList,Ver->Priority) == false)
95 Ver->Priority = pkgCache::State::Extra;
96 }
97
98 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
99 return false;
100 if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
101 return false;
102 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
103 return false;
104 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
105 return false;
106 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
107 return false;
108 if (ParseDepends(Ver,"Breaks",pkgCache::Dep::DpkgBreaks) == false)
109 return false;
110 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false)
111 return false;
112 if (ParseDepends(Ver,"Enhances",pkgCache::Dep::Enhances) == false)
113 return false;
114
115 // Obsolete.
116 if (ParseDepends(Ver,"Optional",pkgCache::Dep::Suggests) == false)
117 return false;
118
119 if (ParseProvides(Ver) == false)
120 return false;
121
122 return true;
123}
124 /*}}}*/
125// ListParser::Description - Return the description string /*{{{*/
126// ---------------------------------------------------------------------
127/* This is to return the string describing the package in debian
128 form. If this returns the blank string then the entry is assumed to
129 only describe package properties */
130string debListParser::Description()
131{
132 if (DescriptionLanguage().empty())
133 return Section.FindS("Description");
134 else
135 return Section.FindS(("Description-" + pkgIndexFile::LanguageCode()).c_str());
136}
137 /*}}}*/
138// ListParser::DescriptionLanguage - Return the description lang string /*{{{*/
139// ---------------------------------------------------------------------
140/* This is to return the string describing the language of
141 description. If this returns the blank string then the entry is
142 assumed to describe original description. */
143string debListParser::DescriptionLanguage()
144{
145 return Section.FindS("Description").empty() ? pkgIndexFile::LanguageCode() : "";
146}
147 /*}}}*/
148// ListParser::Description - Return the description_md5 MD5SumValue /*{{{*/
149// ---------------------------------------------------------------------
150/* This is to return the md5 string to allow the check if it is the right
151 description. If no Description-md5 is found in the section it will be
152 calculated.
153 */
154MD5SumValue debListParser::Description_md5()
155{
156 string value = Section.FindS("Description-md5");
157
158 if (value.empty())
159 {
160 MD5Summation md5;
161 md5.Add((Description() + "\n").c_str());
162 return md5.Result();
163 } else
164 return MD5SumValue(value);
165}
166 /*}}}*/
167// ListParser::UsePackage - Update a package structure /*{{{*/
168// ---------------------------------------------------------------------
169/* This is called to update the package with any new information
170 that might be found in the section */
171bool debListParser::UsePackage(pkgCache::PkgIterator Pkg,
172 pkgCache::VerIterator Ver)
173{
174 if (Pkg->Section == 0)
175 Pkg->Section = UniqFindTagWrite("Section");
176 if (Section.FindFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
177 return false;
178 if (Section.FindFlag("Important",Pkg->Flags,pkgCache::Flag::Important) == false)
179 return false;
180
181 if (strcmp(Pkg.Name(),"apt") == 0)
182 Pkg->Flags |= pkgCache::Flag::Important;
183
184 if (ParseStatus(Pkg,Ver) == false)
185 return false;
186 return true;
187}
188 /*}}}*/
189// ListParser::VersionHash - Compute a unique hash for this version /*{{{*/
190// ---------------------------------------------------------------------
191/* */
192unsigned short debListParser::VersionHash()
193{
194 const char *Sections[] ={"Installed-Size",
195 "Depends",
196 "Pre-Depends",
197// "Suggests",
198// "Recommends",
199 "Conflicts",
200 "Breaks",
201 "Replaces",0};
202 unsigned long Result = INIT_FCS;
203 char S[1024];
204 for (const char **I = Sections; *I != 0; I++)
205 {
206 const char *Start;
207 const char *End;
208 if (Section.Find(*I,Start,End) == false || End - Start >= (signed)sizeof(S))
209 continue;
210
211 /* Strip out any spaces from the text, this undoes dpkgs reformatting
212 of certain fields. dpkg also has the rather interesting notion of
213 reformatting depends operators < -> <= */
214 char *I = S;
215 for (; Start != End; Start++)
216 {
217 if (isspace(*Start) == 0)
218 *I++ = tolower(*Start);
219 if (*Start == '<' && Start[1] != '<' && Start[1] != '=')
220 *I++ = '=';
221 if (*Start == '>' && Start[1] != '>' && Start[1] != '=')
222 *I++ = '=';
223 }
224
225 Result = AddCRC16(Result,S,I - S);
226 }
227
228 return Result;
229}
230 /*}}}*/
231// ListParser::ParseStatus - Parse the status field /*{{{*/
232// ---------------------------------------------------------------------
233/* Status lines are of the form,
234 Status: want flag status
235 want = unknown, install, hold, deinstall, purge
236 flag = ok, reinstreq, hold, hold-reinstreq
237 status = not-installed, unpacked, half-configured,
238 half-installed, config-files, post-inst-failed,
239 removal-failed, installed
240
241 Some of the above are obsolete (I think?) flag = hold-* and
242 status = post-inst-failed, removal-failed at least.
243 */
244bool debListParser::ParseStatus(pkgCache::PkgIterator Pkg,
245 pkgCache::VerIterator Ver)
246{
247 const char *Start;
248 const char *Stop;
249 if (Section.Find("Status",Start,Stop) == false)
250 return true;
251
252 // Isolate the first word
253 const char *I = Start;
254 for(; I < Stop && *I != ' '; I++);
255 if (I >= Stop || *I != ' ')
256 return _error->Error("Malformed Status line");
257
258 // Process the want field
259 WordList WantList[] = {{"unknown",pkgCache::State::Unknown},
260 {"install",pkgCache::State::Install},
261 {"hold",pkgCache::State::Hold},
262 {"deinstall",pkgCache::State::DeInstall},
263 {"purge",pkgCache::State::Purge},
264 {}};
265 if (GrabWord(string(Start,I-Start),WantList,Pkg->SelectedState) == false)
266 return _error->Error("Malformed 1st word in the Status line");
267
268 // Isloate the next word
269 I++;
270 Start = I;
271 for(; I < Stop && *I != ' '; I++);
272 if (I >= Stop || *I != ' ')
273 return _error->Error("Malformed status line, no 2nd word");
274
275 // Process the flag field
276 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
277 {"reinstreq",pkgCache::State::ReInstReq},
278 {"hold",pkgCache::State::HoldInst},
279 {"hold-reinstreq",pkgCache::State::HoldReInstReq},
280 {}};
281 if (GrabWord(string(Start,I-Start),FlagList,Pkg->InstState) == false)
282 return _error->Error("Malformed 2nd word in the Status line");
283
284 // Isloate the last word
285 I++;
286 Start = I;
287 for(; I < Stop && *I != ' '; I++);
288 if (I != Stop)
289 return _error->Error("Malformed Status line, no 3rd word");
290
291 // Process the flag field
292 WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled},
293 {"unpacked",pkgCache::State::UnPacked},
294 {"half-configured",pkgCache::State::HalfConfigured},
295 {"installed",pkgCache::State::Installed},
296 {"half-installed",pkgCache::State::HalfInstalled},
297 {"config-files",pkgCache::State::ConfigFiles},
298 {"triggers-awaited",pkgCache::State::TriggersAwaited},
299 {"triggers-pending",pkgCache::State::TriggersPending},
300 {"post-inst-failed",pkgCache::State::HalfConfigured},
301 {"removal-failed",pkgCache::State::HalfInstalled},
302 {}};
303 if (GrabWord(string(Start,I-Start),StatusList,Pkg->CurrentState) == false)
304 return _error->Error("Malformed 3rd word in the Status line");
305
306 /* A Status line marks the package as indicating the current
307 version as well. Only if it is actually installed.. Otherwise
308 the interesting dpkg handling of the status file creates bogus
309 entries. */
310 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
311 Pkg->CurrentState == pkgCache::State::ConfigFiles))
312 {
313 if (Ver.end() == true)
314 _error->Warning("Encountered status field in a non-version description");
315 else
316 Pkg->CurrentVer = Ver.Index();
317 }
318
319 return true;
320}
321
322const char *debListParser::ConvertRelation(const char *I,unsigned int &Op)
323{
324 // Determine the operator
325 switch (*I)
326 {
327 case '<':
328 I++;
329 if (*I == '=')
330 {
331 I++;
332 Op = pkgCache::Dep::LessEq;
333 break;
334 }
335
336 if (*I == '<')
337 {
338 I++;
339 Op = pkgCache::Dep::Less;
340 break;
341 }
342
343 // < is the same as <= and << is really Cs < for some reason
344 Op = pkgCache::Dep::LessEq;
345 break;
346
347 case '>':
348 I++;
349 if (*I == '=')
350 {
351 I++;
352 Op = pkgCache::Dep::GreaterEq;
353 break;
354 }
355
356 if (*I == '>')
357 {
358 I++;
359 Op = pkgCache::Dep::Greater;
360 break;
361 }
362
363 // > is the same as >= and >> is really Cs > for some reason
364 Op = pkgCache::Dep::GreaterEq;
365 break;
366
367 case '=':
368 Op = pkgCache::Dep::Equals;
369 I++;
370 break;
371
372 // HACK around bad package definitions
373 default:
374 Op = pkgCache::Dep::Equals;
375 break;
376 }
377 return I;
378}
379
380 /*}}}*/
381// ListParser::ParseDepends - Parse a dependency element /*{{{*/
382// ---------------------------------------------------------------------
383/* This parses the dependency elements out of a standard string in place,
384 bit by bit. */
385const char *debListParser::ParseDepends(const char *Start,const char *Stop,
386 string &Package,string &Ver,
387 unsigned int &Op, bool ParseArchFlags)
388{
389 // Strip off leading space
390 for (;Start != Stop && isspace(*Start) != 0; Start++);
391
392 // Parse off the package name
393 const char *I = Start;
394 for (;I != Stop && isspace(*I) == 0 && *I != '(' && *I != ')' &&
395 *I != ',' && *I != '|'; I++);
396
397 // Malformed, no '('
398 if (I != Stop && *I == ')')
399 return 0;
400
401 if (I == Start)
402 return 0;
403
404 // Stash the package name
405 Package.assign(Start,I - Start);
406
407 // Skip white space to the '('
408 for (;I != Stop && isspace(*I) != 0 ; I++);
409
410 // Parse a version
411 if (I != Stop && *I == '(')
412 {
413 // Skip the '('
414 for (I++; I != Stop && isspace(*I) != 0 ; I++);
415 if (I + 3 >= Stop)
416 return 0;
417 I = ConvertRelation(I,Op);
418
419 // Skip whitespace
420 for (;I != Stop && isspace(*I) != 0; I++);
421 Start = I;
422 for (;I != Stop && *I != ')'; I++);
423 if (I == Stop || Start == I)
424 return 0;
425
426 // Skip trailing whitespace
427 const char *End = I;
428 for (; End > Start && isspace(End[-1]); End--);
429
430 Ver.assign(Start,End-Start);
431 I++;
432 }
433 else
434 {
435 Ver.clear();
436 Op = pkgCache::Dep::NoOp;
437 }
438
439 // Skip whitespace
440 for (;I != Stop && isspace(*I) != 0; I++);
441
442 if (ParseArchFlags == true)
443 {
444 string arch = _config->Find("APT::Architecture");
445
446 // Parse an architecture
447 if (I != Stop && *I == '[')
448 {
449 // malformed
450 I++;
451 if (I == Stop)
452 return 0;
453
454 const char *End = I;
455 bool Found = false;
456 bool NegArch = false;
457 while (I != Stop)
458 {
459 // look for whitespace or ending ']'
460 while (End != Stop && !isspace(*End) && *End != ']')
461 End++;
462
463 if (End == Stop)
464 return 0;
465
466 if (*I == '!')
467 {
468 NegArch = true;
469 I++;
470 }
471
472 if (stringcmp(arch,I,End) == 0)
473 Found = true;
474
475 if (*End++ == ']') {
476 I = End;
477 break;
478 }
479
480 I = End;
481 for (;I != Stop && isspace(*I) != 0; I++);
482 }
483
484 if (NegArch)
485 Found = !Found;
486
487 if (Found == false)
488 Package = ""; /* not for this arch */
489 }
490
491 // Skip whitespace
492 for (;I != Stop && isspace(*I) != 0; I++);
493 }
494
495 if (I != Stop && *I == '|')
496 Op |= pkgCache::Dep::Or;
497
498 if (I == Stop || *I == ',' || *I == '|')
499 {
500 if (I != Stop)
501 for (I++; I != Stop && isspace(*I) != 0; I++);
502 return I;
503 }
504
505 return 0;
506}
507 /*}}}*/
508// ListParser::ParseDepends - Parse a dependency list /*{{{*/
509// ---------------------------------------------------------------------
510/* This is the higher level depends parser. It takes a tag and generates
511 a complete depends tree for the given version. */
512bool debListParser::ParseDepends(pkgCache::VerIterator Ver,
513 const char *Tag,unsigned int Type)
514{
515 const char *Start;
516 const char *Stop;
517 if (Section.Find(Tag,Start,Stop) == false)
518 return true;
519
520 string Package;
521 string Version;
522 unsigned int Op;
523
524 while (1)
525 {
526 Start = ParseDepends(Start,Stop,Package,Version,Op);
527 if (Start == 0)
528 return _error->Error("Problem parsing dependency %s",Tag);
529
530 if (NewDepends(Ver,Package,Version,Op,Type) == false)
531 return false;
532 if (Start == Stop)
533 break;
534 }
535 return true;
536}
537 /*}}}*/
538// ListParser::ParseProvides - Parse the provides list /*{{{*/
539// ---------------------------------------------------------------------
540/* */
541bool debListParser::ParseProvides(pkgCache::VerIterator Ver)
542{
543 const char *Start;
544 const char *Stop;
545 if (Section.Find("Provides",Start,Stop) == false)
546 return true;
547
548 string Package;
549 string Version;
550 unsigned int Op;
551
552 while (1)
553 {
554 Start = ParseDepends(Start,Stop,Package,Version,Op);
555 if (Start == 0)
556 return _error->Error("Problem parsing Provides line");
557 if (Op != pkgCache::Dep::NoOp) {
558 _error->Warning("Ignoring Provides line with DepCompareOp for package %s", Package.c_str());
559 } else {
560 if (NewProvides(Ver,Package,Version) == false)
561 return false;
562 }
563
564 if (Start == Stop)
565 break;
566 }
567
568 return true;
569}
570 /*}}}*/
571// ListParser::GrabWord - Matches a word and returns /*{{{*/
572// ---------------------------------------------------------------------
573/* Looks for a word in a list of words - for ParseStatus */
574bool debListParser::GrabWord(string Word,WordList *List,unsigned char &Out)
575{
576 for (unsigned int C = 0; List[C].Str != 0; C++)
577 {
578 if (strcasecmp(Word.c_str(),List[C].Str) == 0)
579 {
580 Out = List[C].Val;
581 return true;
582 }
583 }
584 return false;
585}
586 /*}}}*/
587// ListParser::Step - Move to the next section in the file /*{{{*/
588// ---------------------------------------------------------------------
589/* This has to be carefull to only process the correct architecture */
590bool debListParser::Step()
591{
592 iOffset = Tags.Offset();
593 while (Tags.Step(Section) == true)
594 {
595 /* See if this is the correct Architecture, if it isn't then we
596 drop the whole section. A missing arch tag only happens (in theory)
597 inside the Status file, so that is a positive return */
598 const char *Start;
599 const char *Stop;
600 if (Section.Find("Architecture",Start,Stop) == false)
601 return true;
602
603 if (stringcmp(Arch,Start,Stop) == 0)
604 return true;
605
606 if (stringcmp(Start,Stop,"all") == 0)
607 return true;
608
609 iOffset = Tags.Offset();
610 }
611 return false;
612}
613 /*}}}*/
614// ListParser::LoadReleaseInfo - Load the release information /*{{{*/
615// ---------------------------------------------------------------------
616/* */
617bool debListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
618 FileFd &File, string component)
619{
620 pkgTagFile Tags(&File, File.Size() + 256); // XXX
621 pkgTagSection Section;
622 if (Tags.Step(Section) == false)
623 return false;
624
625 //mvo: I don't think we need to fill that in (it's unused since apt-0.6)
626 //FileI->Architecture = WriteUniqString(Arch);
627
628 // apt-secure does no longer download individual (per-section) Release
629 // file. to provide Component pinning we use the section name now
630 FileI->Component = WriteUniqString(component);
631
632 const char *Start;
633 const char *Stop;
634 if (Section.Find("Suite",Start,Stop) == true)
635 FileI->Archive = WriteUniqString(Start,Stop - Start);
636 if (Section.Find("Component",Start,Stop) == true)
637 FileI->Component = WriteUniqString(Start,Stop - Start);
638 if (Section.Find("Version",Start,Stop) == true)
639 FileI->Version = WriteUniqString(Start,Stop - Start);
640 if (Section.Find("Origin",Start,Stop) == true)
641 FileI->Origin = WriteUniqString(Start,Stop - Start);
642 if (Section.Find("Label",Start,Stop) == true)
643 FileI->Label = WriteUniqString(Start,Stop - Start);
644 if (Section.Find("Architecture",Start,Stop) == true)
645 FileI->Architecture = WriteUniqString(Start,Stop - Start);
646
647 if (Section.FindFlag("NotAutomatic",FileI->Flags,
648 pkgCache::Flag::NotAutomatic) == false)
649 _error->Warning("Bad NotAutomatic flag");
650
651 return !_error->PendingError();
652}
653 /*}}}*/
654// ListParser::GetPrio - Convert the priority from a string /*{{{*/
655// ---------------------------------------------------------------------
656/* */
657unsigned char debListParser::GetPrio(string Str)
658{
659 unsigned char Out;
660 if (GrabWord(Str,PrioList,Out) == false)
661 Out = pkgCache::State::Extra;
662
663 return Out;
664}
665 /*}}}*/