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