Moved strutl.h
[ntk/apt.git] / apt-pkg / deb / deblistparser.cc
CommitLineData
f55a958f
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
cdcc6d34 3// $Id: deblistparser.cc,v 1.14 1999/01/27 02:48:53 jgg Exp $
f55a958f
AL
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 /*{{{*/
094a497d
AL
13#include <apt-pkg/deblistparser.h>
14#include <apt-pkg/error.h>
15#include <apt-pkg/configuration.h>
cdcc6d34 16#include <apt-pkg/strutl.h>
9c14e3d6 17
f55a958f
AL
18#include <system.h>
19 /*}}}*/
20
21// ListParser::debListParser - Constructor /*{{{*/
22// ---------------------------------------------------------------------
23/* */
8e06abb2 24debListParser::debListParser(FileFd &File) : Tags(File)
f55a958f 25{
0149949b
AL
26}
27 /*}}}*/
f55a958f
AL
28// ListParser::UniqFindTagWrite - Find the tag and write a unq string /*{{{*/
29// ---------------------------------------------------------------------
30/* */
31unsigned 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 /*}}}*/
f55a958f
AL
40// ListParser::Package - Return the package name /*{{{*/
41// ---------------------------------------------------------------------
42/* This is to return the name of the package this section describes */
43string debListParser::Package()
44{
b0b4efb9 45 string Result = Section.FindS("Package");
f55a958f
AL
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 */
56string debListParser::Version()
57{
b0b4efb9 58 return Section.FindS("Version");
f55a958f
AL
59}
60 /*}}}*/
f55a958f
AL
61// ListParser::NewVersion - Fill in the version structure /*{{{*/
62// ---------------------------------------------------------------------
63/* */
64bool debListParser::NewVersion(pkgCache::VerIterator Ver)
0149949b
AL
65{
66 // Parse the section
b35d2f5f 67 Ver->Section = UniqFindTagWrite("Section");
0149949b
AL
68
69 // Archive Size
b0b4efb9 70 Ver->Size = (unsigned)Section.FindI("Size");
0149949b
AL
71
72 // Unpacked Size (in K)
b0b4efb9 73 Ver->InstalledSize = (unsigned)Section.FindI("Installed-Size");
0149949b
AL
74 Ver->InstalledSize *= 1024;
75
76 // Priority
77 const char *Start;
78 const char *Stop;
79 if (Section.Find("Priority",Start,Stop) == true)
80 {
6c139d6e
AL
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}};
0149949b
AL
86 if (GrabWord(string(Start,Stop-Start),PrioList,
87 _count(PrioList),Ver->Priority) == false)
88 return _error->Error("Malformed Priority line");
89 }
dcb79bae 90
6c139d6e 91 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
dcb79bae 92 return false;
8efa2a3b 93 if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
dcb79bae 94 return false;
6c139d6e 95 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
dcb79bae 96 return false;
6c139d6e 97 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
dcb79bae 98 return false;
6c139d6e 99 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
dcb79bae 100 return false;
f55ece0e 101 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false)
dcb79bae
AL
102 return false;
103
104 if (ParseProvides(Ver) == false)
105 return false;
0149949b 106
f55a958f
AL
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 */
114bool debListParser::UsePackage(pkgCache::PkgIterator Pkg,
115 pkgCache::VerIterator Ver)
116{
117 if (Pkg->Section == 0)
b35d2f5f 118 Pkg->Section = UniqFindTagWrite("Section");
b0b4efb9 119 if (Section.FindFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
f55a958f 120 return false;
b0b4efb9 121 if (Section.FindFlag("Immediate-Configure",Pkg->Flags,pkgCache::Flag::ImmediateConf) == false)
f55a958f
AL
122 return false;
123 if (ParseStatus(Pkg,Ver) == false)
124 return false;
125 return true;
126}
127 /*}}}*/
dcb79bae 128// ListParser::ParseStatus - Parse the status field /*{{{*/
f55a958f
AL
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 */
141bool 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
6c139d6e
AL
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}};
f55a958f
AL
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
6c139d6e
AL
173 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
174 {"reinstreq",pkgCache::State::ReInstReq},
175 {"hold",pkgCache::State::HoldInst},
176 {"hold-reinstreq",pkgCache::State::HoldReInstReq}};
f55a958f
AL
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
6c139d6e
AL
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}};
f55a958f
AL
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. */
6c139d6e
AL
206 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
207 Pkg->CurrentState == pkgCache::State::ConfigFiles))
f55a958f
AL
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
dcb79bae
AL
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. */
222const 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++;
6c139d6e 263 Op = pkgCache::Dep::LessEq;
dcb79bae
AL
264 break;
265 }
266
267 if (*I == '<')
268 {
269 I++;
6c139d6e 270 Op = pkgCache::Dep::Less;
dcb79bae
AL
271 break;
272 }
273
274 // < is the same as <= and << is really Cs < for some reason
6c139d6e 275 Op = pkgCache::Dep::LessEq;
dcb79bae
AL
276 break;
277
278 case '>':
279 I++;
280 if (*I == '=')
281 {
282 I++;
6c139d6e 283 Op = pkgCache::Dep::GreaterEq;
dcb79bae
AL
284 break;
285 }
286
287 if (*I == '>')
288 {
289 I++;
6c139d6e 290 Op = pkgCache::Dep::Greater;
dcb79bae
AL
291 break;
292 }
293
294 // > is the same as >= and >> is really Cs > for some reason
6c139d6e 295 Op = pkgCache::Dep::GreaterEq;
dcb79bae
AL
296 break;
297
298 case '=':
6c139d6e 299 Op = pkgCache::Dep::Equals;
dcb79bae
AL
300 I++;
301 break;
302
303 // HACK around bad package definitions
304 default:
6c139d6e 305 Op = pkgCache::Dep::Equals;
dcb79bae
AL
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();
6c139d6e 322 Op = pkgCache::Dep::NoOp;
dcb79bae
AL
323 }
324
325 // Skip whitespace
326 for (;I != Stop && isspace(*I) != 0; I++);
327 if (I != Stop && *I == '|')
6c139d6e 328 Op |= pkgCache::Dep::Or;
dcb79bae
AL
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. */
344bool 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
8efa2a3b 356 while (1)
dcb79bae 357 {
8efa2a3b 358 Start = ParseDepends(Start,Stop,Package,Version,Op);
dcb79bae
AL
359 if (Start == 0)
360 return _error->Error("Problem parsing dependency %s",Tag);
8efa2a3b 361
dcb79bae
AL
362 if (NewDepends(Ver,Package,Version,Op,Type) == false)
363 return false;
8efa2a3b
AL
364 if (Start == Stop)
365 break;
dcb79bae
AL
366 }
367 return true;
368}
369 /*}}}*/
370// ListParser::ParseProvides - Parse the provides list /*{{{*/
371// ---------------------------------------------------------------------
372/* */
373bool 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");
6c139d6e 389 if (Op != pkgCache::Dep::NoOp)
dcb79bae
AL
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
f55a958f
AL
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 */
405bool 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// ---------------------------------------------------------------------
0149949b 421/* This has to be carefull to only process the correct architecture */
f55a958f
AL
422bool debListParser::Step()
423{
dcb79bae 424 iOffset = Tags.Offset();
9c14e3d6 425 string Arch = _config->Find("APT::architecture");
0149949b
AL
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;
9c14e3d6
AL
434
435 if (stringcmp(Start,Stop,Arch.begin(),Arch.end()) == 0)
0149949b
AL
436 return true;
437
9c14e3d6 438 if (stringcmp(Start,Stop,"all") == 0)
0149949b 439 return true;
dcb79bae
AL
440
441 iOffset = Tags.Offset();
0149949b
AL
442 }
443 return false;
f55a958f
AL
444}
445 /*}}}*/
b0b4efb9
AL
446// ListParser::LoadReleaseInfo - Load the release information /*{{{*/
447// ---------------------------------------------------------------------
448/* */
449bool 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);
0dbb95d8 471
3c124dde
AL
472 if (Section.FindFlag("NotAutomatic",FileI->Flags,
473 pkgCache::Flag::NotAutomatic) == false)
0dbb95d8 474 _error->Warning("Bad NotAutomatic flag");
0dbb95d8 475
b0b4efb9
AL
476 return !_error->PendingError();
477}
478 /*}}}*/