]> git.saurik.com Git - apt.git/blob - apt-pkg/deb/deblistparser.cc
Fixed bad return for missing updates directory
[apt.git] / apt-pkg / deb / deblistparser.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: deblistparser.cc,v 1.22 1999/07/30 02:54:25 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,
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 {"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 while (Tags.Step(Section) == true)
470 {
471 /* See if this is the correct Architecture, if it isn't then we
472 drop the whole section. A missing arch tag only happens (in theory)
473 inside the Status file, so that is a positive return */
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 /*}}}*/