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