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