Minor bug fixes
[ntk/apt.git] / apt-pkg / deb / deblistparser.cc
CommitLineData
f55a958f
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
418a471f 3// $Id: deblistparser.cc,v 1.27 2001/07/26 06:15:59 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
e7b470ee
AL
19#include <ctype.h>
20
f55a958f
AL
21#include <system.h>
22 /*}}}*/
23
b2e465d6
AL
24static debListParser::WordList PrioList[] = {{"important",pkgCache::State::Important},
25 {"required",pkgCache::State::Required},
26 {"standard",pkgCache::State::Standard},
27 {"optional",pkgCache::State::Optional},
28 {"extra",pkgCache::State::Extra},
29 {}};
30
f55a958f
AL
31// ListParser::debListParser - Constructor /*{{{*/
32// ---------------------------------------------------------------------
33/* */
b2e465d6 34debListParser::debListParser(FileFd *File) : Tags(File)
f55a958f 35{
ddc1d8d0 36 Arch = _config->Find("APT::architecture");
0149949b
AL
37}
38 /*}}}*/
f55a958f
AL
39// ListParser::UniqFindTagWrite - Find the tag and write a unq string /*{{{*/
40// ---------------------------------------------------------------------
41/* */
42unsigned long debListParser::UniqFindTagWrite(const char *Tag)
43{
44 const char *Start;
45 const char *Stop;
46 if (Section.Find(Tag,Start,Stop) == false)
47 return 0;
48 return WriteUniqString(Start,Stop - Start);
49}
50 /*}}}*/
f55a958f
AL
51// ListParser::Package - Return the package name /*{{{*/
52// ---------------------------------------------------------------------
53/* This is to return the name of the package this section describes */
54string debListParser::Package()
55{
b0b4efb9 56 string Result = Section.FindS("Package");
f55a958f 57 if (Result.empty() == true)
65a1e968 58 _error->Error("Encountered a section with no Package: header");
f55a958f
AL
59 return Result;
60}
61 /*}}}*/
62// ListParser::Version - Return the version string /*{{{*/
63// ---------------------------------------------------------------------
64/* This is to return the string describing the version in debian form,
65 epoch:upstream-release. If this returns the blank string then the
66 entry is assumed to only describe package properties */
67string debListParser::Version()
68{
b0b4efb9 69 return Section.FindS("Version");
f55a958f
AL
70}
71 /*}}}*/
f55a958f
AL
72// ListParser::NewVersion - Fill in the version structure /*{{{*/
73// ---------------------------------------------------------------------
74/* */
75bool debListParser::NewVersion(pkgCache::VerIterator Ver)
0149949b
AL
76{
77 // Parse the section
b35d2f5f 78 Ver->Section = UniqFindTagWrite("Section");
17caf1b1 79 Ver->Arch = UniqFindTagWrite("Architecture");
0149949b
AL
80
81 // Archive Size
b0b4efb9 82 Ver->Size = (unsigned)Section.FindI("Size");
0149949b
AL
83
84 // Unpacked Size (in K)
b0b4efb9 85 Ver->InstalledSize = (unsigned)Section.FindI("Installed-Size");
0149949b
AL
86 Ver->InstalledSize *= 1024;
87
88 // Priority
89 const char *Start;
90 const char *Stop;
91 if (Section.Find("Priority",Start,Stop) == true)
b2e465d6
AL
92 {
93 if (GrabWord(string(Start,Stop-Start),PrioList,Ver->Priority) == false)
421c8d10 94 Ver->Priority = pkgCache::State::Extra;
0149949b 95 }
dcb79bae 96
6c139d6e 97 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
dcb79bae 98 return false;
8efa2a3b 99 if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
dcb79bae 100 return false;
6c139d6e 101 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
dcb79bae 102 return false;
6c139d6e 103 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
dcb79bae 104 return false;
6c139d6e 105 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
dcb79bae 106 return false;
f55ece0e 107 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false)
dcb79bae
AL
108 return false;
109
b2e465d6
AL
110 // Obsolete.
111 if (ParseDepends(Ver,"Optional",pkgCache::Dep::Suggests) == false)
112 return false;
113
dcb79bae
AL
114 if (ParseProvides(Ver) == false)
115 return false;
0149949b 116
f55a958f
AL
117 return true;
118}
119 /*}}}*/
120// ListParser::UsePackage - Update a package structure /*{{{*/
121// ---------------------------------------------------------------------
122/* This is called to update the package with any new information
123 that might be found in the section */
124bool debListParser::UsePackage(pkgCache::PkgIterator Pkg,
125 pkgCache::VerIterator Ver)
126{
127 if (Pkg->Section == 0)
b35d2f5f 128 Pkg->Section = UniqFindTagWrite("Section");
b0b4efb9 129 if (Section.FindFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
f55a958f 130 return false;
138d4b3d 131 if (Section.FindFlag("Important",Pkg->Flags,pkgCache::Flag::Important) == false)
f55a958f 132 return false;
138d4b3d
AL
133
134 if (strcmp(Pkg.Name(),"apt") == 0)
135 Pkg->Flags |= pkgCache::Flag::Important;
136
f55a958f
AL
137 if (ParseStatus(Pkg,Ver) == false)
138 return false;
139 return true;
140}
141 /*}}}*/
204fbdcc
AL
142// ListParser::VersionHash - Compute a unique hash for this version /*{{{*/
143// ---------------------------------------------------------------------
144/* */
145unsigned short debListParser::VersionHash()
146{
147 const char *Sections[] ={"Installed-Size",
148 "Depends",
149 "Pre-Depends",
f78439bf
AL
150// "Suggests",
151// "Recommends",
204fbdcc
AL
152 "Conflicts",
153 "Replaces",0};
154 unsigned long Result = INIT_FCS;
418a471f 155 char S[1024];
204fbdcc
AL
156 for (const char **I = Sections; *I != 0; I++)
157 {
158 const char *Start;
159 const char *End;
160 if (Section.Find(*I,Start,End) == false || End - Start >= (signed)sizeof(S))
161 continue;
162
163 /* Strip out any spaces from the text, this undoes dpkgs reformatting
421c8d10
AL
164 of certain fields. dpkg also has the rather interesting notion of
165 reformatting depends operators < -> <= */
204fbdcc
AL
166 char *I = S;
167 for (; Start != End; Start++)
421c8d10 168 {
204fbdcc 169 if (isspace(*Start) == 0)
ddc1d8d0 170 *I++ = tolower(*Start);
421c8d10
AL
171 if (*Start == '<' && Start[1] != '<' && Start[1] != '=')
172 *I++ = '=';
173 if (*Start == '>' && Start[1] != '>' && Start[1] != '=')
174 *I++ = '=';
175 }
418a471f 176
204fbdcc
AL
177 Result = AddCRC16(Result,S,I - S);
178 }
179
180 return Result;
181}
182 /*}}}*/
dcb79bae 183// ListParser::ParseStatus - Parse the status field /*{{{*/
f55a958f
AL
184// ---------------------------------------------------------------------
185/* Status lines are of the form,
186 Status: want flag status
187 want = unknown, install, hold, deinstall, purge
188 flag = ok, reinstreq, hold, hold-reinstreq
a005475e 189 status = not-installed, unpacked, half-configured,
f55a958f
AL
190 half-installed, config-files, post-inst-failed,
191 removal-failed, installed
192
193 Some of the above are obsolete (I think?) flag = hold-* and
194 status = post-inst-failed, removal-failed at least.
195 */
196bool debListParser::ParseStatus(pkgCache::PkgIterator Pkg,
197 pkgCache::VerIterator Ver)
198{
199 const char *Start;
200 const char *Stop;
201 if (Section.Find("Status",Start,Stop) == false)
202 return true;
203
204 // Isolate the first word
205 const char *I = Start;
206 for(; I < Stop && *I != ' '; I++);
207 if (I >= Stop || *I != ' ')
208 return _error->Error("Malformed Status line");
209
210 // Process the want field
6c139d6e
AL
211 WordList WantList[] = {{"unknown",pkgCache::State::Unknown},
212 {"install",pkgCache::State::Install},
213 {"hold",pkgCache::State::Hold},
214 {"deinstall",pkgCache::State::DeInstall},
b2e465d6
AL
215 {"purge",pkgCache::State::Purge},
216 {}};
217 if (GrabWord(string(Start,I-Start),WantList,Pkg->SelectedState) == false)
f55a958f
AL
218 return _error->Error("Malformed 1st word in the Status line");
219
220 // Isloate the next word
221 I++;
222 Start = I;
223 for(; I < Stop && *I != ' '; I++);
224 if (I >= Stop || *I != ' ')
225 return _error->Error("Malformed status line, no 2nd word");
226
227 // Process the flag field
6c139d6e
AL
228 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
229 {"reinstreq",pkgCache::State::ReInstReq},
230 {"hold",pkgCache::State::HoldInst},
b2e465d6
AL
231 {"hold-reinstreq",pkgCache::State::HoldReInstReq},
232 {}};
233 if (GrabWord(string(Start,I-Start),FlagList,Pkg->InstState) == false)
f55a958f
AL
234 return _error->Error("Malformed 2nd word in the Status line");
235
236 // Isloate the last word
237 I++;
238 Start = I;
239 for(; I < Stop && *I != ' '; I++);
240 if (I != Stop)
241 return _error->Error("Malformed Status line, no 3rd word");
242
243 // Process the flag field
6c139d6e
AL
244 WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled},
245 {"unpacked",pkgCache::State::UnPacked},
246 {"half-configured",pkgCache::State::HalfConfigured},
247 {"installed",pkgCache::State::Installed},
6c139d6e
AL
248 {"half-installed",pkgCache::State::HalfInstalled},
249 {"config-files",pkgCache::State::ConfigFiles},
250 {"post-inst-failed",pkgCache::State::HalfConfigured},
b2e465d6
AL
251 {"removal-failed",pkgCache::State::HalfInstalled},
252 {}};
253 if (GrabWord(string(Start,I-Start),StatusList,Pkg->CurrentState) == false)
f55a958f
AL
254 return _error->Error("Malformed 3rd word in the Status line");
255
256 /* A Status line marks the package as indicating the current
257 version as well. Only if it is actually installed.. Otherwise
258 the interesting dpkg handling of the status file creates bogus
259 entries. */
6c139d6e
AL
260 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
261 Pkg->CurrentState == pkgCache::State::ConfigFiles))
f55a958f
AL
262 {
263 if (Ver.end() == true)
264 _error->Warning("Encountered status field in a non-version description");
265 else
266 Pkg->CurrentVer = Ver.Index();
267 }
268
dcb79bae
AL
269 return true;
270}
271 /*}}}*/
272// ListParser::ParseDepends - Parse a dependency element /*{{{*/
273// ---------------------------------------------------------------------
274/* This parses the dependency elements out of a standard string in place,
275 bit by bit. */
b2e465d6
AL
276const char *debListParser::ConvertRelation(const char *I,unsigned int &Op)
277{
278 // Determine the operator
279 switch (*I)
280 {
281 case '<':
282 I++;
283 if (*I == '=')
284 {
285 I++;
286 Op = pkgCache::Dep::LessEq;
287 break;
288 }
289
290 if (*I == '<')
291 {
292 I++;
293 Op = pkgCache::Dep::Less;
294 break;
295 }
296
297 // < is the same as <= and << is really Cs < for some reason
298 Op = pkgCache::Dep::LessEq;
299 break;
300
301 case '>':
302 I++;
303 if (*I == '=')
304 {
305 I++;
306 Op = pkgCache::Dep::GreaterEq;
307 break;
308 }
309
310 if (*I == '>')
311 {
312 I++;
313 Op = pkgCache::Dep::Greater;
314 break;
315 }
316
317 // > is the same as >= and >> is really Cs > for some reason
318 Op = pkgCache::Dep::GreaterEq;
319 break;
320
321 case '=':
322 Op = pkgCache::Dep::Equals;
323 I++;
324 break;
325
326 // HACK around bad package definitions
327 default:
328 Op = pkgCache::Dep::Equals;
329 break;
330 }
331 return I;
332}
333
dcb79bae
AL
334const char *debListParser::ParseDepends(const char *Start,const char *Stop,
335 string &Package,string &Ver,
b2e465d6 336 unsigned int &Op, bool ParseArchFlags)
dcb79bae
AL
337{
338 // Strip off leading space
339 for (;Start != Stop && isspace(*Start) != 0; Start++);
340
341 // Parse off the package name
342 const char *I = Start;
343 for (;I != Stop && isspace(*I) == 0 && *I != '(' && *I != ')' &&
344 *I != ',' && *I != '|'; I++);
345
346 // Malformed, no '('
347 if (I != Stop && *I == ')')
348 return 0;
349
350 if (I == Start)
351 return 0;
352
353 // Stash the package name
354 Package.assign(Start,I - Start);
355
356 // Skip white space to the '('
357 for (;I != Stop && isspace(*I) != 0 ; I++);
358
359 // Parse a version
360 if (I != Stop && *I == '(')
361 {
362 // Skip the '('
363 for (I++; I != Stop && isspace(*I) != 0 ; I++);
364 if (I + 3 >= Stop)
365 return 0;
b2e465d6 366 I = ConvertRelation(I,Op);
dcb79bae
AL
367
368 // Skip whitespace
369 for (;I != Stop && isspace(*I) != 0; I++);
370 Start = I;
371 for (;I != Stop && *I != ')'; I++);
372 if (I == Stop || Start == I)
373 return 0;
374
02f000a9
AL
375 // Skip trailing whitespace
376 const char *End = I;
377 for (; End > Start && isspace(End[-1]); End--);
378
379 Ver = string(Start,End-Start);
dcb79bae
AL
380 I++;
381 }
382 else
383 {
384 Ver = string();
6c139d6e 385 Op = pkgCache::Dep::NoOp;
dcb79bae
AL
386 }
387
388 // Skip whitespace
389 for (;I != Stop && isspace(*I) != 0; I++);
b2e465d6
AL
390
391 if (ParseArchFlags == true)
392 {
393 string arch = _config->Find("APT::Architecture");
394
395 // Parse an architecture
396 if (I != Stop && *I == '[')
397 {
398 // malformed
399 I++;
400 if (I == Stop)
401 return 0;
402
403 const char *End = I;
404 bool Found = false;
405 while (I != Stop)
406 {
407 // look for whitespace or ending ']'
408 while (End != Stop && !isspace(*End) && *End != ']')
409 End++;
410
411 if (End == Stop)
412 return 0;
413
3e18cc75 414 if (stringcmp(arch,I,End) == 0)
b2e465d6
AL
415 Found = true;
416
417 if (*End++ == ']') {
418 I = End;
419 break;
420 }
421
422 I = End;
423 for (;I != Stop && isspace(*I) != 0; I++);
424 }
425
426 if (Found == false)
427 Package = ""; /* not for this arch */
428 }
429
430 // Skip whitespace
431 for (;I != Stop && isspace(*I) != 0; I++);
432 }
433
dcb79bae 434 if (I != Stop && *I == '|')
6c139d6e 435 Op |= pkgCache::Dep::Or;
dcb79bae
AL
436
437 if (I == Stop || *I == ',' || *I == '|')
438 {
439 if (I != Stop)
440 for (I++; I != Stop && isspace(*I) != 0; I++);
441 return I;
442 }
443
444 return 0;
445}
446 /*}}}*/
447// ListParser::ParseDepends - Parse a dependency list /*{{{*/
448// ---------------------------------------------------------------------
449/* This is the higher level depends parser. It takes a tag and generates
450 a complete depends tree for the given version. */
451bool debListParser::ParseDepends(pkgCache::VerIterator Ver,
452 const char *Tag,unsigned int Type)
453{
454 const char *Start;
455 const char *Stop;
456 if (Section.Find(Tag,Start,Stop) == false)
457 return true;
458
459 string Package;
460 string Version;
461 unsigned int Op;
462
8efa2a3b 463 while (1)
dcb79bae 464 {
8efa2a3b 465 Start = ParseDepends(Start,Stop,Package,Version,Op);
dcb79bae
AL
466 if (Start == 0)
467 return _error->Error("Problem parsing dependency %s",Tag);
8efa2a3b 468
dcb79bae
AL
469 if (NewDepends(Ver,Package,Version,Op,Type) == false)
470 return false;
8efa2a3b
AL
471 if (Start == Stop)
472 break;
dcb79bae
AL
473 }
474 return true;
475}
476 /*}}}*/
477// ListParser::ParseProvides - Parse the provides list /*{{{*/
478// ---------------------------------------------------------------------
479/* */
480bool debListParser::ParseProvides(pkgCache::VerIterator Ver)
481{
482 const char *Start;
483 const char *Stop;
484 if (Section.Find("Provides",Start,Stop) == false)
485 return true;
486
487 string Package;
488 string Version;
489 unsigned int Op;
490
491 while (1)
492 {
493 Start = ParseDepends(Start,Stop,Package,Version,Op);
494 if (Start == 0)
495 return _error->Error("Problem parsing Provides line");
6c139d6e 496 if (Op != pkgCache::Dep::NoOp)
dcb79bae
AL
497 return _error->Error("Malformed provides line");
498
499 if (NewProvides(Ver,Package,Version) == false)
500 return false;
501
502 if (Start == Stop)
503 break;
504 }
505
f55a958f
AL
506 return true;
507}
508 /*}}}*/
509// ListParser::GrabWord - Matches a word and returns /*{{{*/
510// ---------------------------------------------------------------------
511/* Looks for a word in a list of words - for ParseStatus */
b2e465d6 512bool debListParser::GrabWord(string Word,WordList *List,unsigned char &Out)
f55a958f 513{
b2e465d6 514 for (unsigned int C = 0; List[C].Str != 0; C++)
f55a958f
AL
515 {
516 if (strcasecmp(Word.c_str(),List[C].Str) == 0)
517 {
518 Out = List[C].Val;
519 return true;
520 }
521 }
522 return false;
523}
524 /*}}}*/
525// ListParser::Step - Move to the next section in the file /*{{{*/
526// ---------------------------------------------------------------------
0149949b 527/* This has to be carefull to only process the correct architecture */
f55a958f
AL
528bool debListParser::Step()
529{
dcb79bae 530 iOffset = Tags.Offset();
0149949b 531 while (Tags.Step(Section) == true)
ddc1d8d0
AL
532 {
533 /* See if this is the correct Architecture, if it isn't then we
534 drop the whole section. A missing arch tag only happens (in theory)
535 inside the Status file, so that is a positive return */
0149949b
AL
536 const char *Start;
537 const char *Stop;
538 if (Section.Find("Architecture",Start,Stop) == false)
539 return true;
9c14e3d6 540
3e18cc75 541 if (stringcmp(Arch,Start,Stop) == 0)
0149949b
AL
542 return true;
543
9c14e3d6 544 if (stringcmp(Start,Stop,"all") == 0)
0149949b 545 return true;
dcb79bae
AL
546
547 iOffset = Tags.Offset();
0149949b
AL
548 }
549 return false;
f55a958f
AL
550}
551 /*}}}*/
b0b4efb9
AL
552// ListParser::LoadReleaseInfo - Load the release information /*{{{*/
553// ---------------------------------------------------------------------
554/* */
555bool debListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
556 FileFd &File)
557{
b2e465d6 558 pkgTagFile Tags(&File);
b0b4efb9
AL
559 pkgTagSection Section;
560 if (Tags.Step(Section) == false)
561 return false;
562
563 const char *Start;
564 const char *Stop;
565 if (Section.Find("Archive",Start,Stop) == true)
566 FileI->Archive = WriteUniqString(Start,Stop - Start);
567 if (Section.Find("Component",Start,Stop) == true)
568 FileI->Component = WriteUniqString(Start,Stop - Start);
569 if (Section.Find("Version",Start,Stop) == true)
570 FileI->Version = WriteUniqString(Start,Stop - Start);
571 if (Section.Find("Origin",Start,Stop) == true)
572 FileI->Origin = WriteUniqString(Start,Stop - Start);
573 if (Section.Find("Label",Start,Stop) == true)
574 FileI->Label = WriteUniqString(Start,Stop - Start);
575 if (Section.Find("Architecture",Start,Stop) == true)
576 FileI->Architecture = WriteUniqString(Start,Stop - Start);
0dbb95d8 577
3c124dde
AL
578 if (Section.FindFlag("NotAutomatic",FileI->Flags,
579 pkgCache::Flag::NotAutomatic) == false)
0dbb95d8 580 _error->Warning("Bad NotAutomatic flag");
0dbb95d8 581
b0b4efb9
AL
582 return !_error->PendingError();
583}
584 /*}}}*/
b2e465d6
AL
585// ListParser::GetPrio - Convert the priority from a string /*{{{*/
586// ---------------------------------------------------------------------
587/* */
588unsigned char debListParser::GetPrio(string Str)
589{
590 unsigned char Out;
591 if (GrabWord(Str,PrioList,Out) == false)
592 Out = pkgCache::State::Extra;
593
594 return Out;
595}
596 /*}}}*/