Sync
[ntk/apt.git] / apt-pkg / deb / deblistparser.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: deblistparser.cc,v 1.10 1998/08/09 00:51:35 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 /* */
24 debListParser::debListParser(FileFd &File) : Tags(File)
25 {
26 }
27 /*}}}*/
28 // ListParser::FindTag - Find the tag and return a string /*{{{*/
29 // ---------------------------------------------------------------------
30 /* */
31 string 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 /* */
43 signed 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 /* */
67 unsigned 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 */
79 bool 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 */
112 string 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 */
125 string debListParser::Version()
126 {
127 return FindTag("Version");
128 }
129 /*}}}*/
130 // ListParser::NewVersion - Fill in the version structure /*{{{*/
131 // ---------------------------------------------------------------------
132 /* */
133 bool debListParser::NewVersion(pkgCache::VerIterator Ver)
134 {
135 // Parse the section
136 Ver->Section = UniqFindTagWrite("Section");
137
138 // Archive Size
139 Ver->Size = (unsigned)FindTagI("Size");
140
141 // Unpacked Size (in K)
142 Ver->InstalledSize = (unsigned)FindTagI("Installed-Size");
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::State::Important},
151 {"required",pkgCache::State::Required},
152 {"standard",pkgCache::State::Standard},
153 {"optional",pkgCache::State::Optional},
154 {"extra",pkgCache::State::Extra}};
155 if (GrabWord(string(Start,Stop-Start),PrioList,
156 _count(PrioList),Ver->Priority) == false)
157 return _error->Error("Malformed Priority line");
158 }
159
160 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
161 return false;
162 if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
163 return false;
164 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
165 return false;
166 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
167 return false;
168 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
169 return false;
170 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false)
171 return false;
172
173 if (ParseProvides(Ver) == false)
174 return false;
175
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 */
183 bool debListParser::UsePackage(pkgCache::PkgIterator Pkg,
184 pkgCache::VerIterator Ver)
185 {
186 if (Pkg->Section == 0)
187 Pkg->Section = UniqFindTagWrite("Section");
188 if (HandleFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
189 return false;
190 if (HandleFlag("Immediate-Configure",Pkg->Flags,pkgCache::Flag::ImmediateConf) == false)
191 return false;
192 if (ParseStatus(Pkg,Ver) == false)
193 return false;
194 return true;
195 }
196 /*}}}*/
197 // ListParser::ParseStatus - Parse the status field /*{{{*/
198 // ---------------------------------------------------------------------
199 /* Status lines are of the form,
200 Status: want flag status
201 want = unknown, install, hold, deinstall, purge
202 flag = ok, reinstreq, hold, hold-reinstreq
203 status = not-installed, unpacked, half-configured, uninstalled,
204 half-installed, config-files, post-inst-failed,
205 removal-failed, installed
206
207 Some of the above are obsolete (I think?) flag = hold-* and
208 status = post-inst-failed, removal-failed at least.
209 */
210 bool debListParser::ParseStatus(pkgCache::PkgIterator Pkg,
211 pkgCache::VerIterator Ver)
212 {
213 const char *Start;
214 const char *Stop;
215 if (Section.Find("Status",Start,Stop) == false)
216 return true;
217
218 // Isolate the first word
219 const char *I = Start;
220 for(; I < Stop && *I != ' '; I++);
221 if (I >= Stop || *I != ' ')
222 return _error->Error("Malformed Status line");
223
224 // Process the want field
225 WordList WantList[] = {{"unknown",pkgCache::State::Unknown},
226 {"install",pkgCache::State::Install},
227 {"hold",pkgCache::State::Hold},
228 {"deinstall",pkgCache::State::DeInstall},
229 {"purge",pkgCache::State::Purge}};
230 if (GrabWord(string(Start,I-Start),WantList,
231 _count(WantList),Pkg->SelectedState) == false)
232 return _error->Error("Malformed 1st word in the Status line");
233
234 // Isloate the next word
235 I++;
236 Start = I;
237 for(; I < Stop && *I != ' '; I++);
238 if (I >= Stop || *I != ' ')
239 return _error->Error("Malformed status line, no 2nd word");
240
241 // Process the flag field
242 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
243 {"reinstreq",pkgCache::State::ReInstReq},
244 {"hold",pkgCache::State::HoldInst},
245 {"hold-reinstreq",pkgCache::State::HoldReInstReq}};
246 if (GrabWord(string(Start,I-Start),FlagList,
247 _count(FlagList),Pkg->InstState) == false)
248 return _error->Error("Malformed 2nd word in the Status line");
249
250 // Isloate the last word
251 I++;
252 Start = I;
253 for(; I < Stop && *I != ' '; I++);
254 if (I != Stop)
255 return _error->Error("Malformed Status line, no 3rd word");
256
257 // Process the flag field
258 WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled},
259 {"unpacked",pkgCache::State::UnPacked},
260 {"half-configured",pkgCache::State::HalfConfigured},
261 {"installed",pkgCache::State::Installed},
262 {"uninstalled",pkgCache::State::UnInstalled},
263 {"half-installed",pkgCache::State::HalfInstalled},
264 {"config-files",pkgCache::State::ConfigFiles},
265 {"post-inst-failed",pkgCache::State::HalfConfigured},
266 {"removal-failed",pkgCache::State::HalfInstalled}};
267 if (GrabWord(string(Start,I-Start),StatusList,
268 _count(StatusList),Pkg->CurrentState) == false)
269 return _error->Error("Malformed 3rd word in the Status line");
270
271 /* A Status line marks the package as indicating the current
272 version as well. Only if it is actually installed.. Otherwise
273 the interesting dpkg handling of the status file creates bogus
274 entries. */
275 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
276 Pkg->CurrentState == pkgCache::State::ConfigFiles))
277 {
278 if (Ver.end() == true)
279 _error->Warning("Encountered status field in a non-version description");
280 else
281 Pkg->CurrentVer = Ver.Index();
282 }
283
284 return true;
285 }
286 /*}}}*/
287 // ListParser::ParseDepends - Parse a dependency element /*{{{*/
288 // ---------------------------------------------------------------------
289 /* This parses the dependency elements out of a standard string in place,
290 bit by bit. */
291 const char *debListParser::ParseDepends(const char *Start,const char *Stop,
292 string &Package,string &Ver,
293 unsigned int &Op)
294 {
295 // Strip off leading space
296 for (;Start != Stop && isspace(*Start) != 0; Start++);
297
298 // Parse off the package name
299 const char *I = Start;
300 for (;I != Stop && isspace(*I) == 0 && *I != '(' && *I != ')' &&
301 *I != ',' && *I != '|'; I++);
302
303 // Malformed, no '('
304 if (I != Stop && *I == ')')
305 return 0;
306
307 if (I == Start)
308 return 0;
309
310 // Stash the package name
311 Package.assign(Start,I - Start);
312
313 // Skip white space to the '('
314 for (;I != Stop && isspace(*I) != 0 ; I++);
315
316 // Parse a version
317 if (I != Stop && *I == '(')
318 {
319 // Skip the '('
320 for (I++; I != Stop && isspace(*I) != 0 ; I++);
321 if (I + 3 >= Stop)
322 return 0;
323
324 // Determine the operator
325 switch (*I)
326 {
327 case '<':
328 I++;
329 if (*I == '=')
330 {
331 I++;
332 Op = pkgCache::Dep::LessEq;
333 break;
334 }
335
336 if (*I == '<')
337 {
338 I++;
339 Op = pkgCache::Dep::Less;
340 break;
341 }
342
343 // < is the same as <= and << is really Cs < for some reason
344 Op = pkgCache::Dep::LessEq;
345 break;
346
347 case '>':
348 I++;
349 if (*I == '=')
350 {
351 I++;
352 Op = pkgCache::Dep::GreaterEq;
353 break;
354 }
355
356 if (*I == '>')
357 {
358 I++;
359 Op = pkgCache::Dep::Greater;
360 break;
361 }
362
363 // > is the same as >= and >> is really Cs > for some reason
364 Op = pkgCache::Dep::GreaterEq;
365 break;
366
367 case '=':
368 Op = pkgCache::Dep::Equals;
369 I++;
370 break;
371
372 // HACK around bad package definitions
373 default:
374 Op = pkgCache::Dep::Equals;
375 break;
376 }
377
378 // Skip whitespace
379 for (;I != Stop && isspace(*I) != 0; I++);
380 Start = I;
381 for (;I != Stop && *I != ')'; I++);
382 if (I == Stop || Start == I)
383 return 0;
384
385 Ver = string(Start,I-Start);
386 I++;
387 }
388 else
389 {
390 Ver = string();
391 Op = pkgCache::Dep::NoOp;
392 }
393
394 // Skip whitespace
395 for (;I != Stop && isspace(*I) != 0; I++);
396 if (I != Stop && *I == '|')
397 Op |= pkgCache::Dep::Or;
398
399 if (I == Stop || *I == ',' || *I == '|')
400 {
401 if (I != Stop)
402 for (I++; I != Stop && isspace(*I) != 0; I++);
403 return I;
404 }
405
406 return 0;
407 }
408 /*}}}*/
409 // ListParser::ParseDepends - Parse a dependency list /*{{{*/
410 // ---------------------------------------------------------------------
411 /* This is the higher level depends parser. It takes a tag and generates
412 a complete depends tree for the given version. */
413 bool debListParser::ParseDepends(pkgCache::VerIterator Ver,
414 const char *Tag,unsigned int Type)
415 {
416 const char *Start;
417 const char *Stop;
418 if (Section.Find(Tag,Start,Stop) == false)
419 return true;
420
421 string Package;
422 string Version;
423 unsigned int Op;
424
425 while (1)
426 {
427 Start = ParseDepends(Start,Stop,Package,Version,Op);
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 if (Start == Stop)
434 break;
435 }
436 return true;
437 }
438 /*}}}*/
439 // ListParser::ParseProvides - Parse the provides list /*{{{*/
440 // ---------------------------------------------------------------------
441 /* */
442 bool debListParser::ParseProvides(pkgCache::VerIterator Ver)
443 {
444 const char *Start;
445 const char *Stop;
446 if (Section.Find("Provides",Start,Stop) == false)
447 return true;
448
449 string Package;
450 string Version;
451 unsigned int Op;
452
453 while (1)
454 {
455 Start = ParseDepends(Start,Stop,Package,Version,Op);
456 if (Start == 0)
457 return _error->Error("Problem parsing Provides line");
458 if (Op != pkgCache::Dep::NoOp)
459 return _error->Error("Malformed provides line");
460
461 if (NewProvides(Ver,Package,Version) == false)
462 return false;
463
464 if (Start == Stop)
465 break;
466 }
467
468 return true;
469 }
470 /*}}}*/
471 // ListParser::GrabWord - Matches a word and returns /*{{{*/
472 // ---------------------------------------------------------------------
473 /* Looks for a word in a list of words - for ParseStatus */
474 bool debListParser::GrabWord(string Word,WordList *List,int Count,
475 unsigned char &Out)
476 {
477 for (int C = 0; C != Count; C++)
478 {
479 if (strcasecmp(Word.c_str(),List[C].Str) == 0)
480 {
481 Out = List[C].Val;
482 return true;
483 }
484 }
485 return false;
486 }
487 /*}}}*/
488 // ListParser::Step - Move to the next section in the file /*{{{*/
489 // ---------------------------------------------------------------------
490 /* This has to be carefull to only process the correct architecture */
491 bool debListParser::Step()
492 {
493 iOffset = Tags.Offset();
494 string Arch = _config->Find("APT::architecture");
495 while (Tags.Step(Section) == true)
496 {
497 /* See if this is the correct Architecture, if it isnt then we
498 drop the whole section */
499 const char *Start;
500 const char *Stop;
501 if (Section.Find("Architecture",Start,Stop) == false)
502 return true;
503
504 if (stringcmp(Start,Stop,Arch.begin(),Arch.end()) == 0)
505 return true;
506
507 if (stringcmp(Start,Stop,"all") == 0)
508 return true;
509
510 iOffset = Tags.Offset();
511 }
512 return false;
513 }
514 /*}}}*/