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