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