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