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