]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/tagfile.cc
merged from donkult
[apt.git] / apt-pkg / tagfile.cc
... / ...
CommitLineData
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
28using std::string;
29
30class pkgTagFilePrivate
31{
32public:
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/* */
51pkgTagFile::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/* */
73pkgTagFile::~pkgTagFile()
74{
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;
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 */
90bool 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 */
118bool 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 */
143bool 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 */
187bool 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. */
225bool 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. */
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 /*}}}*/
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 /*}}}*/
285// TagSection::Find - Locate a tag /*{{{*/
286// ---------------------------------------------------------------------
287/* This searches the section for a tag that matches the given string. */
288bool pkgTagSection::Find(const char *Tag,unsigned &Pos) const
289{
290 unsigned int Length = strlen(Tag);
291 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
292 if (I == 0)
293 return false;
294 I--;
295
296 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
297 I = (I+1)%TagCount)
298 {
299 const char *St;
300 St = Section + Indexes[I];
301 if (strncasecmp(Tag,St,Length) != 0)
302 continue;
303
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
337 // Make sure the colon is in the right place
338 const char *C = St + Length;
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];
346 if (Start >= End)
347 return _error->Error("Internal parsing error");
348
349 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
350 for (; isspace(End[-1]) != 0 && End > Start; End--);
351
352 return true;
353 }
354
355 Start = End = 0;
356 return false;
357}
358 /*}}}*/
359// TagSection::FindS - Find a string /*{{{*/
360// ---------------------------------------------------------------------
361/* */
362string pkgTagSection::FindS(const char *Tag) const
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/* */
374signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
375{
376 const char *Start;
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 /*}}}*/
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 /*}}}*/
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,
423 unsigned long Flag) const
424{
425 const char *Start;
426 const char *Stop;
427 if (Find(Tag,Start,Stop) == false)
428 return true;
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 {
436 case 0:
437 Flags &= ~Flag;
438 return true;
439
440 case 1:
441 Flags |= Flag;
442 return true;
443
444 default:
445 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
446 return true;
447 }
448 return true;
449}
450 /*}}}*/
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",
467 "Original-Maintainer",
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",
480 "Breaks",
481 "Conffiles",
482 "Filename",
483 "Size",
484 "MD5Sum",
485 "SHA1",
486 "SHA256",
487 "SHA512",
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",
498 "Original-Maintainer",
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 /*}}}*/