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