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