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