First draft of make system and name change to apt-pkg
[ntk/apt.git] / apt-pkg / deb / deblistparser.cc
CommitLineData
f55a958f
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
094a497d 3// $Id: deblistparser.cc,v 1.6 1998/07/12 23:58:52 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/* */
24debListParser::debListParser(File &File) : Tags(File)
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
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 {
6c139d6e
AL
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}};
0149949b
AL
158 if (GrabWord(string(Start,Stop-Start),PrioList,
159 _count(PrioList),Ver->Priority) == false)
160 return _error->Error("Malformed Priority line");
161 }
dcb79bae 162
6c139d6e 163 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
dcb79bae 164 return false;
6c139d6e 165 if (ParseDepends(Ver,"PreDepends",pkgCache::Dep::PreDepends) == false)
dcb79bae 166 return false;
6c139d6e 167 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
dcb79bae 168 return false;
6c139d6e 169 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
dcb79bae 170 return false;
6c139d6e 171 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
dcb79bae 172 return false;
6c139d6e 173 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Depends) == false)
dcb79bae
AL
174 return false;
175
176 if (ParseProvides(Ver) == false)
177 return false;
0149949b 178
f55a958f
AL
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;
6c139d6e 192 if (HandleFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
f55a958f 193 return false;
6c139d6e 194 if (HandleFlag("Immediate-Configure",Pkg->Flags,pkgCache::Flag::ImmediateConf) == false)
f55a958f
AL
195 return false;
196 if (ParseStatus(Pkg,Ver) == false)
197 return false;
198 return true;
199}
200 /*}}}*/
dcb79bae 201// ListParser::ParseStatus - Parse the status field /*{{{*/
f55a958f
AL
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
6c139d6e
AL
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}};
f55a958f
AL
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
6c139d6e
AL
246 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
247 {"reinstreq",pkgCache::State::ReInstReq},
248 {"hold",pkgCache::State::HoldInst},
249 {"hold-reinstreq",pkgCache::State::HoldReInstReq}};
f55a958f
AL
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
6c139d6e
AL
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}};
f55a958f
AL
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. */
6c139d6e
AL
279 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
280 Pkg->CurrentState == pkgCache::State::ConfigFiles))
f55a958f
AL
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
dcb79bae
AL
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++;
6c139d6e 336 Op = pkgCache::Dep::LessEq;
dcb79bae
AL
337 break;
338 }
339
340 if (*I == '<')
341 {
342 I++;
6c139d6e 343 Op = pkgCache::Dep::Less;
dcb79bae
AL
344 break;
345 }
346
347 // < is the same as <= and << is really Cs < for some reason
6c139d6e 348 Op = pkgCache::Dep::LessEq;
dcb79bae
AL
349 break;
350
351 case '>':
352 I++;
353 if (*I == '=')
354 {
355 I++;
6c139d6e 356 Op = pkgCache::Dep::GreaterEq;
dcb79bae
AL
357 break;
358 }
359
360 if (*I == '>')
361 {
362 I++;
6c139d6e 363 Op = pkgCache::Dep::Greater;
dcb79bae
AL
364 break;
365 }
366
367 // > is the same as >= and >> is really Cs > for some reason
6c139d6e 368 Op = pkgCache::Dep::GreaterEq;
dcb79bae
AL
369 break;
370
371 case '=':
6c139d6e 372 Op = pkgCache::Dep::Equals;
dcb79bae
AL
373 I++;
374 break;
375
376 // HACK around bad package definitions
377 default:
6c139d6e 378 Op = pkgCache::Dep::Equals;
dcb79bae
AL
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();
6c139d6e 395 Op = pkgCache::Dep::NoOp;
dcb79bae
AL
396 }
397
398 // Skip whitespace
399 for (;I != Stop && isspace(*I) != 0; I++);
400 if (I != Stop && *I == '|')
6c139d6e 401 Op |= pkgCache::Dep::Or;
dcb79bae
AL
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 ((Start = ParseDepends(Start,Stop,Package,Version,Op)) != Stop)
430 {
431 if (Start == 0)
432 return _error->Error("Problem parsing dependency %s",Tag);
433
434 if (NewDepends(Ver,Package,Version,Op,Type) == false)
435 return false;
436 }
437 return true;
438}
439 /*}}}*/
440// ListParser::ParseProvides - Parse the provides list /*{{{*/
441// ---------------------------------------------------------------------
442/* */
443bool debListParser::ParseProvides(pkgCache::VerIterator Ver)
444{
445 const char *Start;
446 const char *Stop;
447 if (Section.Find("Provides",Start,Stop) == false)
448 return true;
449
450 string Package;
451 string Version;
452 unsigned int Op;
453
454 while (1)
455 {
456 Start = ParseDepends(Start,Stop,Package,Version,Op);
457 if (Start == 0)
458 return _error->Error("Problem parsing Provides line");
6c139d6e 459 if (Op != pkgCache::Dep::NoOp)
dcb79bae
AL
460 return _error->Error("Malformed provides line");
461
462 if (NewProvides(Ver,Package,Version) == false)
463 return false;
464
465 if (Start == Stop)
466 break;
467 }
468
f55a958f
AL
469 return true;
470}
471 /*}}}*/
472// ListParser::GrabWord - Matches a word and returns /*{{{*/
473// ---------------------------------------------------------------------
474/* Looks for a word in a list of words - for ParseStatus */
475bool debListParser::GrabWord(string Word,WordList *List,int Count,
476 unsigned char &Out)
477{
478 for (int C = 0; C != Count; C++)
479 {
480 if (strcasecmp(Word.c_str(),List[C].Str) == 0)
481 {
482 Out = List[C].Val;
483 return true;
484 }
485 }
486 return false;
487}
488 /*}}}*/
489// ListParser::Step - Move to the next section in the file /*{{{*/
490// ---------------------------------------------------------------------
0149949b 491/* This has to be carefull to only process the correct architecture */
f55a958f
AL
492bool debListParser::Step()
493{
dcb79bae 494 iOffset = Tags.Offset();
9c14e3d6 495 string Arch = _config->Find("APT::architecture");
0149949b
AL
496 while (Tags.Step(Section) == true)
497 {
498 /* See if this is the correct Architecture, if it isnt then we
499 drop the whole section */
500 const char *Start;
501 const char *Stop;
502 if (Section.Find("Architecture",Start,Stop) == false)
503 return true;
9c14e3d6
AL
504
505 if (stringcmp(Start,Stop,Arch.begin(),Arch.end()) == 0)
0149949b
AL
506 return true;
507
9c14e3d6 508 if (stringcmp(Start,Stop,"all") == 0)
0149949b 509 return true;
dcb79bae
AL
510
511 iOffset = Tags.Offset();
0149949b
AL
512 }
513 return false;
f55a958f
AL
514}
515 /*}}}*/