Sync with apt--main--0--patch-76
[ntk/apt.git] / apt-pkg / deb / deblistparser.cc
CommitLineData
f55a958f
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7db98ffc 3// $Id: deblistparser.cc,v 1.29.2.5 2004/01/06 01:43:44 mdz Exp $
f55a958f
AL
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 /*{{{*/
094a497d
AL
13#include <apt-pkg/deblistparser.h>
14#include <apt-pkg/error.h>
15#include <apt-pkg/configuration.h>
cdcc6d34 16#include <apt-pkg/strutl.h>
204fbdcc 17#include <apt-pkg/crc-16.h>
a52f938b 18#include <apt-pkg/md5.h>
9c14e3d6 19
e7b470ee
AL
20#include <ctype.h>
21
f55a958f
AL
22#include <system.h>
23 /*}}}*/
24
b2e465d6
AL
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
f55a958f
AL
32// ListParser::debListParser - Constructor /*{{{*/
33// ---------------------------------------------------------------------
34/* */
b2e465d6 35debListParser::debListParser(FileFd *File) : Tags(File)
f55a958f 36{
ddc1d8d0 37 Arch = _config->Find("APT::architecture");
0149949b
AL
38}
39 /*}}}*/
f55a958f
AL
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 /*}}}*/
f55a958f
AL
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{
b0b4efb9 57 string Result = Section.FindS("Package");
f55a958f 58 if (Result.empty() == true)
65a1e968 59 _error->Error("Encountered a section with no Package: header");
f55a958f
AL
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{
b0b4efb9 70 return Section.FindS("Version");
f55a958f
AL
71}
72 /*}}}*/
f55a958f
AL
73// ListParser::NewVersion - Fill in the version structure /*{{{*/
74// ---------------------------------------------------------------------
75/* */
76bool debListParser::NewVersion(pkgCache::VerIterator Ver)
0149949b
AL
77{
78 // Parse the section
b35d2f5f 79 Ver->Section = UniqFindTagWrite("Section");
17caf1b1 80 Ver->Arch = UniqFindTagWrite("Architecture");
0149949b
AL
81
82 // Archive Size
b0b4efb9 83 Ver->Size = (unsigned)Section.FindI("Size");
0149949b
AL
84
85 // Unpacked Size (in K)
b0b4efb9 86 Ver->InstalledSize = (unsigned)Section.FindI("Installed-Size");
0149949b
AL
87 Ver->InstalledSize *= 1024;
88
89 // Priority
90 const char *Start;
91 const char *Stop;
92 if (Section.Find("Priority",Start,Stop) == true)
b2e465d6
AL
93 {
94 if (GrabWord(string(Start,Stop-Start),PrioList,Ver->Priority) == false)
421c8d10 95 Ver->Priority = pkgCache::State::Extra;
0149949b 96 }
dcb79bae 97
6c139d6e 98 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
dcb79bae 99 return false;
8efa2a3b 100 if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
dcb79bae 101 return false;
6c139d6e 102 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
dcb79bae 103 return false;
6c139d6e 104 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
dcb79bae 105 return false;
6c139d6e 106 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
dcb79bae 107 return false;
f55ece0e 108 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false)
dcb79bae
AL
109 return false;
110
b2e465d6
AL
111 // Obsolete.
112 if (ParseDepends(Ver,"Optional",pkgCache::Dep::Suggests) == false)
113 return false;
114
dcb79bae
AL
115 if (ParseProvides(Ver) == false)
116 return false;
0149949b 117
f55a958f
AL
118 return true;
119}
120 /*}}}*/
a52f938b
OS
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 */
126string 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. */
139string 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. */
149MD5SumValue 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 /*}}}*/
f55a958f
AL
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 */
165bool debListParser::UsePackage(pkgCache::PkgIterator Pkg,
166 pkgCache::VerIterator Ver)
167{
168 if (Pkg->Section == 0)
b35d2f5f 169 Pkg->Section = UniqFindTagWrite("Section");
b0b4efb9 170 if (Section.FindFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
f55a958f 171 return false;
138d4b3d 172 if (Section.FindFlag("Important",Pkg->Flags,pkgCache::Flag::Important) == false)
f55a958f 173 return false;
138d4b3d
AL
174
175 if (strcmp(Pkg.Name(),"apt") == 0)
176 Pkg->Flags |= pkgCache::Flag::Important;
177
f55a958f
AL
178 if (ParseStatus(Pkg,Ver) == false)
179 return false;
180 return true;
181}
182 /*}}}*/
204fbdcc
AL
183// ListParser::VersionHash - Compute a unique hash for this version /*{{{*/
184// ---------------------------------------------------------------------
185/* */
186unsigned short debListParser::VersionHash()
187{
188 const char *Sections[] ={"Installed-Size",
189 "Depends",
190 "Pre-Depends",
f78439bf
AL
191// "Suggests",
192// "Recommends",
204fbdcc
AL
193 "Conflicts",
194 "Replaces",0};
195 unsigned long Result = INIT_FCS;
418a471f 196 char S[1024];
204fbdcc
AL
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
421c8d10
AL
205 of certain fields. dpkg also has the rather interesting notion of
206 reformatting depends operators < -> <= */
204fbdcc
AL
207 char *I = S;
208 for (; Start != End; Start++)
421c8d10 209 {
204fbdcc 210 if (isspace(*Start) == 0)
ddc1d8d0 211 *I++ = tolower(*Start);
421c8d10
AL
212 if (*Start == '<' && Start[1] != '<' && Start[1] != '=')
213 *I++ = '=';
214 if (*Start == '>' && Start[1] != '>' && Start[1] != '=')
215 *I++ = '=';
216 }
418a471f 217
204fbdcc
AL
218 Result = AddCRC16(Result,S,I - S);
219 }
220
221 return Result;
222}
223 /*}}}*/
dcb79bae 224// ListParser::ParseStatus - Parse the status field /*{{{*/
f55a958f
AL
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
a005475e 230 status = not-installed, unpacked, half-configured,
f55a958f
AL
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 */
237bool 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
6c139d6e
AL
252 WordList WantList[] = {{"unknown",pkgCache::State::Unknown},
253 {"install",pkgCache::State::Install},
254 {"hold",pkgCache::State::Hold},
255 {"deinstall",pkgCache::State::DeInstall},
b2e465d6
AL
256 {"purge",pkgCache::State::Purge},
257 {}};
258 if (GrabWord(string(Start,I-Start),WantList,Pkg->SelectedState) == false)
f55a958f
AL
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
6c139d6e
AL
269 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
270 {"reinstreq",pkgCache::State::ReInstReq},
271 {"hold",pkgCache::State::HoldInst},
b2e465d6
AL
272 {"hold-reinstreq",pkgCache::State::HoldReInstReq},
273 {}};
274 if (GrabWord(string(Start,I-Start),FlagList,Pkg->InstState) == false)
f55a958f
AL
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
6c139d6e
AL
285 WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled},
286 {"unpacked",pkgCache::State::UnPacked},
287 {"half-configured",pkgCache::State::HalfConfigured},
288 {"installed",pkgCache::State::Installed},
6c139d6e
AL
289 {"half-installed",pkgCache::State::HalfInstalled},
290 {"config-files",pkgCache::State::ConfigFiles},
291 {"post-inst-failed",pkgCache::State::HalfConfigured},
b2e465d6
AL
292 {"removal-failed",pkgCache::State::HalfInstalled},
293 {}};
294 if (GrabWord(string(Start,I-Start),StatusList,Pkg->CurrentState) == false)
f55a958f
AL
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. */
6c139d6e
AL
301 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
302 Pkg->CurrentState == pkgCache::State::ConfigFiles))
f55a958f
AL
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
dcb79bae
AL
310 return true;
311}
a1826878 312
b2e465d6
AL
313const 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
a1826878
AL
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. */
dcb79bae
AL
376const char *debListParser::ParseDepends(const char *Start,const char *Stop,
377 string &Package,string &Ver,
b2e465d6 378 unsigned int &Op, bool ParseArchFlags)
dcb79bae
AL
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;
b2e465d6 408 I = ConvertRelation(I,Op);
dcb79bae
AL
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
02f000a9
AL
417 // Skip trailing whitespace
418 const char *End = I;
419 for (; End > Start && isspace(End[-1]); End--);
420
421 Ver = string(Start,End-Start);
dcb79bae
AL
422 I++;
423 }
424 else
425 {
426 Ver = string();
6c139d6e 427 Op = pkgCache::Dep::NoOp;
dcb79bae
AL
428 }
429
430 // Skip whitespace
431 for (;I != Stop && isspace(*I) != 0; I++);
b2e465d6
AL
432
433 if (ParseArchFlags == true)
434 {
435 string arch = _config->Find("APT::Architecture");
9e10ad8a 436
b2e465d6
AL
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;
9e10ad8a 447 bool NegArch = false;
b2e465d6
AL
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;
9e10ad8a
AL
456
457 if (*I == '!')
458 {
459 NegArch = true;
460 I++;
461 }
462
3e18cc75 463 if (stringcmp(arch,I,End) == 0)
b2e465d6
AL
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 }
9e10ad8a
AL
474
475 if (NegArch)
476 Found = !Found;
b2e465d6 477
9e10ad8a 478 if (Found == false)
b2e465d6
AL
479 Package = ""; /* not for this arch */
480 }
481
482 // Skip whitespace
483 for (;I != Stop && isspace(*I) != 0; I++);
484 }
485
dcb79bae 486 if (I != Stop && *I == '|')
6c139d6e 487 Op |= pkgCache::Dep::Or;
dcb79bae
AL
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. */
503bool 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
8efa2a3b 515 while (1)
dcb79bae 516 {
8efa2a3b 517 Start = ParseDepends(Start,Stop,Package,Version,Op);
dcb79bae
AL
518 if (Start == 0)
519 return _error->Error("Problem parsing dependency %s",Tag);
8efa2a3b 520
dcb79bae
AL
521 if (NewDepends(Ver,Package,Version,Op,Type) == false)
522 return false;
8efa2a3b
AL
523 if (Start == Stop)
524 break;
dcb79bae
AL
525 }
526 return true;
527}
528 /*}}}*/
529// ListParser::ParseProvides - Parse the provides list /*{{{*/
530// ---------------------------------------------------------------------
531/* */
532bool 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");
6c139d6e 548 if (Op != pkgCache::Dep::NoOp)
dcb79bae
AL
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
f55a958f
AL
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 */
b2e465d6 564bool debListParser::GrabWord(string Word,WordList *List,unsigned char &Out)
f55a958f 565{
b2e465d6 566 for (unsigned int C = 0; List[C].Str != 0; C++)
f55a958f
AL
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// ---------------------------------------------------------------------
0149949b 579/* This has to be carefull to only process the correct architecture */
f55a958f
AL
580bool debListParser::Step()
581{
dcb79bae 582 iOffset = Tags.Offset();
0149949b 583 while (Tags.Step(Section) == true)
ddc1d8d0
AL
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 */
0149949b
AL
588 const char *Start;
589 const char *Stop;
590 if (Section.Find("Architecture",Start,Stop) == false)
591 return true;
9c14e3d6 592
3e18cc75 593 if (stringcmp(Arch,Start,Stop) == 0)
0149949b
AL
594 return true;
595
9c14e3d6 596 if (stringcmp(Start,Stop,"all") == 0)
0149949b 597 return true;
dcb79bae
AL
598
599 iOffset = Tags.Offset();
0149949b
AL
600 }
601 return false;
f55a958f
AL
602}
603 /*}}}*/
b0b4efb9
AL
604// ListParser::LoadReleaseInfo - Load the release information /*{{{*/
605// ---------------------------------------------------------------------
606/* */
607bool debListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
608 FileFd &File)
609{
7db98ffc 610 pkgTagFile Tags(&File, File.Size() + 256); // XXX
b0b4efb9
AL
611 pkgTagSection Section;
612 if (Tags.Step(Section) == false)
613 return false;
614
615 const char *Start;
616 const char *Stop;
7db98ffc 617 if (Section.Find("Suite",Start,Stop) == true)
b0b4efb9
AL
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);
0dbb95d8 629
3c124dde
AL
630 if (Section.FindFlag("NotAutomatic",FileI->Flags,
631 pkgCache::Flag::NotAutomatic) == false)
0dbb95d8 632 _error->Warning("Bad NotAutomatic flag");
0dbb95d8 633
b0b4efb9
AL
634 return !_error->PendingError();
635}
636 /*}}}*/
b2e465d6
AL
637// ListParser::GetPrio - Convert the priority from a string /*{{{*/
638// ---------------------------------------------------------------------
639/* */
640unsigned 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 /*}}}*/