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