]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/deb/deblistparser.cc
DumpAvail works and apt-cache is complete
[apt.git] / apt-pkg / deb / deblistparser.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: deblistparser.cc,v 1.7 1998/07/16 06:08:42 jgg 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 <strutl.h>
17
18#include <system.h>
19 /*}}}*/
20
21// ListParser::debListParser - Constructor /*{{{*/
22// ---------------------------------------------------------------------
23/* */
24debListParser::debListParser(File &File) : Tags(File)
25{
26}
27 /*}}}*/
28// ListParser::FindTag - Find the tag and return a string /*{{{*/
29// ---------------------------------------------------------------------
30/* */
31string debListParser::FindTag(const char *Tag)
32{
33 const char *Start;
34 const char *Stop;
35 if (Section.Find(Tag,Start,Stop) == false)
36 return string();
37 return string(Start,Stop - Start);
38}
39 /*}}}*/
40// ListParser::FindTagI - Find the tag and return an int /*{{{*/
41// ---------------------------------------------------------------------
42/* */
43signed long debListParser::FindTagI(const char *Tag,signed long Default)
44{
45 const char *Start;
46 const char *Stop;
47 if (Section.Find(Tag,Start,Stop) == false)
48 return Default;
49
50 // Copy it into a temp buffer so we can use strtol
51 char S[300];
52 if ((unsigned)(Stop - Start) >= sizeof(S))
53 return Default;
54 strncpy(S,Start,Stop-Start);
55 S[Stop - Start] = 0;
56
57 char *End;
58 signed long Result = strtol(S,&End,10);
59 if (S == End)
60 return Default;
61 return Result;
62}
63 /*}}}*/
64// ListParser::UniqFindTagWrite - Find the tag and write a unq string /*{{{*/
65// ---------------------------------------------------------------------
66/* */
67unsigned long debListParser::UniqFindTagWrite(const char *Tag)
68{
69 const char *Start;
70 const char *Stop;
71 if (Section.Find(Tag,Start,Stop) == false)
72 return 0;
73 return WriteUniqString(Start,Stop - Start);
74}
75 /*}}}*/
76// ListParser::HandleFlag - Sets a flag variable based on a tag /*{{{*/
77// ---------------------------------------------------------------------
78/* This checks the tag for true/false yes/no etc */
79bool debListParser::HandleFlag(const char *Tag,unsigned long &Flags,
80 unsigned long Flag)
81{
82 const char *Start;
83 const char *Stop;
84 if (Section.Find(Tag,Start,Stop) == false)
85 return true;
86
87 int Set = 2;
88 if (stringcasecmp(Start,Stop,"yes") == 0)
89 Set = 1;
90 if (stringcasecmp(Start,Stop,"true") == 0)
91 Set = 1;
92 if (stringcasecmp(Start,Stop,"no") == 0)
93 Set = 0;
94 if (stringcasecmp(Start,Stop,"false") == 0)
95 Set = 0;
96 if (Set == 2)
97 {
98 _error->Warning("Unknown flag value");
99 return true;
100 }
101
102 if (Set == 0)
103 Flags &= ~Flag;
104 if (Set == 1)
105 Flags |= Flag;
106 return true;
107}
108 /*}}}*/
109// ListParser::Package - Return the package name /*{{{*/
110// ---------------------------------------------------------------------
111/* This is to return the name of the package this section describes */
112string debListParser::Package()
113{
114 string Result = FindTag("Package");
115 if (Result.empty() == true)
116 _error->Error("Encoutered a section with no Package: header");
117 return Result;
118}
119 /*}}}*/
120// ListParser::Version - Return the version string /*{{{*/
121// ---------------------------------------------------------------------
122/* This is to return the string describing the version in debian form,
123 epoch:upstream-release. If this returns the blank string then the
124 entry is assumed to only describe package properties */
125string debListParser::Version()
126{
127 return FindTag("Version");
128}
129 /*}}}*/
130// ListParser::NewVersion - Fill in the version structure /*{{{*/
131// ---------------------------------------------------------------------
132/* */
133bool debListParser::NewVersion(pkgCache::VerIterator Ver)
134{
135 // Parse the section
136 if ((Ver->Section = UniqFindTagWrite("Section")) == 0)
137 return _error->Warning("Missing Section tag");
138
139 // Archive Size
140 if ((Ver->Size = (unsigned)FindTagI("Size")) == 0)
141 return _error->Error("Unparsable Size field");
142
143 // Unpacked Size (in K)
144 if ((Ver->InstalledSize = (unsigned)FindTagI("Installed-Size")) == 0)
145 return _error->Error("Unparsable Installed-Size field");
146 Ver->InstalledSize *= 1024;
147
148 // Priority
149 const char *Start;
150 const char *Stop;
151 if (Section.Find("Priority",Start,Stop) == true)
152 {
153 WordList PrioList[] = {{"important",pkgCache::State::Important},
154 {"required",pkgCache::State::Required},
155 {"standard",pkgCache::State::Standard},
156 {"optional",pkgCache::State::Optional},
157 {"extra",pkgCache::State::Extra}};
158 if (GrabWord(string(Start,Stop-Start),PrioList,
159 _count(PrioList),Ver->Priority) == false)
160 return _error->Error("Malformed Priority line");
161 }
162
163 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
164 return false;
165 if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
166 return false;
167 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
168 return false;
169 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
170 return false;
171 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
172 return false;
173 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Depends) == false)
174 return false;
175
176 if (ParseProvides(Ver) == false)
177 return false;
178
179 return true;
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->Section == 0)
190 if ((Pkg->Section = UniqFindTagWrite("Section")) == 0)
191 return false;
192 if (HandleFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
193 return false;
194 if (HandleFlag("Immediate-Configure",Pkg->Flags,pkgCache::Flag::ImmediateConf) == false)
195 return false;
196 if (ParseStatus(Pkg,Ver) == false)
197 return false;
198 return true;
199}
200 /*}}}*/
201// ListParser::ParseStatus - Parse the status field /*{{{*/
202// ---------------------------------------------------------------------
203/* Status lines are of the form,
204 Status: want flag status
205 want = unknown, install, hold, deinstall, purge
206 flag = ok, reinstreq, hold, hold-reinstreq
207 status = not-installed, unpacked, half-configured, uninstalled,
208 half-installed, config-files, post-inst-failed,
209 removal-failed, installed
210
211 Some of the above are obsolete (I think?) flag = hold-* and
212 status = post-inst-failed, removal-failed at least.
213 */
214bool debListParser::ParseStatus(pkgCache::PkgIterator Pkg,
215 pkgCache::VerIterator Ver)
216{
217 const char *Start;
218 const char *Stop;
219 if (Section.Find("Status",Start,Stop) == false)
220 return true;
221
222 // Isolate the first word
223 const char *I = Start;
224 for(; I < Stop && *I != ' '; I++);
225 if (I >= Stop || *I != ' ')
226 return _error->Error("Malformed Status line");
227
228 // Process the want field
229 WordList WantList[] = {{"unknown",pkgCache::State::Unknown},
230 {"install",pkgCache::State::Install},
231 {"hold",pkgCache::State::Hold},
232 {"deinstall",pkgCache::State::DeInstall},
233 {"purge",pkgCache::State::Purge}};
234 if (GrabWord(string(Start,I-Start),WantList,
235 _count(WantList),Pkg->SelectedState) == false)
236 return _error->Error("Malformed 1st word in the Status line");
237
238 // Isloate the next word
239 I++;
240 Start = I;
241 for(; I < Stop && *I != ' '; I++);
242 if (I >= Stop || *I != ' ')
243 return _error->Error("Malformed status line, no 2nd word");
244
245 // Process the flag field
246 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
247 {"reinstreq",pkgCache::State::ReInstReq},
248 {"hold",pkgCache::State::HoldInst},
249 {"hold-reinstreq",pkgCache::State::HoldReInstReq}};
250 if (GrabWord(string(Start,I-Start),FlagList,
251 _count(FlagList),Pkg->InstState) == false)
252 return _error->Error("Malformed 2nd word in the Status line");
253
254 // Isloate the last word
255 I++;
256 Start = I;
257 for(; I < Stop && *I != ' '; I++);
258 if (I != Stop)
259 return _error->Error("Malformed Status line, no 3rd word");
260
261 // Process the flag field
262 WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled},
263 {"unpacked",pkgCache::State::UnPacked},
264 {"half-configured",pkgCache::State::HalfConfigured},
265 {"installed",pkgCache::State::Installed},
266 {"uninstalled",pkgCache::State::UnInstalled},
267 {"half-installed",pkgCache::State::HalfInstalled},
268 {"config-files",pkgCache::State::ConfigFiles},
269 {"post-inst-failed",pkgCache::State::HalfConfigured},
270 {"removal-failed",pkgCache::State::HalfInstalled}};
271 if (GrabWord(string(Start,I-Start),StatusList,
272 _count(StatusList),Pkg->CurrentState) == false)
273 return _error->Error("Malformed 3rd word in the Status line");
274
275 /* A Status line marks the package as indicating the current
276 version as well. Only if it is actually installed.. Otherwise
277 the interesting dpkg handling of the status file creates bogus
278 entries. */
279 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
280 Pkg->CurrentState == pkgCache::State::ConfigFiles))
281 {
282 if (Ver.end() == true)
283 _error->Warning("Encountered status field in a non-version description");
284 else
285 Pkg->CurrentVer = Ver.Index();
286 }
287
288 return true;
289}
290 /*}}}*/
291// ListParser::ParseDepends - Parse a dependency element /*{{{*/
292// ---------------------------------------------------------------------
293/* This parses the dependency elements out of a standard string in place,
294 bit by bit. */
295const char *debListParser::ParseDepends(const char *Start,const char *Stop,
296 string &Package,string &Ver,
297 unsigned int &Op)
298{
299 // Strip off leading space
300 for (;Start != Stop && isspace(*Start) != 0; Start++);
301
302 // Parse off the package name
303 const char *I = Start;
304 for (;I != Stop && isspace(*I) == 0 && *I != '(' && *I != ')' &&
305 *I != ',' && *I != '|'; I++);
306
307 // Malformed, no '('
308 if (I != Stop && *I == ')')
309 return 0;
310
311 if (I == Start)
312 return 0;
313
314 // Stash the package name
315 Package.assign(Start,I - Start);
316
317 // Skip white space to the '('
318 for (;I != Stop && isspace(*I) != 0 ; I++);
319
320 // Parse a version
321 if (I != Stop && *I == '(')
322 {
323 // Skip the '('
324 for (I++; I != Stop && isspace(*I) != 0 ; I++);
325 if (I + 3 >= Stop)
326 return 0;
327
328 // Determine the operator
329 switch (*I)
330 {
331 case '<':
332 I++;
333 if (*I == '=')
334 {
335 I++;
336 Op = pkgCache::Dep::LessEq;
337 break;
338 }
339
340 if (*I == '<')
341 {
342 I++;
343 Op = pkgCache::Dep::Less;
344 break;
345 }
346
347 // < is the same as <= and << is really Cs < for some reason
348 Op = pkgCache::Dep::LessEq;
349 break;
350
351 case '>':
352 I++;
353 if (*I == '=')
354 {
355 I++;
356 Op = pkgCache::Dep::GreaterEq;
357 break;
358 }
359
360 if (*I == '>')
361 {
362 I++;
363 Op = pkgCache::Dep::Greater;
364 break;
365 }
366
367 // > is the same as >= and >> is really Cs > for some reason
368 Op = pkgCache::Dep::GreaterEq;
369 break;
370
371 case '=':
372 Op = pkgCache::Dep::Equals;
373 I++;
374 break;
375
376 // HACK around bad package definitions
377 default:
378 Op = pkgCache::Dep::Equals;
379 break;
380 }
381
382 // Skip whitespace
383 for (;I != Stop && isspace(*I) != 0; I++);
384 Start = I;
385 for (;I != Stop && *I != ')'; I++);
386 if (I == Stop || Start == I)
387 return 0;
388
389 Ver = string(Start,I-Start);
390 I++;
391 }
392 else
393 {
394 Ver = string();
395 Op = pkgCache::Dep::NoOp;
396 }
397
398 // Skip whitespace
399 for (;I != Stop && isspace(*I) != 0; I++);
400 if (I != Stop && *I == '|')
401 Op |= pkgCache::Dep::Or;
402
403 if (I == Stop || *I == ',' || *I == '|')
404 {
405 if (I != Stop)
406 for (I++; I != Stop && isspace(*I) != 0; I++);
407 return I;
408 }
409
410 return 0;
411}
412 /*}}}*/
413// ListParser::ParseDepends - Parse a dependency list /*{{{*/
414// ---------------------------------------------------------------------
415/* This is the higher level depends parser. It takes a tag and generates
416 a complete depends tree for the given version. */
417bool debListParser::ParseDepends(pkgCache::VerIterator Ver,
418 const char *Tag,unsigned int Type)
419{
420 const char *Start;
421 const char *Stop;
422 if (Section.Find(Tag,Start,Stop) == false)
423 return true;
424
425 string Package;
426 string Version;
427 unsigned int Op;
428
429 while (1)
430 {
431 Start = ParseDepends(Start,Stop,Package,Version,Op);
432 if (Start == 0)
433 return _error->Error("Problem parsing dependency %s",Tag);
434
435 if (NewDepends(Ver,Package,Version,Op,Type) == false)
436 return false;
437 if (Start == Stop)
438 break;
439 }
440 return true;
441}
442 /*}}}*/
443// ListParser::ParseProvides - Parse the provides list /*{{{*/
444// ---------------------------------------------------------------------
445/* */
446bool debListParser::ParseProvides(pkgCache::VerIterator Ver)
447{
448 const char *Start;
449 const char *Stop;
450 if (Section.Find("Provides",Start,Stop) == false)
451 return true;
452
453 string Package;
454 string Version;
455 unsigned int Op;
456
457 while (1)
458 {
459 Start = ParseDepends(Start,Stop,Package,Version,Op);
460 if (Start == 0)
461 return _error->Error("Problem parsing Provides line");
462 if (Op != pkgCache::Dep::NoOp)
463 return _error->Error("Malformed provides line");
464
465 if (NewProvides(Ver,Package,Version) == false)
466 return false;
467
468 if (Start == Stop)
469 break;
470 }
471
472 return true;
473}
474 /*}}}*/
475// ListParser::GrabWord - Matches a word and returns /*{{{*/
476// ---------------------------------------------------------------------
477/* Looks for a word in a list of words - for ParseStatus */
478bool debListParser::GrabWord(string Word,WordList *List,int Count,
479 unsigned char &Out)
480{
481 for (int C = 0; C != Count; C++)
482 {
483 if (strcasecmp(Word.c_str(),List[C].Str) == 0)
484 {
485 Out = List[C].Val;
486 return true;
487 }
488 }
489 return false;
490}
491 /*}}}*/
492// ListParser::Step - Move to the next section in the file /*{{{*/
493// ---------------------------------------------------------------------
494/* This has to be carefull to only process the correct architecture */
495bool debListParser::Step()
496{
497 iOffset = Tags.Offset();
498 string Arch = _config->Find("APT::architecture");
499 while (Tags.Step(Section) == true)
500 {
501 /* See if this is the correct Architecture, if it isnt then we
502 drop the whole section */
503 const char *Start;
504 const char *Stop;
505 if (Section.Find("Architecture",Start,Stop) == false)
506 return true;
507
508 if (stringcmp(Start,Stop,Arch.begin(),Arch.end()) == 0)
509 return true;
510
511 if (stringcmp(Start,Stop,"all") == 0)
512 return true;
513
514 iOffset = Tags.Offset();
515 }
516 return false;
517}
518 /*}}}*/