merged from davids bundle
[ntk/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 /*}}}*/
578bfd0a
AL
285// TagSection::Find - Locate a tag /*{{{*/
286// ---------------------------------------------------------------------
287/* This searches the section for a tag that matches the given string. */
b2e465d6 288bool pkgTagSection::Find(const char *Tag,unsigned &Pos) const
578bfd0a
AL
289{
290 unsigned int Length = strlen(Tag);
b2e465d6 291 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
c1a22377
AL
292 if (I == 0)
293 return false;
294 I--;
295
296 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
297 I = (I+1)%TagCount)
578bfd0a 298 {
c1a22377
AL
299 const char *St;
300 St = Section + Indexes[I];
301 if (strncasecmp(Tag,St,Length) != 0)
578bfd0a
AL
302 continue;
303
b2e465d6
AL
304 // Make sure the colon is in the right place
305 const char *C = St + Length;
306 for (; isspace(*C) != 0; C++);
307 if (*C != ':')
308 continue;
309 Pos = I;
310 return true;
311 }
312
313 Pos = 0;
314 return false;
315}
316 /*}}}*/
317// TagSection::Find - Locate a tag /*{{{*/
318// ---------------------------------------------------------------------
319/* This searches the section for a tag that matches the given string. */
320bool pkgTagSection::Find(const char *Tag,const char *&Start,
321 const char *&End) const
322{
323 unsigned int Length = strlen(Tag);
324 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
325 if (I == 0)
326 return false;
327 I--;
328
329 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
330 I = (I+1)%TagCount)
331 {
332 const char *St;
333 St = Section + Indexes[I];
334 if (strncasecmp(Tag,St,Length) != 0)
335 continue;
336
578bfd0a 337 // Make sure the colon is in the right place
c1a22377 338 const char *C = St + Length;
578bfd0a
AL
339 for (; isspace(*C) != 0; C++);
340 if (*C != ':')
341 continue;
342
343 // Strip off the gunk from the start end
344 Start = C;
345 End = Section + Indexes[I+1];
06bba740
AL
346 if (Start >= End)
347 return _error->Error("Internal parsing error");
348
578bfd0a
AL
349 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
350 for (; isspace(End[-1]) != 0 && End > Start; End--);
06bba740 351
578bfd0a
AL
352 return true;
353 }
c1a22377 354
578bfd0a
AL
355 Start = End = 0;
356 return false;
357}
358 /*}}}*/
0e66b144 359// TagSection::FindS - Find a string /*{{{*/
a05599f1
AL
360// ---------------------------------------------------------------------
361/* */
b2e465d6 362string pkgTagSection::FindS(const char *Tag) const
a05599f1
AL
363{
364 const char *Start;
365 const char *End;
366 if (Find(Tag,Start,End) == false)
367 return string();
368 return string(Start,End);
369}
370 /*}}}*/
371// TagSection::FindI - Find an integer /*{{{*/
372// ---------------------------------------------------------------------
373/* */
b2e465d6 374signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
a05599f1
AL
375{
376 const char *Start;
b0b4efb9
AL
377 const char *Stop;
378 if (Find(Tag,Start,Stop) == false)
379 return Default;
380
381 // Copy it into a temp buffer so we can use strtol
382 char S[300];
383 if ((unsigned)(Stop - Start) >= sizeof(S))
384 return Default;
385 strncpy(S,Start,Stop-Start);
386 S[Stop - Start] = 0;
387
388 char *End;
389 signed long Result = strtol(S,&End,10);
390 if (S == End)
391 return Default;
392 return Result;
393}
394 /*}}}*/
e2c66de5
DK
395// TagSection::FindULL - Find an unsigned long long integer /*{{{*/
396// ---------------------------------------------------------------------
397/* */
398unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long const &Default) const
399{
400 const char *Start;
401 const char *Stop;
402 if (Find(Tag,Start,Stop) == false)
403 return Default;
404
405 // Copy it into a temp buffer so we can use strtoull
406 char S[100];
407 if ((unsigned)(Stop - Start) >= sizeof(S))
408 return Default;
409 strncpy(S,Start,Stop-Start);
410 S[Stop - Start] = 0;
411
412 char *End;
413 unsigned long long Result = strtoull(S,&End,10);
414 if (S == End)
415 return Default;
416 return Result;
417}
418 /*}}}*/
b0b4efb9
AL
419// TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
420// ---------------------------------------------------------------------
421/* The bits marked in Flag are masked on/off in Flags */
422bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
b2e465d6 423 unsigned long Flag) const
b0b4efb9
AL
424{
425 const char *Start;
426 const char *Stop;
427 if (Find(Tag,Start,Stop) == false)
428 return true;
fe0f7911
DK
429 return FindFlag(Flags, Flag, Start, Stop);
430}
431bool const pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
432 char const* Start, char const* Stop)
433{
434 switch (StringToBool(string(Start, Stop)))
435 {
b0b4efb9
AL
436 case 0:
437 Flags &= ~Flag;
438 return true;
439
440 case 1:
441 Flags |= Flag;
442 return true;
443
444 default:
b2e465d6 445 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
b0b4efb9
AL
446 return true;
447 }
448 return true;
a05599f1
AL
449}
450 /*}}}*/
b2e465d6
AL
451// TFRewrite - Rewrite a control record /*{{{*/
452// ---------------------------------------------------------------------
453/* This writes the control record to stdout rewriting it as necessary. The
454 override map item specificies the rewriting rules to follow. This also
455 takes the time to sort the feild list. */
456
457/* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
458 array. */
459static const char *iTFRewritePackageOrder[] = {
460 "Package",
461 "Essential",
462 "Status",
463 "Priority",
464 "Section",
465 "Installed-Size",
466 "Maintainer",
47e7ebb3 467 "Original-Maintainer",
b2e465d6
AL
468 "Architecture",
469 "Source",
470 "Version",
471 "Revision", // Obsolete
472 "Config-Version", // Obsolete
473 "Replaces",
474 "Provides",
475 "Depends",
476 "Pre-Depends",
477 "Recommends",
478 "Suggests",
479 "Conflicts",
308c7d30 480 "Breaks",
b2e465d6
AL
481 "Conffiles",
482 "Filename",
483 "Size",
484 "MD5Sum",
cde41ae8
MV
485 "SHA1",
486 "SHA256",
d9b9e9e2 487 "SHA512",
b2e465d6
AL
488 "MSDOS-Filename", // Obsolete
489 "Description",
490 0};
491static const char *iTFRewriteSourceOrder[] = {"Package",
492 "Source",
493 "Binary",
494 "Version",
495 "Priority",
496 "Section",
497 "Maintainer",
47e7ebb3 498 "Original-Maintainer",
b2e465d6
AL
499 "Build-Depends",
500 "Build-Depends-Indep",
501 "Build-Conflicts",
502 "Build-Conflicts-Indep",
503 "Architecture",
504 "Standards-Version",
505 "Format",
506 "Directory",
507 "Files",
508 0};
509
510/* Two levels of initialization are used because gcc will set the symbol
511 size of an array to the length of the array, causing dynamic relinking
512 errors. Doing this makes the symbol size constant */
513const char **TFRewritePackageOrder = iTFRewritePackageOrder;
514const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
515
516bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
517 TFRewriteData *Rewrite)
518{
519 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
520 for (unsigned I = 0; I != 256; I++)
521 Visited[I] = 0;
522
523 // Set new tag up as necessary.
524 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
525 {
526 if (Rewrite[J].NewTag == 0)
527 Rewrite[J].NewTag = Rewrite[J].Tag;
528 }
529
530 // Write all all of the tags, in order.
531 for (unsigned int I = 0; Order[I] != 0; I++)
532 {
533 bool Rewritten = false;
534
535 // See if this is a field that needs to be rewritten
536 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
537 {
538 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
539 {
540 Visited[J] |= 2;
541 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
542 {
543 if (isspace(Rewrite[J].Rewrite[0]))
544 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
545 else
546 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
547 }
548
549 Rewritten = true;
550 break;
551 }
552 }
553
554 // See if it is in the fragment
555 unsigned Pos;
556 if (Tags.Find(Order[I],Pos) == false)
557 continue;
558 Visited[Pos] |= 1;
559
560 if (Rewritten == true)
561 continue;
562
563 /* Write out this element, taking a moment to rewrite the tag
564 in case of changes of case. */
565 const char *Start;
566 const char *Stop;
567 Tags.Get(Start,Stop,Pos);
568
569 if (fputs(Order[I],Output) < 0)
570 return _error->Errno("fputs","IO Error to output");
571 Start += strlen(Order[I]);
572 if (fwrite(Start,Stop - Start,1,Output) != 1)
573 return _error->Errno("fwrite","IO Error to output");
574 if (Stop[-1] != '\n')
575 fprintf(Output,"\n");
576 }
577
578 // Now write all the old tags that were missed.
579 for (unsigned int I = 0; I != Tags.Count(); I++)
580 {
581 if ((Visited[I] & 1) == 1)
582 continue;
583
584 const char *Start;
585 const char *Stop;
586 Tags.Get(Start,Stop,I);
587 const char *End = Start;
588 for (; End < Stop && *End != ':'; End++);
589
590 // See if this is a field that needs to be rewritten
591 bool Rewritten = false;
592 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
593 {
594 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
595 {
596 Visited[J] |= 2;
597 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
598 {
599 if (isspace(Rewrite[J].Rewrite[0]))
600 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
601 else
602 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
603 }
604
605 Rewritten = true;
606 break;
607 }
608 }
609
610 if (Rewritten == true)
611 continue;
612
613 // Write out this element
614 if (fwrite(Start,Stop - Start,1,Output) != 1)
615 return _error->Errno("fwrite","IO Error to output");
616 if (Stop[-1] != '\n')
617 fprintf(Output,"\n");
618 }
619
620 // Now write all the rewrites that were missed
621 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
622 {
623 if ((Visited[J] & 2) == 2)
624 continue;
625
626 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
627 {
628 if (isspace(Rewrite[J].Rewrite[0]))
629 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
630 else
631 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
632 }
633 }
634
635 return true;
636}
637 /*}}}*/