]> git.saurik.com Git - apt.git/blob - apt-pkg/deb/deblistparser.cc
* fixed apt-pkg/acquire-item.h (looks like the last commit didn't include it?)
[apt.git] / apt-pkg / deb / deblistparser.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: deblistparser.cc,v 1.29.2.5 2004/01/06 01:43:44 mdz 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 #include <apt-pkg/crc-16.h>
18
19 #include <ctype.h>
20
21 #include <system.h>
22 /*}}}*/
23
24 static 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
31 // ListParser::debListParser - Constructor /*{{{*/
32 // ---------------------------------------------------------------------
33 /* */
34 debListParser::debListParser(FileFd *File) : Tags(File)
35 {
36 Arch = _config->Find("APT::architecture");
37 }
38 /*}}}*/
39 // ListParser::UniqFindTagWrite - Find the tag and write a unq string /*{{{*/
40 // ---------------------------------------------------------------------
41 /* */
42 unsigned 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 /*}}}*/
51 // ListParser::Package - Return the package name /*{{{*/
52 // ---------------------------------------------------------------------
53 /* This is to return the name of the package this section describes */
54 string debListParser::Package()
55 {
56 string Result = Section.FindS("Package");
57 if (Result.empty() == true)
58 _error->Error("Encountered a section with no Package: header");
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 */
67 string debListParser::Version()
68 {
69 return Section.FindS("Version");
70 }
71 /*}}}*/
72 // ListParser::NewVersion - Fill in the version structure /*{{{*/
73 // ---------------------------------------------------------------------
74 /* */
75 bool debListParser::NewVersion(pkgCache::VerIterator Ver)
76 {
77 // Parse the section
78 Ver->Section = UniqFindTagWrite("Section");
79 Ver->Arch = UniqFindTagWrite("Architecture");
80
81 // Archive Size
82 Ver->Size = (unsigned)Section.FindI("Size");
83
84 // Unpacked Size (in K)
85 Ver->InstalledSize = (unsigned)Section.FindI("Installed-Size");
86 Ver->InstalledSize *= 1024;
87
88 // Priority
89 const char *Start;
90 const char *Stop;
91 if (Section.Find("Priority",Start,Stop) == true)
92 {
93 if (GrabWord(string(Start,Stop-Start),PrioList,Ver->Priority) == false)
94 Ver->Priority = pkgCache::State::Extra;
95 }
96
97 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
98 return false;
99 if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
100 return false;
101 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
102 return false;
103 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
104 return false;
105 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
106 return false;
107 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false)
108 return false;
109
110 // Obsolete.
111 if (ParseDepends(Ver,"Optional",pkgCache::Dep::Suggests) == false)
112 return false;
113
114 if (ParseProvides(Ver) == false)
115 return false;
116
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 */
124 bool debListParser::UsePackage(pkgCache::PkgIterator Pkg,
125 pkgCache::VerIterator Ver)
126 {
127 if (Pkg->Section == 0)
128 Pkg->Section = UniqFindTagWrite("Section");
129 if (Section.FindFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
130 return false;
131 if (Section.FindFlag("Important",Pkg->Flags,pkgCache::Flag::Important) == false)
132 return false;
133
134 if (strcmp(Pkg.Name(),"apt") == 0)
135 Pkg->Flags |= pkgCache::Flag::Important;
136
137 if (ParseStatus(Pkg,Ver) == false)
138 return false;
139 return true;
140 }
141 /*}}}*/
142 // ListParser::VersionHash - Compute a unique hash for this version /*{{{*/
143 // ---------------------------------------------------------------------
144 /* */
145 unsigned short debListParser::VersionHash()
146 {
147 const char *Sections[] ={"Installed-Size",
148 "Depends",
149 "Pre-Depends",
150 // "Suggests",
151 // "Recommends",
152 "Conflicts",
153 "Replaces",0};
154 unsigned long Result = INIT_FCS;
155 char S[1024];
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
164 of certain fields. dpkg also has the rather interesting notion of
165 reformatting depends operators < -> <= */
166 char *I = S;
167 for (; Start != End; Start++)
168 {
169 if (isspace(*Start) == 0)
170 *I++ = tolower(*Start);
171 if (*Start == '<' && Start[1] != '<' && Start[1] != '=')
172 *I++ = '=';
173 if (*Start == '>' && Start[1] != '>' && Start[1] != '=')
174 *I++ = '=';
175 }
176
177 Result = AddCRC16(Result,S,I - S);
178 }
179
180 return Result;
181 }
182 /*}}}*/
183 // ListParser::ParseStatus - Parse the status field /*{{{*/
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
189 status = not-installed, unpacked, half-configured,
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 */
196 bool 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
211 WordList WantList[] = {{"unknown",pkgCache::State::Unknown},
212 {"install",pkgCache::State::Install},
213 {"hold",pkgCache::State::Hold},
214 {"deinstall",pkgCache::State::DeInstall},
215 {"purge",pkgCache::State::Purge},
216 {}};
217 if (GrabWord(string(Start,I-Start),WantList,Pkg->SelectedState) == false)
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
228 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
229 {"reinstreq",pkgCache::State::ReInstReq},
230 {"hold",pkgCache::State::HoldInst},
231 {"hold-reinstreq",pkgCache::State::HoldReInstReq},
232 {}};
233 if (GrabWord(string(Start,I-Start),FlagList,Pkg->InstState) == false)
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
244 WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled},
245 {"unpacked",pkgCache::State::UnPacked},
246 {"half-configured",pkgCache::State::HalfConfigured},
247 {"installed",pkgCache::State::Installed},
248 {"half-installed",pkgCache::State::HalfInstalled},
249 {"config-files",pkgCache::State::ConfigFiles},
250 {"post-inst-failed",pkgCache::State::HalfConfigured},
251 {"removal-failed",pkgCache::State::HalfInstalled},
252 {}};
253 if (GrabWord(string(Start,I-Start),StatusList,Pkg->CurrentState) == false)
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. */
260 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
261 Pkg->CurrentState == pkgCache::State::ConfigFiles))
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
269 return true;
270 }
271
272 const char *debListParser::ConvertRelation(const char *I,unsigned int &Op)
273 {
274 // Determine the operator
275 switch (*I)
276 {
277 case '<':
278 I++;
279 if (*I == '=')
280 {
281 I++;
282 Op = pkgCache::Dep::LessEq;
283 break;
284 }
285
286 if (*I == '<')
287 {
288 I++;
289 Op = pkgCache::Dep::Less;
290 break;
291 }
292
293 // < is the same as <= and << is really Cs < for some reason
294 Op = pkgCache::Dep::LessEq;
295 break;
296
297 case '>':
298 I++;
299 if (*I == '=')
300 {
301 I++;
302 Op = pkgCache::Dep::GreaterEq;
303 break;
304 }
305
306 if (*I == '>')
307 {
308 I++;
309 Op = pkgCache::Dep::Greater;
310 break;
311 }
312
313 // > is the same as >= and >> is really Cs > for some reason
314 Op = pkgCache::Dep::GreaterEq;
315 break;
316
317 case '=':
318 Op = pkgCache::Dep::Equals;
319 I++;
320 break;
321
322 // HACK around bad package definitions
323 default:
324 Op = pkgCache::Dep::Equals;
325 break;
326 }
327 return I;
328 }
329
330 /*}}}*/
331 // ListParser::ParseDepends - Parse a dependency element /*{{{*/
332 // ---------------------------------------------------------------------
333 /* This parses the dependency elements out of a standard string in place,
334 bit by bit. */
335 const char *debListParser::ParseDepends(const char *Start,const char *Stop,
336 string &Package,string &Ver,
337 unsigned int &Op, bool ParseArchFlags)
338 {
339 // Strip off leading space
340 for (;Start != Stop && isspace(*Start) != 0; Start++);
341
342 // Parse off the package name
343 const char *I = Start;
344 for (;I != Stop && isspace(*I) == 0 && *I != '(' && *I != ')' &&
345 *I != ',' && *I != '|'; I++);
346
347 // Malformed, no '('
348 if (I != Stop && *I == ')')
349 return 0;
350
351 if (I == Start)
352 return 0;
353
354 // Stash the package name
355 Package.assign(Start,I - Start);
356
357 // Skip white space to the '('
358 for (;I != Stop && isspace(*I) != 0 ; I++);
359
360 // Parse a version
361 if (I != Stop && *I == '(')
362 {
363 // Skip the '('
364 for (I++; I != Stop && isspace(*I) != 0 ; I++);
365 if (I + 3 >= Stop)
366 return 0;
367 I = ConvertRelation(I,Op);
368
369 // Skip whitespace
370 for (;I != Stop && isspace(*I) != 0; I++);
371 Start = I;
372 for (;I != Stop && *I != ')'; I++);
373 if (I == Stop || Start == I)
374 return 0;
375
376 // Skip trailing whitespace
377 const char *End = I;
378 for (; End > Start && isspace(End[-1]); End--);
379
380 Ver = string(Start,End-Start);
381 I++;
382 }
383 else
384 {
385 Ver = string();
386 Op = pkgCache::Dep::NoOp;
387 }
388
389 // Skip whitespace
390 for (;I != Stop && isspace(*I) != 0; I++);
391
392 if (ParseArchFlags == true)
393 {
394 string arch = _config->Find("APT::Architecture");
395
396 // Parse an architecture
397 if (I != Stop && *I == '[')
398 {
399 // malformed
400 I++;
401 if (I == Stop)
402 return 0;
403
404 const char *End = I;
405 bool Found = false;
406 bool NegArch = false;
407 while (I != Stop)
408 {
409 // look for whitespace or ending ']'
410 while (End != Stop && !isspace(*End) && *End != ']')
411 End++;
412
413 if (End == Stop)
414 return 0;
415
416 if (*I == '!')
417 {
418 NegArch = true;
419 I++;
420 }
421
422 if (stringcmp(arch,I,End) == 0)
423 Found = true;
424
425 if (*End++ == ']') {
426 I = End;
427 break;
428 }
429
430 I = End;
431 for (;I != Stop && isspace(*I) != 0; I++);
432 }
433
434 if (NegArch)
435 Found = !Found;
436
437 if (Found == false)
438 Package = ""; /* not for this arch */
439 }
440
441 // Skip whitespace
442 for (;I != Stop && isspace(*I) != 0; I++);
443 }
444
445 if (I != Stop && *I == '|')
446 Op |= pkgCache::Dep::Or;
447
448 if (I == Stop || *I == ',' || *I == '|')
449 {
450 if (I != Stop)
451 for (I++; I != Stop && isspace(*I) != 0; I++);
452 return I;
453 }
454
455 return 0;
456 }
457 /*}}}*/
458 // ListParser::ParseDepends - Parse a dependency list /*{{{*/
459 // ---------------------------------------------------------------------
460 /* This is the higher level depends parser. It takes a tag and generates
461 a complete depends tree for the given version. */
462 bool debListParser::ParseDepends(pkgCache::VerIterator Ver,
463 const char *Tag,unsigned int Type)
464 {
465 const char *Start;
466 const char *Stop;
467 if (Section.Find(Tag,Start,Stop) == false)
468 return true;
469
470 string Package;
471 string Version;
472 unsigned int Op;
473
474 while (1)
475 {
476 Start = ParseDepends(Start,Stop,Package,Version,Op);
477 if (Start == 0)
478 return _error->Error("Problem parsing dependency %s",Tag);
479
480 if (NewDepends(Ver,Package,Version,Op,Type) == false)
481 return false;
482 if (Start == Stop)
483 break;
484 }
485 return true;
486 }
487 /*}}}*/
488 // ListParser::ParseProvides - Parse the provides list /*{{{*/
489 // ---------------------------------------------------------------------
490 /* */
491 bool debListParser::ParseProvides(pkgCache::VerIterator Ver)
492 {
493 const char *Start;
494 const char *Stop;
495 if (Section.Find("Provides",Start,Stop) == false)
496 return true;
497
498 string Package;
499 string Version;
500 unsigned int Op;
501
502 while (1)
503 {
504 Start = ParseDepends(Start,Stop,Package,Version,Op);
505 if (Start == 0)
506 return _error->Error("Problem parsing Provides line");
507 if (Op != pkgCache::Dep::NoOp)
508 return _error->Error("Malformed provides line");
509
510 if (NewProvides(Ver,Package,Version) == false)
511 return false;
512
513 if (Start == Stop)
514 break;
515 }
516
517 return true;
518 }
519 /*}}}*/
520 // ListParser::GrabWord - Matches a word and returns /*{{{*/
521 // ---------------------------------------------------------------------
522 /* Looks for a word in a list of words - for ParseStatus */
523 bool debListParser::GrabWord(string Word,WordList *List,unsigned char &Out)
524 {
525 for (unsigned int C = 0; List[C].Str != 0; C++)
526 {
527 if (strcasecmp(Word.c_str(),List[C].Str) == 0)
528 {
529 Out = List[C].Val;
530 return true;
531 }
532 }
533 return false;
534 }
535 /*}}}*/
536 // ListParser::Step - Move to the next section in the file /*{{{*/
537 // ---------------------------------------------------------------------
538 /* This has to be carefull to only process the correct architecture */
539 bool debListParser::Step()
540 {
541 iOffset = Tags.Offset();
542 while (Tags.Step(Section) == true)
543 {
544 /* See if this is the correct Architecture, if it isn't then we
545 drop the whole section. A missing arch tag only happens (in theory)
546 inside the Status file, so that is a positive return */
547 const char *Start;
548 const char *Stop;
549 if (Section.Find("Architecture",Start,Stop) == false)
550 return true;
551
552 if (stringcmp(Arch,Start,Stop) == 0)
553 return true;
554
555 if (stringcmp(Start,Stop,"all") == 0)
556 return true;
557
558 iOffset = Tags.Offset();
559 }
560 return false;
561 }
562 /*}}}*/
563 // ListParser::LoadReleaseInfo - Load the release information /*{{{*/
564 // ---------------------------------------------------------------------
565 /* */
566 bool debListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
567 FileFd &File, string component)
568 {
569 pkgTagFile Tags(&File, File.Size() + 256); // XXX
570 pkgTagSection Section;
571 if (Tags.Step(Section) == false)
572 return false;
573
574 //mvo: I don't think we need to fill that in (it's unused since apt-0.6)
575 //FileI->Architecture = WriteUniqString(Arch);
576
577 // apt-secure does no longer download individual (per-section) Release
578 // file. to provide Component pinning we use the section name now
579 FileI->Component = WriteUniqString(component);
580
581 const char *Start;
582 const char *Stop;
583 if (Section.Find("Suite",Start,Stop) == true)
584 FileI->Archive = WriteUniqString(Start,Stop - Start);
585 if (Section.Find("Component",Start,Stop) == true)
586 FileI->Component = WriteUniqString(Start,Stop - Start);
587 if (Section.Find("Version",Start,Stop) == true)
588 FileI->Version = WriteUniqString(Start,Stop - Start);
589 if (Section.Find("Origin",Start,Stop) == true)
590 FileI->Origin = WriteUniqString(Start,Stop - Start);
591 if (Section.Find("Label",Start,Stop) == true)
592 FileI->Label = WriteUniqString(Start,Stop - Start);
593 if (Section.Find("Architecture",Start,Stop) == true)
594 FileI->Architecture = WriteUniqString(Start,Stop - Start);
595
596 if (Section.FindFlag("NotAutomatic",FileI->Flags,
597 pkgCache::Flag::NotAutomatic) == false)
598 _error->Warning("Bad NotAutomatic flag");
599
600 return !_error->PendingError();
601 }
602 /*}}}*/
603 // ListParser::GetPrio - Convert the priority from a string /*{{{*/
604 // ---------------------------------------------------------------------
605 /* */
606 unsigned char debListParser::GetPrio(string Str)
607 {
608 unsigned char Out;
609 if (GrabWord(Str,PrioList,Out) == false)
610 Out = pkgCache::State::Extra;
611
612 return Out;
613 }
614 /*}}}*/