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