1 // -*- mode: cpp; mode: fold -*-
3 // $Id: tagfile.cc,v 1.37.2.2 2003/12/31 16:02:30 mdz Exp $
4 /* ######################################################################
6 Fast scanner for RFC-822 type header information
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.
11 ##################################################################### */
13 // Include Files /*{{{*/
14 #include <apt-pkg/tagfile.h>
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/strutl.h>
27 // TagFile::pkgTagFile - Constructor /*{{{*/
28 // ---------------------------------------------------------------------
30 pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long Size) :
34 if (Fd.IsOpen() == false)
37 Start = End = Buffer = 0;
43 Buffer = new char[Size];
50 // TagFile::~pkgTagFile - Destructor /*{{{*/
51 // ---------------------------------------------------------------------
53 pkgTagFile::~pkgTagFile()
58 // TagFile::Resize - Resize the internal buffer /*{{{*/
59 // ---------------------------------------------------------------------
60 /* Resize the internal buffer (double it in size). Fail if a maximum size
63 bool pkgTagFile::Resize()
66 unsigned long EndSize = End - Start;
68 // fail is the buffer grows too big
69 if(Size > 1024*1024+1)
72 // get new buffer and use it
73 tmp = new char[2*Size];
74 memcpy(tmp, Buffer, Size);
79 // update the start/end pointers to the new buffer
81 End = Start + EndSize;
85 // TagFile::Step - Advance to the next section /*{{{*/
86 // ---------------------------------------------------------------------
87 /* If the Section Scanner fails we refill the buffer and try again.
88 * If that fails too, double the buffer size and try again until a
89 * maximum buffer is reached.
91 bool pkgTagFile::Step(pkgTagSection &Tag)
93 while (Tag.Scan(Start,End - Start) == false)
98 if(Tag.Scan(Start,End - Start))
101 if (Resize() == false)
102 return _error->Error(_("Unable to parse package file %s (1)"),
106 iOffset += Tag.size();
112 // TagFile::Fill - Top up the buffer /*{{{*/
113 // ---------------------------------------------------------------------
114 /* This takes the bit at the end of the buffer and puts it at the start
115 then fills the rest from the file */
116 bool pkgTagFile::Fill()
118 unsigned long EndSize = End - Start;
119 unsigned long Actual = 0;
121 memmove(Buffer,Start,EndSize);
123 End = Buffer + EndSize;
127 // See if only a bit of the file is left
128 if (Fd.Read(End,Size - (End - Buffer),&Actual) == false)
130 if (Actual != Size - (End - Buffer))
137 if (EndSize <= 3 && Actual == 0)
139 if (Size - (End - Buffer) < 4)
142 // Append a double new line if one does not exist
143 unsigned int LineCount = 0;
144 for (const char *E = End - 1; E - End < 6 && (*E == '\n' || *E == '\r'); E--)
147 for (; LineCount < 2; LineCount++)
156 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
157 // ---------------------------------------------------------------------
158 /* This jumps to a pre-recorded file location and reads the record
160 bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long Offset)
162 // We are within a buffer space of the next hit..
163 if (Offset >= iOffset && iOffset + (End - Start) > Offset)
165 unsigned long Dist = Offset - iOffset;
171 // Reposition and reload..
174 if (Fd.Seek(Offset) == false)
176 End = Start = Buffer;
181 if (Tag.Scan(Start,End - Start) == true)
184 // This appends a double new line (for the real eof handling)
188 if (Tag.Scan(Start,End - Start) == false)
189 return _error->Error(_("Unable to parse package file %s (2)"),Fd.Name().c_str());
194 // TagSection::Scan - Scan for the end of the header information /*{{{*/
195 // ---------------------------------------------------------------------
196 /* This looks for the first double new line in the data stream. It also
197 indexes the tags in the section. This very simple hash function for the
198 last 8 letters gives very good performance on the debian package files */
199 inline static unsigned long AlphaHash(const char *Text, const char *End = 0)
201 unsigned long Res = 0;
202 for (; Text != End && *Text != ':' && *Text != 0; Text++)
203 Res = ((unsigned long)(*Text) & 0xDF) ^ (Res << 1);
207 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
209 const char *End = Start + MaxLength;
210 Stop = Section = Start;
211 memset(AlphaIndexes,0,sizeof(AlphaIndexes));
217 while (TagCount+1 < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
219 TrimRecord(true,End);
221 // Start a new index and add it to the hash
222 if (isspace(Stop[0]) == 0)
224 Indexes[TagCount++] = Stop - Section;
225 unsigned long hash(AlphaHash(Stop, End));
226 while (AlphaIndexes[hash] != 0)
227 hash = (hash + 1) % (sizeof(AlphaIndexes) / sizeof(AlphaIndexes[0]));
228 AlphaIndexes[hash] = TagCount;
231 Stop = (const char *)memchr(Stop,'\n',End - Stop);
236 for (; Stop+1 < End && Stop[1] == '\r'; Stop++);
238 // Double newline marks the end of the record
239 if (Stop+1 < End && Stop[1] == '\n')
241 Indexes[TagCount] = Stop - Section;
242 TrimRecord(false,End);
252 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
253 // ---------------------------------------------------------------------
254 /* There should be exactly 2 newline at the end of the record, no more. */
255 void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
257 if (BeforeRecord == true)
259 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
262 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
263 // ---------------------------------------------------------------------
264 /* There should be exactly 1 newline at the end of the buffer, no more. */
265 void pkgTagSection::Trim()
267 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
270 // TagSection::Find - Locate a tag /*{{{*/
271 // ---------------------------------------------------------------------
272 /* This searches the section for a tag that matches the given string. */
273 bool pkgTagSection::Find(const char *Tag,unsigned &Pos) const
275 unsigned int Length = strlen(Tag);
276 unsigned int J = AlphaHash(Tag);
278 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
279 J = (J+1)%(sizeof(AlphaIndexes)/sizeof(AlphaIndexes[0])))
281 unsigned int I = AlphaIndexes[J];
287 St = Section + Indexes[I];
288 if (strncasecmp(Tag,St,Length) != 0)
291 // Make sure the colon is in the right place
292 const char *C = St + Length;
293 for (; isspace(*C) != 0; C++);
304 // TagSection::Find - Locate a tag /*{{{*/
305 // ---------------------------------------------------------------------
306 /* This searches the section for a tag that matches the given string. */
307 bool pkgTagSection::Find(const char *Tag,const char *&Start,
308 const char *&End) const
310 unsigned int Length = strlen(Tag);
311 unsigned int J = AlphaHash(Tag);
313 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
314 J = (J+1)%(sizeof(AlphaIndexes)/sizeof(AlphaIndexes[0])))
316 unsigned int I = AlphaIndexes[J];
322 St = Section + Indexes[I];
323 if (strncasecmp(Tag,St,Length) != 0)
326 // Make sure the colon is in the right place
327 const char *C = St + Length;
328 for (; isspace(*C) != 0; C++);
332 // Strip off the gunk from the start end
334 End = Section + Indexes[I+1];
336 return _error->Error("Internal parsing error");
338 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
339 for (; isspace(End[-1]) != 0 && End > Start; End--);
348 // TagSection::FindS - Find a string /*{{{*/
349 // ---------------------------------------------------------------------
351 string pkgTagSection::FindS(const char *Tag) const
355 if (Find(Tag,Start,End) == false)
357 return string(Start,End);
360 // TagSection::FindI - Find an integer /*{{{*/
361 // ---------------------------------------------------------------------
363 signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
367 if (Find(Tag,Start,Stop) == false)
370 // Copy it into a temp buffer so we can use strtol
372 if ((unsigned)(Stop - Start) >= sizeof(S))
374 strncpy(S,Start,Stop-Start);
378 signed long Result = strtol(S,&End,10);
384 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
385 // ---------------------------------------------------------------------
386 /* The bits marked in Flag are masked on/off in Flags */
387 bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
388 unsigned long Flag) const
392 if (Find(Tag,Start,Stop) == false)
395 switch (StringToBool(string(Start,Stop)))
406 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
412 // TFRewrite - Rewrite a control record /*{{{*/
413 // ---------------------------------------------------------------------
414 /* This writes the control record to stdout rewriting it as necessary. The
415 override map item specificies the rewriting rules to follow. This also
416 takes the time to sort the feild list. */
418 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
420 static const char *iTFRewritePackageOrder[] = {
431 "Revision", // Obsolete
432 "Config-Version", // Obsolete
447 "MSDOS-Filename", // Obsolete
450 static const char *iTFRewriteSourceOrder[] = {"Package",
458 "Build-Depends-Indep",
460 "Build-Conflicts-Indep",
468 /* Two levels of initialization are used because gcc will set the symbol
469 size of an array to the length of the array, causing dynamic relinking
470 errors. Doing this makes the symbol size constant */
471 const char **TFRewritePackageOrder = iTFRewritePackageOrder;
472 const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
474 bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
475 TFRewriteData *Rewrite)
477 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
478 for (unsigned I = 0; I != 256; I++)
481 // Set new tag up as necessary.
482 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
484 if (Rewrite[J].NewTag == 0)
485 Rewrite[J].NewTag = Rewrite[J].Tag;
488 // Write all all of the tags, in order.
489 for (unsigned int I = 0; Order[I] != 0; I++)
491 bool Rewritten = false;
493 // See if this is a field that needs to be rewritten
494 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
496 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
499 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
501 if (isspace(Rewrite[J].Rewrite[0]))
502 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
504 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
512 // See if it is in the fragment
514 if (Tags.Find(Order[I],Pos) == false)
518 if (Rewritten == true)
521 /* Write out this element, taking a moment to rewrite the tag
522 in case of changes of case. */
525 Tags.Get(Start,Stop,Pos);
527 if (fputs(Order[I],Output) < 0)
528 return _error->Errno("fputs","IO Error to output");
529 Start += strlen(Order[I]);
530 if (fwrite(Start,Stop - Start,1,Output) != 1)
531 return _error->Errno("fwrite","IO Error to output");
532 if (Stop[-1] != '\n')
533 fprintf(Output,"\n");
536 // Now write all the old tags that were missed.
537 for (unsigned int I = 0; I != Tags.Count(); I++)
539 if ((Visited[I] & 1) == 1)
544 Tags.Get(Start,Stop,I);
545 const char *End = Start;
546 for (; End < Stop && *End != ':'; End++);
548 // See if this is a field that needs to be rewritten
549 bool Rewritten = false;
550 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
552 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
555 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
557 if (isspace(Rewrite[J].Rewrite[0]))
558 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
560 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
568 if (Rewritten == true)
571 // Write out this element
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");
578 // Now write all the rewrites that were missed
579 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
581 if ((Visited[J] & 2) == 2)
584 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
586 if (isspace(Rewrite[J].Rewrite[0]))
587 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
589 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);