fix build-dependency parser to understand [!arch] flags...
[ntk/apt.git] / apt-pkg / deb / deblistparser.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: deblistparser.cc,v 1.28 2001/10/02 03:03:47 tausq 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 // ListParser::ParseDepends - Parse a dependency element /*{{{*/
273 // ---------------------------------------------------------------------
274 /* This parses the dependency elements out of a standard string in place,
275 bit by bit. */
276 const 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
334 const char *debListParser::ParseDepends(const char *Start,const char *Stop,
335 string &Package,string &Ver,
336 unsigned int &Op, bool ParseArchFlags)
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;
366 I = ConvertRelation(I,Op);
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
375 // Skip trailing whitespace
376 const char *End = I;
377 for (; End > Start && isspace(End[-1]); End--);
378
379 Ver = string(Start,End-Start);
380 I++;
381 }
382 else
383 {
384 Ver = string();
385 Op = pkgCache::Dep::NoOp;
386 }
387
388 // Skip whitespace
389 for (;I != Stop && isspace(*I) != 0; I++);
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 bool NegArch = false;
406 while (I != Stop)
407 {
408 // look for whitespace or ending ']'
409 while (End != Stop && !isspace(*End) && *End != ']')
410 End++;
411
412 if (End == Stop)
413 return 0;
414
415 if (*I == '!')
416 {
417 NegArch = true;
418 I++;
419 }
420
421 if (stringcmp(arch,I,End) == 0)
422 Found = true;
423
424 if (*End++ == ']') {
425 I = End;
426 break;
427 }
428
429 I = End;
430 for (;I != Stop && isspace(*I) != 0; I++);
431 }
432
433 if (NegArch)
434 Found = !Found;
435
436 if (Found == false)
437 Package = ""; /* not for this arch */
438 }
439
440 // Skip whitespace
441 for (;I != Stop && isspace(*I) != 0; I++);
442 }
443
444 if (I != Stop && *I == '|')
445 Op |= pkgCache::Dep::Or;
446
447 if (I == Stop || *I == ',' || *I == '|')
448 {
449 if (I != Stop)
450 for (I++; I != Stop && isspace(*I) != 0; I++);
451 return I;
452 }
453
454 return 0;
455 }
456 /*}}}*/
457 // ListParser::ParseDepends - Parse a dependency list /*{{{*/
458 // ---------------------------------------------------------------------
459 /* This is the higher level depends parser. It takes a tag and generates
460 a complete depends tree for the given version. */
461 bool debListParser::ParseDepends(pkgCache::VerIterator Ver,
462 const char *Tag,unsigned int Type)
463 {
464 const char *Start;
465 const char *Stop;
466 if (Section.Find(Tag,Start,Stop) == false)
467 return true;
468
469 string Package;
470 string Version;
471 unsigned int Op;
472
473 while (1)
474 {
475 Start = ParseDepends(Start,Stop,Package,Version,Op);
476 if (Start == 0)
477 return _error->Error("Problem parsing dependency %s",Tag);
478
479 if (NewDepends(Ver,Package,Version,Op,Type) == false)
480 return false;
481 if (Start == Stop)
482 break;
483 }
484 return true;
485 }
486 /*}}}*/
487 // ListParser::ParseProvides - Parse the provides list /*{{{*/
488 // ---------------------------------------------------------------------
489 /* */
490 bool debListParser::ParseProvides(pkgCache::VerIterator Ver)
491 {
492 const char *Start;
493 const char *Stop;
494 if (Section.Find("Provides",Start,Stop) == false)
495 return true;
496
497 string Package;
498 string Version;
499 unsigned int Op;
500
501 while (1)
502 {
503 Start = ParseDepends(Start,Stop,Package,Version,Op);
504 if (Start == 0)
505 return _error->Error("Problem parsing Provides line");
506 if (Op != pkgCache::Dep::NoOp)
507 return _error->Error("Malformed provides line");
508
509 if (NewProvides(Ver,Package,Version) == false)
510 return false;
511
512 if (Start == Stop)
513 break;
514 }
515
516 return true;
517 }
518 /*}}}*/
519 // ListParser::GrabWord - Matches a word and returns /*{{{*/
520 // ---------------------------------------------------------------------
521 /* Looks for a word in a list of words - for ParseStatus */
522 bool debListParser::GrabWord(string Word,WordList *List,unsigned char &Out)
523 {
524 for (unsigned int C = 0; List[C].Str != 0; C++)
525 {
526 if (strcasecmp(Word.c_str(),List[C].Str) == 0)
527 {
528 Out = List[C].Val;
529 return true;
530 }
531 }
532 return false;
533 }
534 /*}}}*/
535 // ListParser::Step - Move to the next section in the file /*{{{*/
536 // ---------------------------------------------------------------------
537 /* This has to be carefull to only process the correct architecture */
538 bool debListParser::Step()
539 {
540 iOffset = Tags.Offset();
541 while (Tags.Step(Section) == true)
542 {
543 /* See if this is the correct Architecture, if it isn't then we
544 drop the whole section. A missing arch tag only happens (in theory)
545 inside the Status file, so that is a positive return */
546 const char *Start;
547 const char *Stop;
548 if (Section.Find("Architecture",Start,Stop) == false)
549 return true;
550
551 if (stringcmp(Arch,Start,Stop) == 0)
552 return true;
553
554 if (stringcmp(Start,Stop,"all") == 0)
555 return true;
556
557 iOffset = Tags.Offset();
558 }
559 return false;
560 }
561 /*}}}*/
562 // ListParser::LoadReleaseInfo - Load the release information /*{{{*/
563 // ---------------------------------------------------------------------
564 /* */
565 bool debListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
566 FileFd &File)
567 {
568 pkgTagFile Tags(&File);
569 pkgTagSection Section;
570 if (Tags.Step(Section) == false)
571 return false;
572
573 const char *Start;
574 const char *Stop;
575 if (Section.Find("Archive",Start,Stop) == true)
576 FileI->Archive = WriteUniqString(Start,Stop - Start);
577 if (Section.Find("Component",Start,Stop) == true)
578 FileI->Component = WriteUniqString(Start,Stop - Start);
579 if (Section.Find("Version",Start,Stop) == true)
580 FileI->Version = WriteUniqString(Start,Stop - Start);
581 if (Section.Find("Origin",Start,Stop) == true)
582 FileI->Origin = WriteUniqString(Start,Stop - Start);
583 if (Section.Find("Label",Start,Stop) == true)
584 FileI->Label = WriteUniqString(Start,Stop - Start);
585 if (Section.Find("Architecture",Start,Stop) == true)
586 FileI->Architecture = WriteUniqString(Start,Stop - Start);
587
588 if (Section.FindFlag("NotAutomatic",FileI->Flags,
589 pkgCache::Flag::NotAutomatic) == false)
590 _error->Warning("Bad NotAutomatic flag");
591
592 return !_error->PendingError();
593 }
594 /*}}}*/
595 // ListParser::GetPrio - Convert the priority from a string /*{{{*/
596 // ---------------------------------------------------------------------
597 /* */
598 unsigned char debListParser::GetPrio(string Str)
599 {
600 unsigned char Out;
601 if (GrabWord(Str,PrioList,Out) == false)
602 Out = pkgCache::State::Extra;
603
604 return Out;
605 }
606 /*}}}*/