- initial import of revision 374 from cnc
[apt.git] / apt-pkg / deb / deblistparser.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: deblistparser.cc,v 1.29 2003/09/22 04:16:26 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
19 #include <ctype.h>
20
21 #include <system.h>
22                                                                         /*}}}*/
23
24 static 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 /* */
34 debListParser::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 /* */
42 unsigned long debListParser::UniqFindTagWrite(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 WriteUniqString(Start,Stop - Start);
49 }
50                                                                         /*}}}*/
51 // ListParser::Package - Return the package name                        /*{{{*/
52 // ---------------------------------------------------------------------
53 /* This is to return the name of the package this section describes */
54 string debListParser::Package()
55 {
56    string Result = Section.FindS("Package");
57    if (Result.empty() == true)
58       _error->Error("Encountered a section with no Package: header");
59    return Result;
60 }
61                                                                         /*}}}*/
62 // ListParser::Version - Return the version string                      /*{{{*/
63 // ---------------------------------------------------------------------
64 /* This is to return the string describing the version in debian form,
65    epoch:upstream-release. If this returns the blank string then the 
66    entry is assumed to only describe package properties */
67 string debListParser::Version()
68 {
69    return Section.FindS("Version");
70 }
71                                                                         /*}}}*/
72 // ListParser::NewVersion - Fill in the version structure               /*{{{*/
73 // ---------------------------------------------------------------------
74 /* */
75 bool debListParser::NewVersion(pkgCache::VerIterator Ver)
76 {
77    // Parse the section
78    Ver->Section = UniqFindTagWrite("Section");
79    Ver->Arch = UniqFindTagWrite("Architecture");
80    
81    // Archive Size
82    Ver->Size = (unsigned)Section.FindI("Size");
83    
84    // Unpacked Size (in K)
85    Ver->InstalledSize = (unsigned)Section.FindI("Installed-Size");
86    Ver->InstalledSize *= 1024;
87
88    // Priority
89    const char *Start;
90    const char *Stop;
91    if (Section.Find("Priority",Start,Stop) == true)
92    {      
93       if (GrabWord(string(Start,Stop-Start),PrioList,Ver->Priority) == false)
94          Ver->Priority = pkgCache::State::Extra;
95    }
96
97    if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
98       return false;
99    if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
100       return false;
101    if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
102       return false;
103    if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
104       return false;
105    if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
106       return false;
107    if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false)
108       return false;
109
110    // Obsolete.
111    if (ParseDepends(Ver,"Optional",pkgCache::Dep::Suggests) == false)
112       return false;
113    
114    if (ParseProvides(Ver) == false)
115       return false;
116    
117    return true;
118 }
119                                                                         /*}}}*/
120 // ListParser::UsePackage - Update a package structure                  /*{{{*/
121 // ---------------------------------------------------------------------
122 /* This is called to update the package with any new information 
123    that might be found in the section */
124 bool debListParser::UsePackage(pkgCache::PkgIterator Pkg,
125                                pkgCache::VerIterator Ver)
126 {
127    if (Pkg->Section == 0)
128       Pkg->Section = UniqFindTagWrite("Section");
129    if (Section.FindFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
130       return false;
131    if (Section.FindFlag("Important",Pkg->Flags,pkgCache::Flag::Important) == false)
132       return false;
133
134    if (strcmp(Pkg.Name(),"apt") == 0)
135       Pkg->Flags |= pkgCache::Flag::Important;
136    
137    if (ParseStatus(Pkg,Ver) == false)
138       return false;
139    return true;
140 }
141                                                                         /*}}}*/
142 // ListParser::VersionHash - Compute a unique hash for this version     /*{{{*/
143 // ---------------------------------------------------------------------
144 /* */
145 unsigned short debListParser::VersionHash()
146 {
147    const char *Sections[] ={"Installed-Size",
148                             "Depends",
149                             "Pre-Depends",
150 //                            "Suggests",
151 //                            "Recommends",
152                             "Conflicts",
153                             "Replaces",0};
154    unsigned long Result = INIT_FCS;
155    char S[1024];
156    for (const char **I = Sections; *I != 0; I++)
157    {
158       const char *Start;
159       const char *End;
160       if (Section.Find(*I,Start,End) == false || End - Start >= (signed)sizeof(S))
161          continue;
162       
163       /* Strip out any spaces from the text, this undoes dpkgs reformatting
164          of certain fields. dpkg also has the rather interesting notion of
165          reformatting depends operators < -> <= */
166       char *I = S;
167       for (; Start != End; Start++)
168       {
169          if (isspace(*Start) == 0)
170             *I++ = tolower(*Start);
171          if (*Start == '<' && Start[1] != '<' && Start[1] != '=')
172             *I++ = '=';
173          if (*Start == '>' && Start[1] != '>' && Start[1] != '=')
174             *I++ = '=';
175       }
176
177       Result = AddCRC16(Result,S,I - S);
178    }
179    
180    return Result;
181 }
182                                                                         /*}}}*/
183 // ListParser::ParseStatus - Parse the status field                     /*{{{*/
184 // ---------------------------------------------------------------------
185 /* Status lines are of the form,
186      Status: want flag status
187    want = unknown, install, hold, deinstall, purge
188    flag = ok, reinstreq, hold, hold-reinstreq
189    status = not-installed, unpacked, half-configured,
190             half-installed, config-files, post-inst-failed, 
191             removal-failed, installed
192    
193    Some of the above are obsolete (I think?) flag = hold-* and 
194    status = post-inst-failed, removal-failed at least.
195  */
196 bool debListParser::ParseStatus(pkgCache::PkgIterator Pkg,
197                                 pkgCache::VerIterator Ver)
198 {
199    const char *Start;
200    const char *Stop;
201    if (Section.Find("Status",Start,Stop) == false)
202       return true;
203    
204    // Isolate the first word
205    const char *I = Start;
206    for(; I < Stop && *I != ' '; I++);
207    if (I >= Stop || *I != ' ')
208       return _error->Error("Malformed Status line");
209
210    // Process the want field
211    WordList WantList[] = {{"unknown",pkgCache::State::Unknown},
212                           {"install",pkgCache::State::Install},
213                           {"hold",pkgCache::State::Hold},
214                           {"deinstall",pkgCache::State::DeInstall},
215                           {"purge",pkgCache::State::Purge},
216                           {}};
217    if (GrabWord(string(Start,I-Start),WantList,Pkg->SelectedState) == false)
218       return _error->Error("Malformed 1st word in the Status line");
219
220    // Isloate the next word
221    I++;
222    Start = I;
223    for(; I < Stop && *I != ' '; I++);
224    if (I >= Stop || *I != ' ')
225       return _error->Error("Malformed status line, no 2nd word");
226
227    // Process the flag field
228    WordList FlagList[] = {{"ok",pkgCache::State::Ok},
229                           {"reinstreq",pkgCache::State::ReInstReq},
230                           {"hold",pkgCache::State::HoldInst},
231                           {"hold-reinstreq",pkgCache::State::HoldReInstReq},
232                           {}};
233    if (GrabWord(string(Start,I-Start),FlagList,Pkg->InstState) == false)
234       return _error->Error("Malformed 2nd word in the Status line");
235
236    // Isloate the last word
237    I++;
238    Start = I;
239    for(; I < Stop && *I != ' '; I++);
240    if (I != Stop)
241       return _error->Error("Malformed Status line, no 3rd word");
242
243    // Process the flag field
244    WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled},
245                             {"unpacked",pkgCache::State::UnPacked},
246                             {"half-configured",pkgCache::State::HalfConfigured},
247                             {"installed",pkgCache::State::Installed},
248                             {"half-installed",pkgCache::State::HalfInstalled},
249                             {"config-files",pkgCache::State::ConfigFiles},
250                             {"post-inst-failed",pkgCache::State::HalfConfigured},
251                             {"removal-failed",pkgCache::State::HalfInstalled},
252                             {}};
253    if (GrabWord(string(Start,I-Start),StatusList,Pkg->CurrentState) == false)
254       return _error->Error("Malformed 3rd word in the Status line");
255
256    /* A Status line marks the package as indicating the current
257       version as well. Only if it is actually installed.. Otherwise
258       the interesting dpkg handling of the status file creates bogus 
259       entries. */
260    if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
261          Pkg->CurrentState == pkgCache::State::ConfigFiles))
262    {
263       if (Ver.end() == true)
264          _error->Warning("Encountered status field in a non-version description");
265       else
266          Pkg->CurrentVer = Ver.Index();
267    }
268    
269    return true;
270 }
271
272 const char *debListParser::ConvertRelation(const char *I,unsigned int &Op)
273 {
274    // Determine the operator
275    switch (*I)
276    {
277       case '<':
278       I++;
279       if (*I == '=')
280       {
281          I++;
282          Op = pkgCache::Dep::LessEq;
283          break;
284       }
285       
286       if (*I == '<')
287       {
288          I++;
289          Op = pkgCache::Dep::Less;
290          break;
291       }
292       
293       // < is the same as <= and << is really Cs < for some reason
294       Op = pkgCache::Dep::LessEq;
295       break;
296       
297       case '>':
298       I++;
299       if (*I == '=')
300       {
301          I++;
302          Op = pkgCache::Dep::GreaterEq;
303          break;
304       }
305       
306       if (*I == '>')
307       {
308          I++;
309          Op = pkgCache::Dep::Greater;
310          break;
311       }
312       
313       // > is the same as >= and >> is really Cs > for some reason
314       Op = pkgCache::Dep::GreaterEq;
315       break;
316       
317       case '=':
318       Op = pkgCache::Dep::Equals;
319       I++;
320       break;
321       
322       // HACK around bad package definitions
323       default:
324       Op = pkgCache::Dep::Equals;
325       break;
326    }
327    return I;
328 }
329
330                                                                         /*}}}*/
331 // ListParser::ParseDepends - Parse a dependency element                /*{{{*/
332 // ---------------------------------------------------------------------
333 /* This parses the dependency elements out of a standard string in place,
334    bit by bit. */
335 const char *debListParser::ParseDepends(const char *Start,const char *Stop,
336                                         string &Package,string &Ver,
337                                         unsigned int &Op, bool ParseArchFlags)
338 {
339    // Strip off leading space
340    for (;Start != Stop && isspace(*Start) != 0; Start++);
341    
342    // Parse off the package name
343    const char *I = Start;
344    for (;I != Stop && isspace(*I) == 0 && *I != '(' && *I != ')' &&
345         *I != ',' && *I != '|'; I++);
346    
347    // Malformed, no '('
348    if (I != Stop && *I == ')')
349       return 0;
350
351    if (I == Start)
352       return 0;
353    
354    // Stash the package name
355    Package.assign(Start,I - Start);
356    
357    // Skip white space to the '('
358    for (;I != Stop && isspace(*I) != 0 ; I++);
359    
360    // Parse a version
361    if (I != Stop && *I == '(')
362    {
363       // Skip the '('
364       for (I++; I != Stop && isspace(*I) != 0 ; I++);
365       if (I + 3 >= Stop)
366          return 0;
367       I = ConvertRelation(I,Op);
368       
369       // Skip whitespace
370       for (;I != Stop && isspace(*I) != 0; I++);
371       Start = I;
372       for (;I != Stop && *I != ')'; I++);
373       if (I == Stop || Start == I)
374          return 0;     
375       
376       // Skip trailing whitespace
377       const char *End = I;
378       for (; End > Start && isspace(End[-1]); End--);
379       
380       Ver = string(Start,End-Start);
381       I++;
382    }
383    else
384    {
385       Ver = string();
386       Op = pkgCache::Dep::NoOp;
387    }
388    
389    // Skip whitespace
390    for (;I != Stop && isspace(*I) != 0; I++);
391
392    if (ParseArchFlags == true)
393    {
394       string arch = _config->Find("APT::Architecture");
395
396       // Parse an architecture
397       if (I != Stop && *I == '[')
398       {
399          // malformed
400          I++;
401          if (I == Stop)
402             return 0; 
403          
404          const char *End = I;
405          bool Found = false;
406          bool NegArch = false;
407          while (I != Stop) 
408          {
409             // look for whitespace or ending ']'
410             while (End != Stop && !isspace(*End) && *End != ']') 
411                End++;
412          
413             if (End == Stop) 
414                return 0;
415
416             if (*I == '!')
417             {
418                NegArch = true;
419                I++;
420             }
421
422             if (stringcmp(arch,I,End) == 0)
423                Found = true;
424             
425             if (*End++ == ']') {
426                I = End;
427                break;
428             }
429             
430             I = End;
431             for (;I != Stop && isspace(*I) != 0; I++);
432          }
433
434          if (NegArch)
435             Found = !Found;
436          
437          if (Found == false)
438             Package = ""; /* not for this arch */
439       }
440       
441       // Skip whitespace
442       for (;I != Stop && isspace(*I) != 0; I++);
443    }
444
445    if (I != Stop && *I == '|')
446       Op |= pkgCache::Dep::Or;
447    
448    if (I == Stop || *I == ',' || *I == '|')
449    {
450       if (I != Stop)
451          for (I++; I != Stop && isspace(*I) != 0; I++);
452       return I;
453    }
454    
455    return 0;
456 }
457                                                                         /*}}}*/
458 // ListParser::ParseDepends - Parse a dependency list                   /*{{{*/
459 // ---------------------------------------------------------------------
460 /* This is the higher level depends parser. It takes a tag and generates
461    a complete depends tree for the given version. */
462 bool debListParser::ParseDepends(pkgCache::VerIterator Ver,
463                                  const char *Tag,unsigned int Type)
464 {
465    const char *Start;
466    const char *Stop;
467    if (Section.Find(Tag,Start,Stop) == false)
468       return true;
469    
470    string Package;
471    string Version;
472    unsigned int Op;
473
474    while (1)
475    {
476       Start = ParseDepends(Start,Stop,Package,Version,Op);
477       if (Start == 0)
478          return _error->Error("Problem parsing dependency %s",Tag);
479       
480       if (NewDepends(Ver,Package,Version,Op,Type) == false)
481          return false;
482       if (Start == Stop)
483          break;
484    }
485    return true;
486 }
487                                                                         /*}}}*/
488 // ListParser::ParseProvides - Parse the provides list                  /*{{{*/
489 // ---------------------------------------------------------------------
490 /* */
491 bool debListParser::ParseProvides(pkgCache::VerIterator Ver)
492 {
493    const char *Start;
494    const char *Stop;
495    if (Section.Find("Provides",Start,Stop) == false)
496       return true;
497    
498    string Package;
499    string Version;
500    unsigned int Op;
501
502    while (1)
503    {
504       Start = ParseDepends(Start,Stop,Package,Version,Op);
505       if (Start == 0)
506          return _error->Error("Problem parsing Provides line");
507       if (Op != pkgCache::Dep::NoOp)
508          return _error->Error("Malformed provides line");
509
510       if (NewProvides(Ver,Package,Version) == false)
511          return false;
512
513       if (Start == Stop)
514          break;
515    }
516    
517    return true;
518 }
519                                                                         /*}}}*/
520 // ListParser::GrabWord - Matches a word and returns                    /*{{{*/
521 // ---------------------------------------------------------------------
522 /* Looks for a word in a list of words - for ParseStatus */
523 bool debListParser::GrabWord(string Word,WordList *List,unsigned char &Out)
524 {
525    for (unsigned int C = 0; List[C].Str != 0; C++)
526    {
527       if (strcasecmp(Word.c_str(),List[C].Str) == 0)
528       {
529          Out = List[C].Val;
530          return true;
531       }
532    }
533    return false;
534 }
535                                                                         /*}}}*/
536 // ListParser::Step - Move to the next section in the file              /*{{{*/
537 // ---------------------------------------------------------------------
538 /* This has to be carefull to only process the correct architecture */
539 bool debListParser::Step()
540 {
541    iOffset = Tags.Offset();
542    while (Tags.Step(Section) == true)
543    {      
544       /* See if this is the correct Architecture, if it isn't then we
545          drop the whole section. A missing arch tag only happens (in theory)
546          inside the Status file, so that is a positive return */
547       const char *Start;
548       const char *Stop;
549       if (Section.Find("Architecture",Start,Stop) == false)
550          return true;
551
552       if (stringcmp(Arch,Start,Stop) == 0)
553          return true;
554
555       if (stringcmp(Start,Stop,"all") == 0)
556          return true;
557
558       iOffset = Tags.Offset();
559    }   
560    return false;
561 }
562                                                                         /*}}}*/
563 // ListParser::LoadReleaseInfo - Load the release information           /*{{{*/
564 // ---------------------------------------------------------------------
565 /* */
566 bool debListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
567                                     FileFd &File)
568 {
569    pkgTagFile Tags(&File);
570    pkgTagSection Section;
571    if (Tags.Step(Section) == false)
572       return false;
573
574    const char *Start;
575    const char *Stop;
576    if (Section.Find("Archive",Start,Stop) == true)
577       FileI->Archive = WriteUniqString(Start,Stop - Start);
578    if (Section.Find("Component",Start,Stop) == true)
579       FileI->Component = WriteUniqString(Start,Stop - Start);
580    if (Section.Find("Version",Start,Stop) == true)
581       FileI->Version = WriteUniqString(Start,Stop - Start);
582    if (Section.Find("Origin",Start,Stop) == true)
583       FileI->Origin = WriteUniqString(Start,Stop - Start);
584    if (Section.Find("Label",Start,Stop) == true)
585       FileI->Label = WriteUniqString(Start,Stop - Start);
586    if (Section.Find("Architecture",Start,Stop) == true)
587       FileI->Architecture = WriteUniqString(Start,Stop - Start);
588    
589    if (Section.FindFlag("NotAutomatic",FileI->Flags,
590                         pkgCache::Flag::NotAutomatic) == false)
591       _error->Warning("Bad NotAutomatic flag");
592    
593    return !_error->PendingError();
594 }
595                                                                         /*}}}*/
596 // ListParser::GetPrio - Convert the priority from a string             /*{{{*/
597 // ---------------------------------------------------------------------
598 /* */
599 unsigned char debListParser::GetPrio(string Str)
600 {
601    unsigned char Out;
602    if (GrabWord(Str,PrioList,Out) == false)
603       Out = pkgCache::State::Extra;
604    
605    return Out;
606 }
607                                                                         /*}}}*/