]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/deb/deblistparser.cc
Newer sub/guess scripts
[apt.git] / apt-pkg / deb / deblistparser.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: deblistparser.cc,v 1.24 2001/02/20 07:03:17 jgg 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 <system.h>
20 /*}}}*/
21
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
29// ListParser::debListParser - Constructor /*{{{*/
30// ---------------------------------------------------------------------
31/* */
32debListParser::debListParser(FileFd *File) : Tags(File)
33{
34 Arch = _config->Find("APT::architecture");
35}
36 /*}}}*/
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 /*}}}*/
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{
54 string Result = Section.FindS("Package");
55 if (Result.empty() == true)
56 _error->Error("Encountered a section with no Package: header");
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{
67 return Section.FindS("Version");
68}
69 /*}}}*/
70// ListParser::NewVersion - Fill in the version structure /*{{{*/
71// ---------------------------------------------------------------------
72/* */
73bool debListParser::NewVersion(pkgCache::VerIterator Ver)
74{
75 // Parse the section
76 Ver->Section = UniqFindTagWrite("Section");
77 Ver->Arch = UniqFindTagWrite("Architecture");
78
79 // Archive Size
80 Ver->Size = (unsigned)Section.FindI("Size");
81
82 // Unpacked Size (in K)
83 Ver->InstalledSize = (unsigned)Section.FindI("Installed-Size");
84 Ver->InstalledSize *= 1024;
85
86 // Priority
87 const char *Start;
88 const char *Stop;
89 if (Section.Find("Priority",Start,Stop) == true)
90 {
91 if (GrabWord(string(Start,Stop-Start),PrioList,Ver->Priority) == false)
92 Ver->Priority = pkgCache::State::Extra;
93 }
94
95 if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false)
96 return false;
97 if (ParseDepends(Ver,"Pre-Depends",pkgCache::Dep::PreDepends) == false)
98 return false;
99 if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false)
100 return false;
101 if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false)
102 return false;
103 if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false)
104 return false;
105 if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false)
106 return false;
107
108 // Obsolete.
109 if (ParseDepends(Ver,"Optional",pkgCache::Dep::Suggests) == false)
110 return false;
111
112 if (ParseProvides(Ver) == false)
113 return false;
114
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)
126 Pkg->Section = UniqFindTagWrite("Section");
127 if (Section.FindFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false)
128 return false;
129 if (Section.FindFlag("Important",Pkg->Flags,pkgCache::Flag::Important) == false)
130 return false;
131
132 if (strcmp(Pkg.Name(),"apt") == 0)
133 Pkg->Flags |= pkgCache::Flag::Important;
134
135 if (ParseStatus(Pkg,Ver) == false)
136 return false;
137 return true;
138}
139 /*}}}*/
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",
148// "Suggests",
149// "Recommends",
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
162 of certain fields. dpkg also has the rather interesting notion of
163 reformatting depends operators < -> <= */
164 char *I = S;
165 for (; Start != End; Start++)
166 {
167 if (isspace(*Start) == 0)
168 *I++ = tolower(*Start);
169 if (*Start == '<' && Start[1] != '<' && Start[1] != '=')
170 *I++ = '=';
171 if (*Start == '>' && Start[1] != '>' && Start[1] != '=')
172 *I++ = '=';
173 }
174
175 Result = AddCRC16(Result,S,I - S);
176 }
177
178 return Result;
179}
180 /*}}}*/
181// ListParser::ParseStatus - Parse the status field /*{{{*/
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
187 status = not-installed, unpacked, half-configured,
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
209 WordList WantList[] = {{"unknown",pkgCache::State::Unknown},
210 {"install",pkgCache::State::Install},
211 {"hold",pkgCache::State::Hold},
212 {"deinstall",pkgCache::State::DeInstall},
213 {"purge",pkgCache::State::Purge},
214 {}};
215 if (GrabWord(string(Start,I-Start),WantList,Pkg->SelectedState) == false)
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
226 WordList FlagList[] = {{"ok",pkgCache::State::Ok},
227 {"reinstreq",pkgCache::State::ReInstReq},
228 {"hold",pkgCache::State::HoldInst},
229 {"hold-reinstreq",pkgCache::State::HoldReInstReq},
230 {}};
231 if (GrabWord(string(Start,I-Start),FlagList,Pkg->InstState) == false)
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
242 WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled},
243 {"unpacked",pkgCache::State::UnPacked},
244 {"half-configured",pkgCache::State::HalfConfigured},
245 {"installed",pkgCache::State::Installed},
246 {"half-installed",pkgCache::State::HalfInstalled},
247 {"config-files",pkgCache::State::ConfigFiles},
248 {"post-inst-failed",pkgCache::State::HalfConfigured},
249 {"removal-failed",pkgCache::State::HalfInstalled},
250 {}};
251 if (GrabWord(string(Start,I-Start),StatusList,Pkg->CurrentState) == false)
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. */
258 if (!(Pkg->CurrentState == pkgCache::State::NotInstalled ||
259 Pkg->CurrentState == pkgCache::State::ConfigFiles))
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
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. */
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
332const char *debListParser::ParseDepends(const char *Start,const char *Stop,
333 string &Package,string &Ver,
334 unsigned int &Op, bool ParseArchFlags)
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;
364 I = ConvertRelation(I,Op);
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
373 // Skip trailing whitespace
374 const char *End = I;
375 for (; End > Start && isspace(End[-1]); End--);
376
377 Ver = string(Start,End-Start);
378 I++;
379 }
380 else
381 {
382 Ver = string();
383 Op = pkgCache::Dep::NoOp;
384 }
385
386 // Skip whitespace
387 for (;I != Stop && isspace(*I) != 0; I++);
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
432 if (I != Stop && *I == '|')
433 Op |= pkgCache::Dep::Or;
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
461 while (1)
462 {
463 Start = ParseDepends(Start,Stop,Package,Version,Op);
464 if (Start == 0)
465 return _error->Error("Problem parsing dependency %s",Tag);
466
467 if (NewDepends(Ver,Package,Version,Op,Type) == false)
468 return false;
469 if (Start == Stop)
470 break;
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");
494 if (Op != pkgCache::Dep::NoOp)
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
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 */
510bool debListParser::GrabWord(string Word,WordList *List,unsigned char &Out)
511{
512 for (unsigned int C = 0; List[C].Str != 0; C++)
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// ---------------------------------------------------------------------
525/* This has to be carefull to only process the correct architecture */
526bool debListParser::Step()
527{
528 iOffset = Tags.Offset();
529 while (Tags.Step(Section) == true)
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 */
534 const char *Start;
535 const char *Stop;
536 if (Section.Find("Architecture",Start,Stop) == false)
537 return true;
538
539 if (stringcmp(Start,Stop,Arch.begin(),Arch.end()) == 0)
540 return true;
541
542 if (stringcmp(Start,Stop,"all") == 0)
543 return true;
544
545 iOffset = Tags.Offset();
546 }
547 return false;
548}
549 /*}}}*/
550// ListParser::LoadReleaseInfo - Load the release information /*{{{*/
551// ---------------------------------------------------------------------
552/* */
553bool debListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
554 FileFd &File)
555{
556 pkgTagFile Tags(&File);
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);
575
576 if (Section.FindFlag("NotAutomatic",FileI->Flags,
577 pkgCache::Flag::NotAutomatic) == false)
578 _error->Warning("Bad NotAutomatic flag");
579
580 return !_error->PendingError();
581}
582 /*}}}*/
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 /*}}}*/