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