]> git.saurik.com Git - apt.git/blob - apt-pkg/deb/deblistparser.cc
* add "purge" commandline argument, closes: #133421)
[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 {"post-inst-failed",pkgCache::State::HalfConfigured},
297 {"removal-failed",pkgCache::State::HalfInstalled},
298 {}};
299 if (GrabWord(string(Start,I-Start),StatusList,Pkg->CurrentState) == false)
300 return _error->Error("Malformed 3rd word in the Status line");
301
302 /* A Status line marks the package as indicating the current
303 version as well. Only if it is actually installed.. Otherwise
304 the interesting dpkg handling of the status file creates bogus
305 entries. */
306 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
307 Pkg->CurrentState == pkgCache::State::ConfigFiles))
308 {
309 if (Ver.end() == true)
310 _error->Warning("Encountered status field in a non-version description");
311 else
312 Pkg->CurrentVer = Ver.Index();
313 }
314
315 return true;
316 }
317
318 const char *debListParser::ConvertRelation(const char *I,unsigned int &Op)
319 {
320 // Determine the operator
321 switch (*I)
322 {
323 case '<':
324 I++;
325 if (*I == '=')
326 {
327 I++;
328 Op = pkgCache::Dep::LessEq;
329 break;
330 }
331
332 if (*I == '<')
333 {
334 I++;
335 Op = pkgCache::Dep::Less;
336 break;
337 }
338
339 // < is the same as <= and << is really Cs < for some reason
340 Op = pkgCache::Dep::LessEq;
341 break;
342
343 case '>':
344 I++;
345 if (*I == '=')
346 {
347 I++;
348 Op = pkgCache::Dep::GreaterEq;
349 break;
350 }
351
352 if (*I == '>')
353 {
354 I++;
355 Op = pkgCache::Dep::Greater;
356 break;
357 }
358
359 // > is the same as >= and >> is really Cs > for some reason
360 Op = pkgCache::Dep::GreaterEq;
361 break;
362
363 case '=':
364 Op = pkgCache::Dep::Equals;
365 I++;
366 break;
367
368 // HACK around bad package definitions
369 default:
370 Op = pkgCache::Dep::Equals;
371 break;
372 }
373 return I;
374 }
375
376 /*}}}*/
377 // ListParser::ParseDepends - Parse a dependency element /*{{{*/
378 // ---------------------------------------------------------------------
379 /* This parses the dependency elements out of a standard string in place,
380 bit by bit. */
381 const char *debListParser::ParseDepends(const char *Start,const char *Stop,
382 string &Package,string &Ver,
383 unsigned int &Op, bool ParseArchFlags)
384 {
385 // Strip off leading space
386 for (;Start != Stop && isspace(*Start) != 0; Start++);
387
388 // Parse off the package name
389 const char *I = Start;
390 for (;I != Stop && isspace(*I) == 0 && *I != '(' && *I != ')' &&
391 *I != ',' && *I != '|'; I++);
392
393 // Malformed, no '('
394 if (I != Stop && *I == ')')
395 return 0;
396
397 if (I == Start)
398 return 0;
399
400 // Stash the package name
401 Package.assign(Start,I - Start);
402
403 // Skip white space to the '('
404 for (;I != Stop && isspace(*I) != 0 ; I++);
405
406 // Parse a version
407 if (I != Stop && *I == '(')
408 {
409 // Skip the '('
410 for (I++; I != Stop && isspace(*I) != 0 ; I++);
411 if (I + 3 >= Stop)
412 return 0;
413 I = ConvertRelation(I,Op);
414
415 // Skip whitespace
416 for (;I != Stop && isspace(*I) != 0; I++);
417 Start = I;
418 for (;I != Stop && *I != ')'; I++);
419 if (I == Stop || Start == I)
420 return 0;
421
422 // Skip trailing whitespace
423 const char *End = I;
424 for (; End > Start && isspace(End[-1]); End--);
425
426 Ver.assign(Start,End-Start);
427 I++;
428 }
429 else
430 {
431 Ver.clear();
432 Op = pkgCache::Dep::NoOp;
433 }
434
435 // Skip whitespace
436 for (;I != Stop && isspace(*I) != 0; I++);
437
438 if (ParseArchFlags == true)
439 {
440 string arch = _config->Find("APT::Architecture");
441
442 // Parse an architecture
443 if (I != Stop && *I == '[')
444 {
445 // malformed
446 I++;
447 if (I == Stop)
448 return 0;
449
450 const char *End = I;
451 bool Found = false;
452 bool NegArch = false;
453 while (I != Stop)
454 {
455 // look for whitespace or ending ']'
456 while (End != Stop && !isspace(*End) && *End != ']')
457 End++;
458
459 if (End == Stop)
460 return 0;
461
462 if (*I == '!')
463 {
464 NegArch = true;
465 I++;
466 }
467
468 if (stringcmp(arch,I,End) == 0)
469 Found = true;
470
471 if (*End++ == ']') {
472 I = End;
473 break;
474 }
475
476 I = End;
477 for (;I != Stop && isspace(*I) != 0; I++);
478 }
479
480 if (NegArch)
481 Found = !Found;
482
483 if (Found == false)
484 Package = ""; /* not for this arch */
485 }
486
487 // Skip whitespace
488 for (;I != Stop && isspace(*I) != 0; I++);
489 }
490
491 if (I != Stop && *I == '|')
492 Op |= pkgCache::Dep::Or;
493
494 if (I == Stop || *I == ',' || *I == '|')
495 {
496 if (I != Stop)
497 for (I++; I != Stop && isspace(*I) != 0; I++);
498 return I;
499 }
500
501 return 0;
502 }
503 /*}}}*/
504 // ListParser::ParseDepends - Parse a dependency list /*{{{*/
505 // ---------------------------------------------------------------------
506 /* This is the higher level depends parser. It takes a tag and generates
507 a complete depends tree for the given version. */
508 bool debListParser::ParseDepends(pkgCache::VerIterator Ver,
509 const char *Tag,unsigned int Type)
510 {
511 const char *Start;
512 const char *Stop;
513 if (Section.Find(Tag,Start,Stop) == false)
514 return true;
515
516 string Package;
517 string Version;
518 unsigned int Op;
519
520 while (1)
521 {
522 Start = ParseDepends(Start,Stop,Package,Version,Op);
523 if (Start == 0)
524 return _error->Error("Problem parsing dependency %s",Tag);
525
526 if (NewDepends(Ver,Package,Version,Op,Type) == false)
527 return false;
528 if (Start == Stop)
529 break;
530 }
531 return true;
532 }
533 /*}}}*/
534 // ListParser::ParseProvides - Parse the provides list /*{{{*/
535 // ---------------------------------------------------------------------
536 /* */
537 bool debListParser::ParseProvides(pkgCache::VerIterator Ver)
538 {
539 const char *Start;
540 const char *Stop;
541 if (Section.Find("Provides",Start,Stop) == false)
542 return true;
543
544 string Package;
545 string Version;
546 unsigned int Op;
547
548 while (1)
549 {
550 Start = ParseDepends(Start,Stop,Package,Version,Op);
551 if (Start == 0)
552 return _error->Error("Problem parsing Provides line");
553 if (Op != pkgCache::Dep::NoOp) {
554 _error->Warning("Ignoring Provides line with DepCompareOp for package %s", Package.c_str());
555 } else {
556 if (NewProvides(Ver,Package,Version) == false)
557 return false;
558 }
559
560 if (Start == Stop)
561 break;
562 }
563
564 return true;
565 }
566 /*}}}*/
567 // ListParser::GrabWord - Matches a word and returns /*{{{*/
568 // ---------------------------------------------------------------------
569 /* Looks for a word in a list of words - for ParseStatus */
570 bool debListParser::GrabWord(string Word,WordList *List,unsigned char &Out)
571 {
572 for (unsigned int C = 0; List[C].Str != 0; C++)
573 {
574 if (strcasecmp(Word.c_str(),List[C].Str) == 0)
575 {
576 Out = List[C].Val;
577 return true;
578 }
579 }
580 return false;
581 }
582 /*}}}*/
583 // ListParser::Step - Move to the next section in the file /*{{{*/
584 // ---------------------------------------------------------------------
585 /* This has to be carefull to only process the correct architecture */
586 bool debListParser::Step()
587 {
588 iOffset = Tags.Offset();
589 while (Tags.Step(Section) == true)
590 {
591 /* See if this is the correct Architecture, if it isn't then we
592 drop the whole section. A missing arch tag only happens (in theory)
593 inside the Status file, so that is a positive return */
594 const char *Start;
595 const char *Stop;
596 if (Section.Find("Architecture",Start,Stop) == false)
597 return true;
598
599 if (stringcmp(Arch,Start,Stop) == 0)
600 return true;
601
602 if (stringcmp(Start,Stop,"all") == 0)
603 return true;
604
605 iOffset = Tags.Offset();
606 }
607 return false;
608 }
609 /*}}}*/
610 // ListParser::LoadReleaseInfo - Load the release information /*{{{*/
611 // ---------------------------------------------------------------------
612 /* */
613 bool debListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
614 FileFd &File, string component)
615 {
616 pkgTagFile Tags(&File, File.Size() + 256); // XXX
617 pkgTagSection Section;
618 if (Tags.Step(Section) == false)
619 return false;
620
621 //mvo: I don't think we need to fill that in (it's unused since apt-0.6)
622 //FileI->Architecture = WriteUniqString(Arch);
623
624 // apt-secure does no longer download individual (per-section) Release
625 // file. to provide Component pinning we use the section name now
626 FileI->Component = WriteUniqString(component);
627
628 const char *Start;
629 const char *Stop;
630 if (Section.Find("Suite",Start,Stop) == true)
631 FileI->Archive = WriteUniqString(Start,Stop - Start);
632 if (Section.Find("Component",Start,Stop) == true)
633 FileI->Component = WriteUniqString(Start,Stop - Start);
634 if (Section.Find("Version",Start,Stop) == true)
635 FileI->Version = WriteUniqString(Start,Stop - Start);
636 if (Section.Find("Origin",Start,Stop) == true)
637 FileI->Origin = WriteUniqString(Start,Stop - Start);
638 if (Section.Find("Label",Start,Stop) == true)
639 FileI->Label = WriteUniqString(Start,Stop - Start);
640 if (Section.Find("Architecture",Start,Stop) == true)
641 FileI->Architecture = WriteUniqString(Start,Stop - Start);
642
643 if (Section.FindFlag("NotAutomatic",FileI->Flags,
644 pkgCache::Flag::NotAutomatic) == false)
645 _error->Warning("Bad NotAutomatic flag");
646
647 return !_error->PendingError();
648 }
649 /*}}}*/
650 // ListParser::GetPrio - Convert the priority from a string /*{{{*/
651 // ---------------------------------------------------------------------
652 /* */
653 unsigned char debListParser::GetPrio(string Str)
654 {
655 unsigned char Out;
656 if (GrabWord(Str,PrioList,Out) == false)
657 Out = pkgCache::State::Extra;
658
659 return Out;
660 }
661 /*}}}*/