Multiple different versions support
[ntk/apt.git] / apt-pkg / deb / deblistparser.cc
CommitLineData
f55a958f
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
204fbdcc 3// $Id: deblistparser.cc,v 1.19 1999/05/23 22:55:54 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>
204fbdcc 17#include <apt-pkg/crc-16.h>
9c14e3d6 18
f55a958f
AL
19#include <system.h>
20 /*}}}*/
21
22// ListParser::debListParser - Constructor /*{{{*/
23// ---------------------------------------------------------------------
24/* */
8e06abb2 25debListParser::debListParser(FileFd &File) : Tags(File)
f55a958f 26{
0149949b
AL
27}
28 /*}}}*/
f55a958f
AL
29// ListParser::UniqFindTagWrite - Find the tag and write a unq string /*{{{*/
30// ---------------------------------------------------------------------
31/* */
32unsigned long debListParser::UniqFindTagWrite(const char *Tag)
33{
34 const char *Start;
35 const char *Stop;
36 if (Section.Find(Tag,Start,Stop) == false)
37 return 0;
38 return WriteUniqString(Start,Stop - Start);
39}
40 /*}}}*/
f55a958f
AL
41// ListParser::Package - Return the package name /*{{{*/
42// ---------------------------------------------------------------------
43/* This is to return the name of the package this section describes */
44string debListParser::Package()
45{
b0b4efb9 46 string Result = Section.FindS("Package");
f55a958f 47 if (Result.empty() == true)
65a1e968 48 _error->Error("Encountered a section with no Package: header");
f55a958f
AL
49 return Result;
50}
51 /*}}}*/
52// ListParser::Version - Return the version string /*{{{*/
53// ---------------------------------------------------------------------
54/* This is to return the string describing the version in debian form,
55 epoch:upstream-release. If this returns the blank string then the
56 entry is assumed to only describe package properties */
57string debListParser::Version()
58{
b0b4efb9 59 return Section.FindS("Version");
f55a958f
AL
60}
61 /*}}}*/
f55a958f
AL
62// ListParser::NewVersion - Fill in the version structure /*{{{*/
63// ---------------------------------------------------------------------
64/* */
65bool debListParser::NewVersion(pkgCache::VerIterator Ver)
0149949b
AL
66{
67 // Parse the section
b35d2f5f 68 Ver->Section = UniqFindTagWrite("Section");
17caf1b1 69 Ver->Arch = UniqFindTagWrite("Architecture");
0149949b
AL
70
71 // Archive Size
b0b4efb9 72 Ver->Size = (unsigned)Section.FindI("Size");
0149949b
AL
73
74 // Unpacked Size (in K)
b0b4efb9 75 Ver->InstalledSize = (unsigned)Section.FindI("Installed-Size");
0149949b
AL
76 Ver->InstalledSize *= 1024;
77
78 // Priority
79 const char *Start;
80 const char *Stop;
81 if (Section.Find("Priority",Start,Stop) == true)
82 {
6c139d6e
AL
83 WordList PrioList[] = {{"important",pkgCache::State::Important},
84 {"required",pkgCache::State::Required},
85 {"standard",pkgCache::State::Standard},
86 {"optional",pkgCache::State::Optional},
87 {"extra",pkgCache::State::Extra}};
0149949b
AL
88 if (GrabWord(string(Start,Stop-Start),PrioList,
89 _count(PrioList),Ver->Priority) == false)
90 return _error->Error("Malformed Priority line");
91 }
dcb79bae 92
6c139d6e 93 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
dcb79bae 94 return false;
8efa2a3b 95 if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
dcb79bae 96 return false;
6c139d6e 97 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
dcb79bae 98 return false;
6c139d6e 99 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
dcb79bae 100 return false;
6c139d6e 101 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
dcb79bae 102 return false;
f55ece0e 103 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false)
dcb79bae
AL
104 return false;
105
106 if (ParseProvides(Ver) == false)
107 return false;
0149949b 108
f55a958f
AL
109 return true;
110}
111 /*}}}*/
112// ListParser::UsePackage - Update a package structure /*{{{*/
113// ---------------------------------------------------------------------
114/* This is called to update the package with any new information
115 that might be found in the section */
116bool debListParser::UsePackage(pkgCache::PkgIterator Pkg,
117 pkgCache::VerIterator Ver)
118{
119 if (Pkg->Section == 0)
b35d2f5f 120 Pkg->Section = UniqFindTagWrite("Section");
b0b4efb9 121 if (Section.FindFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
f55a958f 122 return false;
138d4b3d 123 if (Section.FindFlag("Important",Pkg->Flags,pkgCache::Flag::Important) == false)
f55a958f 124 return false;
138d4b3d
AL
125
126 if (strcmp(Pkg.Name(),"apt") == 0)
127 Pkg->Flags |= pkgCache::Flag::Important;
128
f55a958f
AL
129 if (ParseStatus(Pkg,Ver) == false)
130 return false;
131 return true;
132}
133 /*}}}*/
204fbdcc
AL
134// ListParser::VersionHash - Compute a unique hash for this version /*{{{*/
135// ---------------------------------------------------------------------
136/* */
137unsigned short debListParser::VersionHash()
138{
139 const char *Sections[] ={"Installed-Size",
140 "Depends",
141 "Pre-Depends",
142 "Suggests",
143 "Recommends",
144 "Conflicts",
145 "Replaces",0};
146 unsigned long Result = INIT_FCS;
147 char S[300];
148 for (const char **I = Sections; *I != 0; I++)
149 {
150 const char *Start;
151 const char *End;
152 if (Section.Find(*I,Start,End) == false || End - Start >= (signed)sizeof(S))
153 continue;
154
155 /* Strip out any spaces from the text, this undoes dpkgs reformatting
156 of certain fields */
157 char *I = S;
158 for (; Start != End; Start++)
159 if (isspace(*Start) == 0)
160 *I++ = *Start;
161
162 Result = AddCRC16(Result,S,I - S);
163 }
164
165 return Result;
166}
167 /*}}}*/
dcb79bae 168// ListParser::ParseStatus - Parse the status field /*{{{*/
f55a958f
AL
169// ---------------------------------------------------------------------
170/* Status lines are of the form,
171 Status: want flag status
172 want = unknown, install, hold, deinstall, purge
173 flag = ok, reinstreq, hold, hold-reinstreq
174 status = not-installed, unpacked, half-configured, uninstalled,
175 half-installed, config-files, post-inst-failed,
176 removal-failed, installed
177
178 Some of the above are obsolete (I think?) flag = hold-* and
179 status = post-inst-failed, removal-failed at least.
180 */
181bool debListParser::ParseStatus(pkgCache::PkgIterator Pkg,
182 pkgCache::VerIterator Ver)
183{
184 const char *Start;
185 const char *Stop;
186 if (Section.Find("Status",Start,Stop) == false)
187 return true;
188
189 // Isolate the first word
190 const char *I = Start;
191 for(; I < Stop && *I != ' '; I++);
192 if (I >= Stop || *I != ' ')
193 return _error->Error("Malformed Status line");
194
195 // Process the want field
6c139d6e
AL
196 WordList WantList[] = {{"unknown",pkgCache::State::Unknown},
197 {"install",pkgCache::State::Install},
198 {"hold",pkgCache::State::Hold},
199 {"deinstall",pkgCache::State::DeInstall},
200 {"purge",pkgCache::State::Purge}};
f55a958f
AL
201 if (GrabWord(string(Start,I-Start),WantList,
202 _count(WantList),Pkg->SelectedState) == false)
203 return _error->Error("Malformed 1st word in the Status line");
204
205 // Isloate the next word
206 I++;
207 Start = I;
208 for(; I < Stop && *I != ' '; I++);
209 if (I >= Stop || *I != ' ')
210 return _error->Error("Malformed status line, no 2nd word");
211
212 // Process the flag field
6c139d6e
AL
213 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
214 {"reinstreq",pkgCache::State::ReInstReq},
215 {"hold",pkgCache::State::HoldInst},
216 {"hold-reinstreq",pkgCache::State::HoldReInstReq}};
f55a958f
AL
217 if (GrabWord(string(Start,I-Start),FlagList,
218 _count(FlagList),Pkg->InstState) == false)
219 return _error->Error("Malformed 2nd word in the Status line");
220
221 // Isloate the last word
222 I++;
223 Start = I;
224 for(; I < Stop && *I != ' '; I++);
225 if (I != Stop)
226 return _error->Error("Malformed Status line, no 3rd word");
227
228 // Process the flag field
6c139d6e
AL
229 WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled},
230 {"unpacked",pkgCache::State::UnPacked},
231 {"half-configured",pkgCache::State::HalfConfigured},
232 {"installed",pkgCache::State::Installed},
233 {"uninstalled",pkgCache::State::UnInstalled},
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}};
f55a958f
AL
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. */
6c139d6e
AL
246 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
247 Pkg->CurrentState == pkgCache::State::ConfigFiles))
f55a958f
AL
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
dcb79bae
AL
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. */
262const 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++;
6c139d6e 303 Op = pkgCache::Dep::LessEq;
dcb79bae
AL
304 break;
305 }
306
307 if (*I == '<')
308 {
309 I++;
6c139d6e 310 Op = pkgCache::Dep::Less;
dcb79bae
AL
311 break;
312 }
313
314 // < is the same as <= and << is really Cs < for some reason
6c139d6e 315 Op = pkgCache::Dep::LessEq;
dcb79bae
AL
316 break;
317
318 case '>':
319 I++;
320 if (*I == '=')
321 {
322 I++;
6c139d6e 323 Op = pkgCache::Dep::GreaterEq;
dcb79bae
AL
324 break;
325 }
326
327 if (*I == '>')
328 {
329 I++;
6c139d6e 330 Op = pkgCache::Dep::Greater;
dcb79bae
AL
331 break;
332 }
333
334 // > is the same as >= and >> is really Cs > for some reason
6c139d6e 335 Op = pkgCache::Dep::GreaterEq;
dcb79bae
AL
336 break;
337
338 case '=':
6c139d6e 339 Op = pkgCache::Dep::Equals;
dcb79bae
AL
340 I++;
341 break;
342
343 // HACK around bad package definitions
344 default:
6c139d6e 345 Op = pkgCache::Dep::Equals;
dcb79bae
AL
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
02f000a9
AL
356 // Skip trailing whitespace
357 const char *End = I;
358 for (; End > Start && isspace(End[-1]); End--);
359
360 Ver = string(Start,End-Start);
dcb79bae
AL
361 I++;
362 }
363 else
364 {
365 Ver = string();
6c139d6e 366 Op = pkgCache::Dep::NoOp;
dcb79bae
AL
367 }
368
369 // Skip whitespace
370 for (;I != Stop && isspace(*I) != 0; I++);
371 if (I != Stop && *I == '|')
6c139d6e 372 Op |= pkgCache::Dep::Or;
dcb79bae
AL
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. */
388bool 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
8efa2a3b 400 while (1)
dcb79bae 401 {
8efa2a3b 402 Start = ParseDepends(Start,Stop,Package,Version,Op);
dcb79bae
AL
403 if (Start == 0)
404 return _error->Error("Problem parsing dependency %s",Tag);
8efa2a3b 405
dcb79bae
AL
406 if (NewDepends(Ver,Package,Version,Op,Type) == false)
407 return false;
8efa2a3b
AL
408 if (Start == Stop)
409 break;
dcb79bae
AL
410 }
411 return true;
412}
413 /*}}}*/
414// ListParser::ParseProvides - Parse the provides list /*{{{*/
415// ---------------------------------------------------------------------
416/* */
417bool 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");
6c139d6e 433 if (Op != pkgCache::Dep::NoOp)
dcb79bae
AL
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
f55a958f
AL
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 */
449bool 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// ---------------------------------------------------------------------
0149949b 465/* This has to be carefull to only process the correct architecture */
f55a958f
AL
466bool debListParser::Step()
467{
dcb79bae 468 iOffset = Tags.Offset();
9c14e3d6 469 string Arch = _config->Find("APT::architecture");
0149949b
AL
470 while (Tags.Step(Section) == true)
471 {
472 /* See if this is the correct Architecture, if it isnt then we
473 drop the whole section */
474 const char *Start;
475 const char *Stop;
476 if (Section.Find("Architecture",Start,Stop) == false)
477 return true;
9c14e3d6
AL
478
479 if (stringcmp(Start,Stop,Arch.begin(),Arch.end()) == 0)
0149949b
AL
480 return true;
481
9c14e3d6 482 if (stringcmp(Start,Stop,"all") == 0)
0149949b 483 return true;
dcb79bae
AL
484
485 iOffset = Tags.Offset();
0149949b
AL
486 }
487 return false;
f55a958f
AL
488}
489 /*}}}*/
b0b4efb9
AL
490// ListParser::LoadReleaseInfo - Load the release information /*{{{*/
491// ---------------------------------------------------------------------
492/* */
493bool 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);
0dbb95d8 515
3c124dde
AL
516 if (Section.FindFlag("NotAutomatic",FileI->Flags,
517 pkgCache::Flag::NotAutomatic) == false)
0dbb95d8 518 _error->Warning("Bad NotAutomatic flag");
0dbb95d8 519
b0b4efb9
AL
520 return !_error->PendingError();
521}
522 /*}}}*/