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