]> git.saurik.com Git - apt.git/blame - apt-pkg/deb/deblistparser.cc
Needs Unpack fixes
[apt.git] / apt-pkg / deb / deblistparser.cc
CommitLineData
f55a958f
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
f55ece0e 3// $Id: deblistparser.cc,v 1.10 1998/08/09 00:51:35 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 /*{{{*/
094a497d
AL
13#include <apt-pkg/deblistparser.h>
14#include <apt-pkg/error.h>
15#include <apt-pkg/configuration.h>
9c14e3d6
AL
16#include <strutl.h>
17
f55a958f
AL
18#include <system.h>
19 /*}}}*/
20
21// ListParser::debListParser - Constructor /*{{{*/
22// ---------------------------------------------------------------------
23/* */
8e06abb2 24debListParser::debListParser(FileFd &File) : Tags(File)
f55a958f 25{
f55a958f
AL
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 /*}}}*/
0149949b
AL
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 /*}}}*/
f55a958f
AL
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;
9c14e3d6 88 if (stringcasecmp(Start,Stop,"yes") == 0)
f55a958f 89 Set = 1;
9c14e3d6 90 if (stringcasecmp(Start,Stop,"true") == 0)
f55a958f 91 Set = 1;
9c14e3d6 92 if (stringcasecmp(Start,Stop,"no") == 0)
f55a958f 93 Set = 0;
9c14e3d6 94 if (stringcasecmp(Start,Stop,"false") == 0)
f55a958f
AL
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 /*}}}*/
f55a958f
AL
130// ListParser::NewVersion - Fill in the version structure /*{{{*/
131// ---------------------------------------------------------------------
132/* */
133bool debListParser::NewVersion(pkgCache::VerIterator Ver)
0149949b
AL
134{
135 // Parse the section
b35d2f5f 136 Ver->Section = UniqFindTagWrite("Section");
0149949b
AL
137
138 // Archive Size
b35d2f5f 139 Ver->Size = (unsigned)FindTagI("Size");
0149949b
AL
140
141 // Unpacked Size (in K)
b35d2f5f 142 Ver->InstalledSize = (unsigned)FindTagI("Installed-Size");
0149949b
AL
143 Ver->InstalledSize *= 1024;
144
145 // Priority
146 const char *Start;
147 const char *Stop;
148 if (Section.Find("Priority",Start,Stop) == true)
149 {
6c139d6e
AL
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}};
0149949b
AL
155 if (GrabWord(string(Start,Stop-Start),PrioList,
156 _count(PrioList),Ver->Priority) == false)
157 return _error->Error("Malformed Priority line");
158 }
dcb79bae 159
6c139d6e 160 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
dcb79bae 161 return false;
8efa2a3b 162 if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
dcb79bae 163 return false;
6c139d6e 164 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
dcb79bae 165 return false;
6c139d6e 166 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
dcb79bae 167 return false;
6c139d6e 168 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
dcb79bae 169 return false;
f55ece0e 170 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false)
dcb79bae
AL
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)
b35d2f5f 187 Pkg->Section = UniqFindTagWrite("Section");
6c139d6e 188 if (HandleFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
f55a958f 189 return false;
6c139d6e 190 if (HandleFlag("Immediate-Configure",Pkg->Flags,pkgCache::Flag::ImmediateConf) == false)
f55a958f
AL
191 return false;
192 if (ParseStatus(Pkg,Ver) == false)
193 return false;
194 return true;
195}
196 /*}}}*/
dcb79bae 197// ListParser::ParseStatus - Parse the status field /*{{{*/
f55a958f
AL
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 */
210bool 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
6c139d6e
AL
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}};
f55a958f
AL
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
6c139d6e
AL
242 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
243 {"reinstreq",pkgCache::State::ReInstReq},
244 {"hold",pkgCache::State::HoldInst},
245 {"hold-reinstreq",pkgCache::State::HoldReInstReq}};
f55a958f
AL
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
6c139d6e
AL
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}};
f55a958f
AL
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. */
6c139d6e
AL
275 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
276 Pkg->CurrentState == pkgCache::State::ConfigFiles))
f55a958f
AL
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
dcb79bae
AL
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. */
291const 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++;
6c139d6e 332 Op = pkgCache::Dep::LessEq;
dcb79bae
AL
333 break;
334 }
335
336 if (*I == '<')
337 {
338 I++;
6c139d6e 339 Op = pkgCache::Dep::Less;
dcb79bae
AL
340 break;
341 }
342
343 // < is the same as <= and << is really Cs < for some reason
6c139d6e 344 Op = pkgCache::Dep::LessEq;
dcb79bae
AL
345 break;
346
347 case '>':
348 I++;
349 if (*I == '=')
350 {
351 I++;
6c139d6e 352 Op = pkgCache::Dep::GreaterEq;
dcb79bae
AL
353 break;
354 }
355
356 if (*I == '>')
357 {
358 I++;
6c139d6e 359 Op = pkgCache::Dep::Greater;
dcb79bae
AL
360 break;
361 }
362
363 // > is the same as >= and >> is really Cs > for some reason
6c139d6e 364 Op = pkgCache::Dep::GreaterEq;
dcb79bae
AL
365 break;
366
367 case '=':
6c139d6e 368 Op = pkgCache::Dep::Equals;
dcb79bae
AL
369 I++;
370 break;
371
372 // HACK around bad package definitions
373 default:
6c139d6e 374 Op = pkgCache::Dep::Equals;
dcb79bae
AL
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();
6c139d6e 391 Op = pkgCache::Dep::NoOp;
dcb79bae
AL
392 }
393
394 // Skip whitespace
395 for (;I != Stop && isspace(*I) != 0; I++);
396 if (I != Stop && *I == '|')
6c139d6e 397 Op |= pkgCache::Dep::Or;
dcb79bae
AL
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. */
413bool 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
8efa2a3b 425 while (1)
dcb79bae 426 {
8efa2a3b 427 Start = ParseDepends(Start,Stop,Package,Version,Op);
dcb79bae
AL
428 if (Start == 0)
429 return _error->Error("Problem parsing dependency %s",Tag);
8efa2a3b 430
dcb79bae
AL
431 if (NewDepends(Ver,Package,Version,Op,Type) == false)
432 return false;
8efa2a3b
AL
433 if (Start == Stop)
434 break;
dcb79bae
AL
435 }
436 return true;
437}
438 /*}}}*/
439// ListParser::ParseProvides - Parse the provides list /*{{{*/
440// ---------------------------------------------------------------------
441/* */
442bool 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");
6c139d6e 458 if (Op != pkgCache::Dep::NoOp)
dcb79bae
AL
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
f55a958f
AL
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 */
474bool 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// ---------------------------------------------------------------------
0149949b 490/* This has to be carefull to only process the correct architecture */
f55a958f
AL
491bool debListParser::Step()
492{
dcb79bae 493 iOffset = Tags.Offset();
9c14e3d6 494 string Arch = _config->Find("APT::architecture");
0149949b
AL
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;
9c14e3d6
AL
503
504 if (stringcmp(Start,Stop,Arch.begin(),Arch.end()) == 0)
0149949b
AL
505 return true;
506
9c14e3d6 507 if (stringcmp(Start,Stop,"all") == 0)
0149949b 508 return true;
dcb79bae
AL
509
510 iOffset = Tags.Offset();
0149949b
AL
511 }
512 return false;
f55a958f
AL
513}
514 /*}}}*/