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