]> git.saurik.com Git - apt.git/blame - apt-pkg/tagfile.cc
G++3 fixes from Randolph
[apt.git] / apt-pkg / tagfile.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
f604cf55 3// $Id: tagfile.cc,v 1.29 2001/04/22 05:42:52 jgg Exp $
578bfd0a
AL
4/* ######################################################################
5
6 Fast scanner for RFC-822 type header information
7
ad00ae81 8 This uses a rotating buffer to load the package information into.
578bfd0a
AL
9 The scanner runs over it and isolates and indexes a single section.
10
11 ##################################################################### */
12 /*}}}*/
13// Include Files /*{{{*/
6c139d6e 14#ifdef __GNUG__
094a497d 15#pragma implementation "apt-pkg/tagfile.h"
6c139d6e
AL
16#endif
17
094a497d
AL
18#include <apt-pkg/tagfile.h>
19#include <apt-pkg/error.h>
cdcc6d34 20#include <apt-pkg/strutl.h>
578bfd0a 21
b2e465d6
AL
22#include <apti18n.h>
23
578bfd0a
AL
24#include <string>
25#include <stdio.h>
26 /*}}}*/
27
28// TagFile::pkgTagFile - Constructor /*{{{*/
29// ---------------------------------------------------------------------
30/* */
b2e465d6 31pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long Size) : Fd(*pFd), Size(Size)
578bfd0a 32{
0e72dd52
AL
33 if (Fd.IsOpen() == false)
34 {
35 Buffer = 0;
36 Start = End = Buffer = 0;
f604cf55 37 Done = true;
0e72dd52
AL
38 iOffset = 0;
39 return;
40 }
41
ad00ae81
AL
42 Buffer = new char[Size];
43 Start = End = Buffer;
f604cf55 44 Done = false;
dcb79bae 45 iOffset = 0;
578bfd0a
AL
46 Fill();
47}
48 /*}}}*/
b2e465d6 49// TagFile::~pkgTagFile - Destructor /*{{{*/
29f7b36c
AL
50// ---------------------------------------------------------------------
51/* */
52pkgTagFile::~pkgTagFile()
53{
54 delete [] Buffer;
55}
56 /*}}}*/
578bfd0a
AL
57// TagFile::Step - Advance to the next section /*{{{*/
58// ---------------------------------------------------------------------
59/* If the Section Scanner fails we refill the buffer and try again. */
60bool pkgTagFile::Step(pkgTagSection &Tag)
61{
62 if (Tag.Scan(Start,End - Start) == false)
63 {
64 if (Fill() == false)
65 return false;
66
67 if (Tag.Scan(Start,End - Start) == false)
b2e465d6 68 return _error->Error(_("Unable to parse package file %s (1)"),Fd.Name().c_str());
578bfd0a 69 }
dcb79bae
AL
70 Start += Tag.size();
71 iOffset += Tag.size();
b2e465d6
AL
72
73 Tag.Trim();
dcb79bae 74
578bfd0a
AL
75 return true;
76}
77 /*}}}*/
78// TagFile::Fill - Top up the buffer /*{{{*/
79// ---------------------------------------------------------------------
80/* This takes the bit at the end of the buffer and puts it at the start
81 then fills the rest from the file */
82bool pkgTagFile::Fill()
83{
ad00ae81 84 unsigned long EndSize = End - Start;
578bfd0a 85
c7b5ce1c
AL
86 memmove(Buffer,Start,EndSize);
87 Start = Buffer;
88 End = Buffer + EndSize;
89
f604cf55 90 if (Done == true)
578bfd0a 91 {
c7b5ce1c 92 if (EndSize <= 3)
578bfd0a 93 return false;
c7b5ce1c
AL
94 if (Size - (End - Buffer) < 4)
95 return true;
96
97 // Append a double new line if one does not exist
98 unsigned int LineCount = 0;
99 for (const char *E = End - 1; E - End < 6 && (*E == '\n' || *E == '\r'); E--)
100 if (*E == '\n')
101 LineCount++;
102 for (; LineCount < 2; LineCount++)
103 *End++ = '\n';
104
578bfd0a
AL
105 return true;
106 }
107
c88edf1d 108 // See if only a bit of the file is left
f604cf55
AL
109 unsigned long Actual;
110 if (Fd.Read(End,Size - (End - Buffer),&Actual) == false)
111 return false;
112 if (Actual != Size - (End - Buffer))
113 Done = true;
114 End += Actual;
115/*
c88edf1d 116 if (Left < Size - (End - Buffer))
578bfd0a 117 {
ad00ae81 118 if (Fd.Read(End,Left) == false)
578bfd0a 119 return false;
c88edf1d 120
ad00ae81 121 End += Left;
578bfd0a
AL
122 Left = 0;
123 }
124 else
125 {
ad00ae81 126 if (Fd.Read(End,Size - (End - Buffer)) == false)
578bfd0a 127 return false;
c88edf1d 128
ad00ae81
AL
129 Left -= Size - (End - Buffer);
130 End = Buffer + Size;
f604cf55
AL
131 }*/
132
578bfd0a
AL
133 return true;
134}
135 /*}}}*/
ad00ae81
AL
136// TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
137// ---------------------------------------------------------------------
03e39e59
AL
138/* This jumps to a pre-recorded file location and reads the record
139 that is there */
ad00ae81
AL
140bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long Offset)
141{
b2e465d6
AL
142 // We are within a buffer space of the next hit..
143 if (Offset >= iOffset && iOffset + (End - Start) > Offset)
144 {
145 unsigned long Dist = Offset - iOffset;
146 Start += Dist;
147 iOffset += Dist;
148 return Step(Tag);
149 }
150
151 // Reposition and reload..
ad00ae81 152 iOffset = Offset;
f604cf55 153 Done = false;
ad00ae81
AL
154 if (Fd.Seek(Offset) == false)
155 return false;
156 End = Start = Buffer;
157
138d4b3d
AL
158 if (Fill() == false)
159 return false;
160
161 if (Tag.Scan(Start,End - Start) == true)
162 return true;
163
164 // This appends a double new line (for the real eof handling)
ad00ae81
AL
165 if (Fill() == false)
166 return false;
167
168 if (Tag.Scan(Start,End - Start) == false)
b2e465d6 169 return _error->Error(_("Unable to parse package file %s (2)"),Fd.Name().c_str());
06bba740 170
ad00ae81
AL
171 return true;
172}
173 /*}}}*/
578bfd0a
AL
174// TagSection::Scan - Scan for the end of the header information /*{{{*/
175// ---------------------------------------------------------------------
176/* This looks for the first double new line in the data stream. It also
c1a22377
AL
177 indexes the tags in the section. This very simple hash function for the
178 first 3 letters gives very good performance on the debian package files */
b2e465d6
AL
179inline static unsigned long AlphaHash(const char *Text, const char *End = 0)
180{
181 unsigned long Res = 0;
182 for (; Text != End && *Text != ':' && *Text != 0; Text++)
183 Res = (unsigned long)(*Text) ^ (Res << 2);
184 return Res & 0xFF;
185}
186
578bfd0a
AL
187bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
188{
189 const char *End = Start + MaxLength;
190 Stop = Section = Start;
c1a22377 191 memset(AlphaIndexes,0,sizeof(AlphaIndexes));
c7b5ce1c
AL
192
193 if (Stop == 0)
194 return false;
578bfd0a
AL
195
196 TagCount = 0;
f3bcc383 197 while (TagCount < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
578bfd0a 198 {
90d64280 199 // Start a new index and add it to the hash
c1a22377
AL
200 if (isspace(Stop[0]) == 0)
201 {
202 Indexes[TagCount++] = Stop - Section;
b2e465d6 203 AlphaIndexes[AlphaHash(Stop,End)] = TagCount;
c1a22377 204 }
0a8e3465 205
c1a22377 206 Stop = (const char *)memchr(Stop,'\n',End - Stop);
0a8e3465 207
c1a22377
AL
208 if (Stop == 0)
209 return false;
138d4b3d 210
90d64280 211 for (; Stop[1] == '\r' && Stop+1 < End; Stop++);
c1a22377 212
f3bcc383
AL
213 // Double newline marks the end of the record
214 if (Stop+1 < End && Stop[1] == '\n')
578bfd0a 215 {
578bfd0a 216 Indexes[TagCount] = Stop - Section;
0a8e3465 217 for (; (Stop[0] == '\n' || Stop[0] == '\r') && Stop < End; Stop++);
578bfd0a 218 return true;
578bfd0a
AL
219 }
220
c1a22377
AL
221 Stop++;
222 }
138d4b3d 223
578bfd0a
AL
224 return false;
225}
226 /*}}}*/
b2e465d6
AL
227// TagSection::Trim - Trim off any trailing garbage /*{{{*/
228// ---------------------------------------------------------------------
229/* There should be exactly 1 newline at the end of the buffer, no more. */
230void pkgTagSection::Trim()
231{
232 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
233}
234 /*}}}*/
578bfd0a
AL
235// TagSection::Find - Locate a tag /*{{{*/
236// ---------------------------------------------------------------------
237/* This searches the section for a tag that matches the given string. */
b2e465d6 238bool pkgTagSection::Find(const char *Tag,unsigned &Pos) const
578bfd0a
AL
239{
240 unsigned int Length = strlen(Tag);
b2e465d6 241 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
c1a22377
AL
242 if (I == 0)
243 return false;
244 I--;
245
246 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
247 I = (I+1)%TagCount)
578bfd0a 248 {
c1a22377
AL
249 const char *St;
250 St = Section + Indexes[I];
251 if (strncasecmp(Tag,St,Length) != 0)
578bfd0a
AL
252 continue;
253
b2e465d6
AL
254 // Make sure the colon is in the right place
255 const char *C = St + Length;
256 for (; isspace(*C) != 0; C++);
257 if (*C != ':')
258 continue;
259 Pos = I;
260 return true;
261 }
262
263 Pos = 0;
264 return false;
265}
266 /*}}}*/
267// TagSection::Find - Locate a tag /*{{{*/
268// ---------------------------------------------------------------------
269/* This searches the section for a tag that matches the given string. */
270bool pkgTagSection::Find(const char *Tag,const char *&Start,
271 const char *&End) const
272{
273 unsigned int Length = strlen(Tag);
274 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
275 if (I == 0)
276 return false;
277 I--;
278
279 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
280 I = (I+1)%TagCount)
281 {
282 const char *St;
283 St = Section + Indexes[I];
284 if (strncasecmp(Tag,St,Length) != 0)
285 continue;
286
578bfd0a 287 // Make sure the colon is in the right place
c1a22377 288 const char *C = St + Length;
578bfd0a
AL
289 for (; isspace(*C) != 0; C++);
290 if (*C != ':')
291 continue;
292
293 // Strip off the gunk from the start end
294 Start = C;
295 End = Section + Indexes[I+1];
06bba740
AL
296 if (Start >= End)
297 return _error->Error("Internal parsing error");
298
578bfd0a
AL
299 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
300 for (; isspace(End[-1]) != 0 && End > Start; End--);
06bba740 301
578bfd0a
AL
302 return true;
303 }
c1a22377 304
578bfd0a
AL
305 Start = End = 0;
306 return false;
307}
308 /*}}}*/
0e66b144 309// TagSection::FindS - Find a string /*{{{*/
a05599f1
AL
310// ---------------------------------------------------------------------
311/* */
b2e465d6 312string pkgTagSection::FindS(const char *Tag) const
a05599f1
AL
313{
314 const char *Start;
315 const char *End;
316 if (Find(Tag,Start,End) == false)
317 return string();
318 return string(Start,End);
319}
320 /*}}}*/
321// TagSection::FindI - Find an integer /*{{{*/
322// ---------------------------------------------------------------------
323/* */
b2e465d6 324signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
a05599f1
AL
325{
326 const char *Start;
b0b4efb9
AL
327 const char *Stop;
328 if (Find(Tag,Start,Stop) == false)
329 return Default;
330
331 // Copy it into a temp buffer so we can use strtol
332 char S[300];
333 if ((unsigned)(Stop - Start) >= sizeof(S))
334 return Default;
335 strncpy(S,Start,Stop-Start);
336 S[Stop - Start] = 0;
337
338 char *End;
339 signed long Result = strtol(S,&End,10);
340 if (S == End)
341 return Default;
342 return Result;
343}
344 /*}}}*/
345// TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
346// ---------------------------------------------------------------------
347/* The bits marked in Flag are masked on/off in Flags */
348bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
b2e465d6 349 unsigned long Flag) const
b0b4efb9
AL
350{
351 const char *Start;
352 const char *Stop;
353 if (Find(Tag,Start,Stop) == false)
354 return true;
a05599f1 355
b0b4efb9
AL
356 switch (StringToBool(string(Start,Stop)))
357 {
358 case 0:
359 Flags &= ~Flag;
360 return true;
361
362 case 1:
363 Flags |= Flag;
364 return true;
365
366 default:
b2e465d6 367 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
b0b4efb9
AL
368 return true;
369 }
370 return true;
a05599f1
AL
371}
372 /*}}}*/
b2e465d6
AL
373
374// TFRewrite - Rewrite a control record /*{{{*/
375// ---------------------------------------------------------------------
376/* This writes the control record to stdout rewriting it as necessary. The
377 override map item specificies the rewriting rules to follow. This also
378 takes the time to sort the feild list. */
379
380/* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
381 array. */
382static const char *iTFRewritePackageOrder[] = {
383 "Package",
384 "Essential",
385 "Status",
386 "Priority",
387 "Section",
388 "Installed-Size",
389 "Maintainer",
390 "Architecture",
391 "Source",
392 "Version",
393 "Revision", // Obsolete
394 "Config-Version", // Obsolete
395 "Replaces",
396 "Provides",
397 "Depends",
398 "Pre-Depends",
399 "Recommends",
400 "Suggests",
401 "Conflicts",
402 "Conffiles",
403 "Filename",
404 "Size",
405 "MD5Sum",
a7c835af 406 "SHA1Sum",
b2e465d6
AL
407 "MSDOS-Filename", // Obsolete
408 "Description",
409 0};
410static const char *iTFRewriteSourceOrder[] = {"Package",
411 "Source",
412 "Binary",
413 "Version",
414 "Priority",
415 "Section",
416 "Maintainer",
417 "Build-Depends",
418 "Build-Depends-Indep",
419 "Build-Conflicts",
420 "Build-Conflicts-Indep",
421 "Architecture",
422 "Standards-Version",
423 "Format",
424 "Directory",
425 "Files",
426 0};
427
428/* Two levels of initialization are used because gcc will set the symbol
429 size of an array to the length of the array, causing dynamic relinking
430 errors. Doing this makes the symbol size constant */
431const char **TFRewritePackageOrder = iTFRewritePackageOrder;
432const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
433
434bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
435 TFRewriteData *Rewrite)
436{
437 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
438 for (unsigned I = 0; I != 256; I++)
439 Visited[I] = 0;
440
441 // Set new tag up as necessary.
442 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
443 {
444 if (Rewrite[J].NewTag == 0)
445 Rewrite[J].NewTag = Rewrite[J].Tag;
446 }
447
448 // Write all all of the tags, in order.
449 for (unsigned int I = 0; Order[I] != 0; I++)
450 {
451 bool Rewritten = false;
452
453 // See if this is a field that needs to be rewritten
454 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
455 {
456 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
457 {
458 Visited[J] |= 2;
459 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
460 {
461 if (isspace(Rewrite[J].Rewrite[0]))
462 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
463 else
464 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
465 }
466
467 Rewritten = true;
468 break;
469 }
470 }
471
472 // See if it is in the fragment
473 unsigned Pos;
474 if (Tags.Find(Order[I],Pos) == false)
475 continue;
476 Visited[Pos] |= 1;
477
478 if (Rewritten == true)
479 continue;
480
481 /* Write out this element, taking a moment to rewrite the tag
482 in case of changes of case. */
483 const char *Start;
484 const char *Stop;
485 Tags.Get(Start,Stop,Pos);
486
487 if (fputs(Order[I],Output) < 0)
488 return _error->Errno("fputs","IO Error to output");
489 Start += strlen(Order[I]);
490 if (fwrite(Start,Stop - Start,1,Output) != 1)
491 return _error->Errno("fwrite","IO Error to output");
492 if (Stop[-1] != '\n')
493 fprintf(Output,"\n");
494 }
495
496 // Now write all the old tags that were missed.
497 for (unsigned int I = 0; I != Tags.Count(); I++)
498 {
499 if ((Visited[I] & 1) == 1)
500 continue;
501
502 const char *Start;
503 const char *Stop;
504 Tags.Get(Start,Stop,I);
505 const char *End = Start;
506 for (; End < Stop && *End != ':'; End++);
507
508 // See if this is a field that needs to be rewritten
509 bool Rewritten = false;
510 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
511 {
512 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
513 {
514 Visited[J] |= 2;
515 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
516 {
517 if (isspace(Rewrite[J].Rewrite[0]))
518 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
519 else
520 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
521 }
522
523 Rewritten = true;
524 break;
525 }
526 }
527
528 if (Rewritten == true)
529 continue;
530
531 // Write out this element
532 if (fwrite(Start,Stop - Start,1,Output) != 1)
533 return _error->Errno("fwrite","IO Error to output");
534 if (Stop[-1] != '\n')
535 fprintf(Output,"\n");
536 }
537
538 // Now write all the rewrites that were missed
539 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
540 {
541 if ((Visited[J] & 2) == 2)
542 continue;
543
544 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
545 {
546 if (isspace(Rewrite[J].Rewrite[0]))
547 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
548 else
549 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
550 }
551 }
552
553 return true;
554}
555 /*}}}*/