merged lp:~mvo/apt/fix-inrelease5
[ntk/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
25 #include <apti18n.h>
26 /*}}}*/
27
28 using std::string;
29
30 class pkgTagFilePrivate
31 {
32 public:
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)
37 {
38 }
39 FileFd &Fd;
40 char *Buffer;
41 char *Start;
42 char *End;
43 bool Done;
44 unsigned long long iOffset;
45 unsigned long long Size;
46 };
47
48 // TagFile::pkgTagFile - Constructor /*{{{*/
49 // ---------------------------------------------------------------------
50 /* */
51 pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long long Size)
52 {
53 d = new pkgTagFilePrivate(pFd, Size);
54
55 if (d->Fd.IsOpen() == false)
56 {
57 d->Start = d->End = d->Buffer = 0;
58 d->Done = true;
59 d->iOffset = 0;
60 return;
61 }
62
63 d->Buffer = new char[Size];
64 d->Start = d->End = d->Buffer;
65 d->Done = false;
66 d->iOffset = 0;
67 Fill();
68 }
69 /*}}}*/
70 // TagFile::~pkgTagFile - Destructor /*{{{*/
71 // ---------------------------------------------------------------------
72 /* */
73 pkgTagFile::~pkgTagFile()
74 {
75 delete [] d->Buffer;
76 delete d;
77 }
78 /*}}}*/
79 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
80 unsigned long pkgTagFile::Offset()
81 {
82 return d->iOffset;
83 }
84 /*}}}*/
85 // TagFile::Resize - Resize the internal buffer /*{{{*/
86 // ---------------------------------------------------------------------
87 /* Resize the internal buffer (double it in size). Fail if a maximum size
88 * size is reached.
89 */
90 bool pkgTagFile::Resize()
91 {
92 char *tmp;
93 unsigned long long EndSize = d->End - d->Start;
94
95 // fail is the buffer grows too big
96 if(d->Size > 1024*1024+1)
97 return false;
98
99 // get new buffer and use it
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;
105
106 // update the start/end pointers to the new buffer
107 d->Start = d->Buffer;
108 d->End = d->Start + EndSize;
109 return true;
110 }
111 /*}}}*/
112 // TagFile::Step - Advance to the next section /*{{{*/
113 // ---------------------------------------------------------------------
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 */
118 bool pkgTagFile::Step(pkgTagSection &Tag)
119 {
120 while (Tag.Scan(d->Start,d->End - d->Start) == false)
121 {
122 if (Fill() == false)
123 return false;
124
125 if(Tag.Scan(d->Start,d->End - d->Start))
126 break;
127
128 if (Resize() == false)
129 return _error->Error(_("Unable to parse package file %s (1)"),
130 d->Fd.Name().c_str());
131 }
132 d->Start += Tag.size();
133 d->iOffset += Tag.size();
134
135 Tag.Trim();
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 */
143 bool pkgTagFile::Fill()
144 {
145 unsigned long long EndSize = d->End - d->Start;
146 unsigned long long Actual = 0;
147
148 memmove(d->Buffer,d->Start,EndSize);
149 d->Start = d->Buffer;
150 d->End = d->Buffer + EndSize;
151
152 if (d->Done == false)
153 {
154 // See if only a bit of the file is left
155 if (d->Fd.Read(d->End, d->Size - (d->End - d->Buffer),&Actual) == false)
156 return false;
157 if (Actual != d->Size - (d->End - d->Buffer))
158 d->Done = true;
159 d->End += Actual;
160 }
161
162 if (d->Done == true)
163 {
164 if (EndSize <= 3 && Actual == 0)
165 return false;
166 if (d->Size - (d->End - d->Buffer) < 4)
167 return true;
168
169 // Append a double new line if one does not exist
170 unsigned int LineCount = 0;
171 for (const char *E = d->End - 1; E - d->End < 6 && (*E == '\n' || *E == '\r'); E--)
172 if (*E == '\n')
173 LineCount++;
174 for (; LineCount < 2; LineCount++)
175 *d->End++ = '\n';
176
177 return true;
178 }
179
180 return true;
181 }
182 /*}}}*/
183 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
184 // ---------------------------------------------------------------------
185 /* This jumps to a pre-recorded file location and reads the record
186 that is there */
187 bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
188 {
189 // We are within a buffer space of the next hit..
190 if (Offset >= d->iOffset && d->iOffset + (d->End - d->Start) > Offset)
191 {
192 unsigned long long Dist = Offset - d->iOffset;
193 d->Start += Dist;
194 d->iOffset += Dist;
195 return Step(Tag);
196 }
197
198 // Reposition and reload..
199 d->iOffset = Offset;
200 d->Done = false;
201 if (d->Fd.Seek(Offset) == false)
202 return false;
203 d->End = d->Start = d->Buffer;
204
205 if (Fill() == false)
206 return false;
207
208 if (Tag.Scan(d->Start, d->End - d->Start) == true)
209 return true;
210
211 // This appends a double new line (for the real eof handling)
212 if (Fill() == false)
213 return false;
214
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());
217
218 return true;
219 }
220 /*}}}*/
221 // TagSection::Scan - Scan for the end of the header information /*{{{*/
222 // ---------------------------------------------------------------------
223 /* This looks for the first double new line in the data stream.
224 It also indexes the tags in the section. */
225 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
226 {
227 const char *End = Start + MaxLength;
228 Stop = Section = Start;
229 memset(AlphaIndexes,0,sizeof(AlphaIndexes));
230
231 if (Stop == 0)
232 return false;
233
234 TagCount = 0;
235 while (TagCount+1 < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
236 {
237 TrimRecord(true,End);
238
239 // Start a new index and add it to the hash
240 if (isspace(Stop[0]) == 0)
241 {
242 Indexes[TagCount++] = Stop - Section;
243 AlphaIndexes[AlphaHash(Stop,End)] = TagCount;
244 }
245
246 Stop = (const char *)memchr(Stop,'\n',End - Stop);
247
248 if (Stop == 0)
249 return false;
250
251 for (; Stop+1 < End && Stop[1] == '\r'; Stop++);
252
253 // Double newline marks the end of the record
254 if (Stop+1 < End && Stop[1] == '\n')
255 {
256 Indexes[TagCount] = Stop - Section;
257 TrimRecord(false,End);
258 return true;
259 }
260
261 Stop++;
262 }
263
264 return false;
265 }
266 /*}}}*/
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. */
270 void 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 /*}}}*/
277 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
278 // ---------------------------------------------------------------------
279 /* There should be exactly 1 newline at the end of the buffer, no more. */
280 void pkgTagSection::Trim()
281 {
282 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
283 }
284 /*}}}*/
285 // TagSection::Exists - return True if a tag exists /*{{{*/
286 bool pkgTagSection::Exists(const char* const Tag)
287 {
288 unsigned int tmp;
289 return Find(Tag, tmp);
290 }
291 /*}}}*/
292 // TagSection::Find - Locate a tag /*{{{*/
293 // ---------------------------------------------------------------------
294 /* This searches the section for a tag that matches the given string. */
295 bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
296 {
297 unsigned int Length = strlen(Tag);
298 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
299 if (I == 0)
300 return false;
301 I--;
302
303 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
304 I = (I+1)%TagCount)
305 {
306 const char *St;
307 St = Section + Indexes[I];
308 if (strncasecmp(Tag,St,Length) != 0)
309 continue;
310
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. */
327 bool 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
344 // Make sure the colon is in the right place
345 const char *C = St + Length;
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];
353 if (Start >= End)
354 return _error->Error("Internal parsing error");
355
356 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
357 for (; isspace(End[-1]) != 0 && End > Start; End--);
358
359 return true;
360 }
361
362 Start = End = 0;
363 return false;
364 }
365 /*}}}*/
366 // TagSection::FindS - Find a string /*{{{*/
367 // ---------------------------------------------------------------------
368 /* */
369 string pkgTagSection::FindS(const char *Tag) const
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 /* */
381 signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
382 {
383 const char *Start;
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 /*}}}*/
402 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
403 // ---------------------------------------------------------------------
404 /* */
405 unsigned 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 /*}}}*/
426 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
427 // ---------------------------------------------------------------------
428 /* The bits marked in Flag are masked on/off in Flags */
429 bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
430 unsigned long Flag) const
431 {
432 const char *Start;
433 const char *Stop;
434 if (Find(Tag,Start,Stop) == false)
435 return true;
436 return FindFlag(Flags, Flag, Start, Stop);
437 }
438 bool const pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
439 char const* Start, char const* Stop)
440 {
441 switch (StringToBool(string(Start, Stop)))
442 {
443 case 0:
444 Flags &= ~Flag;
445 return true;
446
447 case 1:
448 Flags |= Flag;
449 return true;
450
451 default:
452 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
453 return true;
454 }
455 return true;
456 }
457 /*}}}*/
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. */
466 static const char *iTFRewritePackageOrder[] = {
467 "Package",
468 "Essential",
469 "Status",
470 "Priority",
471 "Section",
472 "Installed-Size",
473 "Maintainer",
474 "Original-Maintainer",
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",
487 "Breaks",
488 "Conffiles",
489 "Filename",
490 "Size",
491 "MD5Sum",
492 "SHA1",
493 "SHA256",
494 "SHA512",
495 "MSDOS-Filename", // Obsolete
496 "Description",
497 0};
498 static const char *iTFRewriteSourceOrder[] = {"Package",
499 "Source",
500 "Binary",
501 "Version",
502 "Priority",
503 "Section",
504 "Maintainer",
505 "Original-Maintainer",
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 */
520 const char **TFRewritePackageOrder = iTFRewritePackageOrder;
521 const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
522
523 bool 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 /*}}}*/