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