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