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