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