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