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