Fixed space parsing problem
[ntk/apt.git] / apt-pkg / deb / deblistparser.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: deblistparser.cc,v 1.18 1999/04/12 19:16:11 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 // Skip trailing whitespace
322 const char *End = I;
323 for (; End > Start && isspace(End[-1]); End--);
324
325 Ver = string(Start,End-Start);
326 I++;
327 }
328 else
329 {
330 Ver = string();
331 Op = pkgCache::Dep::NoOp;
332 }
333
334 // Skip whitespace
335 for (;I != Stop && isspace(*I) != 0; I++);
336 if (I != Stop && *I == '|')
337 Op |= pkgCache::Dep::Or;
338
339 if (I == Stop || *I == ',' || *I == '|')
340 {
341 if (I != Stop)
342 for (I++; I != Stop && isspace(*I) != 0; I++);
343 return I;
344 }
345
346 return 0;
347 }
348 /*}}}*/
349 // ListParser::ParseDepends - Parse a dependency list /*{{{*/
350 // ---------------------------------------------------------------------
351 /* This is the higher level depends parser. It takes a tag and generates
352 a complete depends tree for the given version. */
353 bool debListParser::ParseDepends(pkgCache::VerIterator Ver,
354 const char *Tag,unsigned int Type)
355 {
356 const char *Start;
357 const char *Stop;
358 if (Section.Find(Tag,Start,Stop) == false)
359 return true;
360
361 string Package;
362 string Version;
363 unsigned int Op;
364
365 while (1)
366 {
367 Start = ParseDepends(Start,Stop,Package,Version,Op);
368 if (Start == 0)
369 return _error->Error("Problem parsing dependency %s",Tag);
370
371 if (NewDepends(Ver,Package,Version,Op,Type) == false)
372 return false;
373 if (Start == Stop)
374 break;
375 }
376 return true;
377 }
378 /*}}}*/
379 // ListParser::ParseProvides - Parse the provides list /*{{{*/
380 // ---------------------------------------------------------------------
381 /* */
382 bool debListParser::ParseProvides(pkgCache::VerIterator Ver)
383 {
384 const char *Start;
385 const char *Stop;
386 if (Section.Find("Provides",Start,Stop) == false)
387 return true;
388
389 string Package;
390 string Version;
391 unsigned int Op;
392
393 while (1)
394 {
395 Start = ParseDepends(Start,Stop,Package,Version,Op);
396 if (Start == 0)
397 return _error->Error("Problem parsing Provides line");
398 if (Op != pkgCache::Dep::NoOp)
399 return _error->Error("Malformed provides line");
400
401 if (NewProvides(Ver,Package,Version) == false)
402 return false;
403
404 if (Start == Stop)
405 break;
406 }
407
408 return true;
409 }
410 /*}}}*/
411 // ListParser::GrabWord - Matches a word and returns /*{{{*/
412 // ---------------------------------------------------------------------
413 /* Looks for a word in a list of words - for ParseStatus */
414 bool debListParser::GrabWord(string Word,WordList *List,int Count,
415 unsigned char &Out)
416 {
417 for (int C = 0; C != Count; C++)
418 {
419 if (strcasecmp(Word.c_str(),List[C].Str) == 0)
420 {
421 Out = List[C].Val;
422 return true;
423 }
424 }
425 return false;
426 }
427 /*}}}*/
428 // ListParser::Step - Move to the next section in the file /*{{{*/
429 // ---------------------------------------------------------------------
430 /* This has to be carefull to only process the correct architecture */
431 bool debListParser::Step()
432 {
433 iOffset = Tags.Offset();
434 string Arch = _config->Find("APT::architecture");
435 while (Tags.Step(Section) == true)
436 {
437 /* See if this is the correct Architecture, if it isnt then we
438 drop the whole section */
439 const char *Start;
440 const char *Stop;
441 if (Section.Find("Architecture",Start,Stop) == false)
442 return true;
443
444 if (stringcmp(Start,Stop,Arch.begin(),Arch.end()) == 0)
445 return true;
446
447 if (stringcmp(Start,Stop,"all") == 0)
448 return true;
449
450 iOffset = Tags.Offset();
451 }
452 return false;
453 }
454 /*}}}*/
455 // ListParser::LoadReleaseInfo - Load the release information /*{{{*/
456 // ---------------------------------------------------------------------
457 /* */
458 bool debListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
459 FileFd &File)
460 {
461 pkgTagFile Tags(File);
462 pkgTagSection Section;
463 if (Tags.Step(Section) == false)
464 return false;
465
466 const char *Start;
467 const char *Stop;
468 if (Section.Find("Archive",Start,Stop) == true)
469 FileI->Archive = WriteUniqString(Start,Stop - Start);
470 if (Section.Find("Component",Start,Stop) == true)
471 FileI->Component = WriteUniqString(Start,Stop - Start);
472 if (Section.Find("Version",Start,Stop) == true)
473 FileI->Version = WriteUniqString(Start,Stop - Start);
474 if (Section.Find("Origin",Start,Stop) == true)
475 FileI->Origin = WriteUniqString(Start,Stop - Start);
476 if (Section.Find("Label",Start,Stop) == true)
477 FileI->Label = WriteUniqString(Start,Stop - Start);
478 if (Section.Find("Architecture",Start,Stop) == true)
479 FileI->Architecture = WriteUniqString(Start,Stop - Start);
480
481 if (Section.FindFlag("NotAutomatic",FileI->Flags,
482 pkgCache::Flag::NotAutomatic) == false)
483 _error->Warning("Bad NotAutomatic flag");
484
485 return !_error->PendingError();
486 }
487 /*}}}*/