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