]> git.saurik.com Git - apt-legacy.git/blob - apt-pkg/tagfile.cc.orig
Attempt to skip and resync any seriously broken semantics in package sections.
[apt-legacy.git] / apt-pkg / tagfile.cc.orig
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 <apt-pkg/tagfile.h>
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/strutl.h>
17
18 #include <apti18n.h>
19
20 #include <string>
21 #include <stdio.h>
22 #include <ctype.h>
23 /*}}}*/
24
25 using std::string;
26
27 // TagFile::pkgTagFile - Constructor /*{{{*/
28 // ---------------------------------------------------------------------
29 /* */
30 pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long Size) :
31 Fd(*pFd),
32 Size(Size)
33 {
34 if (Fd.IsOpen() == false)
35 {
36 Buffer = 0;
37 Start = End = Buffer = 0;
38 Done = true;
39 iOffset = 0;
40 return;
41 }
42
43 Buffer = new char[Size];
44 Start = End = Buffer;
45 Done = false;
46 iOffset = 0;
47 Fill();
48 }
49 /*}}}*/
50 // TagFile::~pkgTagFile - Destructor /*{{{*/
51 // ---------------------------------------------------------------------
52 /* */
53 pkgTagFile::~pkgTagFile()
54 {
55 delete [] Buffer;
56 }
57 /*}}}*/
58 // TagFile::Resize - Resize the internal buffer /*{{{*/
59 // ---------------------------------------------------------------------
60 /* Resize the internal buffer (double it in size). Fail if a maximum size
61 * size is reached.
62 */
63 bool pkgTagFile::Resize()
64 {
65 char *tmp;
66 unsigned long EndSize = End - Start;
67
68 // fail is the buffer grows too big
69 if(Size > 1024*1024+1)
70 return false;
71
72 // get new buffer and use it
73 tmp = new char[2*Size];
74 memcpy(tmp, Buffer, Size);
75 Size = Size*2;
76 delete [] Buffer;
77 Buffer = tmp;
78
79 // update the start/end pointers to the new buffer
80 Start = Buffer;
81 End = Start + EndSize;
82 return true;
83 }
84 /*}}}*/
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.
90 */
91 bool pkgTagFile::Step(pkgTagSection &Tag)
92 {
93 while (Tag.Scan(Start,End - Start) == false)
94 {
95 if (Fill() == false)
96 return false;
97
98 if(Tag.Scan(Start,End - Start))
99 break;
100
101 if (Resize() == false)
102 return _error->Error(_("Unable to parse package file %s (1)"),
103 Fd.Name().c_str());
104 }
105 Start += Tag.size();
106 iOffset += Tag.size();
107
108 Tag.Trim();
109 return true;
110 }
111 /*}}}*/
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()
117 {
118 unsigned long EndSize = End - Start;
119 unsigned long Actual = 0;
120
121 memmove(Buffer,Start,EndSize);
122 Start = Buffer;
123 End = Buffer + EndSize;
124
125 if (Done == false)
126 {
127 // See if only a bit of the file is left
128 if (Fd.Read(End,Size - (End - Buffer),&Actual) == false)
129 return false;
130 if (Actual != Size - (End - Buffer))
131 Done = true;
132 End += Actual;
133 }
134
135 if (Done == true)
136 {
137 if (EndSize <= 3 && Actual == 0)
138 return false;
139 if (Size - (End - Buffer) < 4)
140 return true;
141
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--)
145 if (*E == '\n')
146 LineCount++;
147 for (; LineCount < 2; LineCount++)
148 *End++ = '\n';
149
150 return true;
151 }
152
153 return true;
154 }
155 /*}}}*/
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
159 that is there */
160 bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long Offset)
161 {
162 // We are within a buffer space of the next hit..
163 if (Offset >= iOffset && iOffset + (End - Start) > Offset)
164 {
165 unsigned long Dist = Offset - iOffset;
166 Start += Dist;
167 iOffset += Dist;
168 return Step(Tag);
169 }
170
171 // Reposition and reload..
172 iOffset = Offset;
173 Done = false;
174 if (Fd.Seek(Offset) == false)
175 return false;
176 End = Start = Buffer;
177
178 if (Fill() == false)
179 return false;
180
181 if (Tag.Scan(Start,End - Start) == true)
182 return true;
183
184 // This appends a double new line (for the real eof handling)
185 if (Fill() == false)
186 return false;
187
188 if (Tag.Scan(Start,End - Start) == false)
189 return _error->Error(_("Unable to parse package file %s (2)"),Fd.Name().c_str());
190
191 return true;
192 }
193 /*}}}*/
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)
200 {
201 unsigned long Res = 0;
202 for (; Text != End && *Text != ':' && *Text != 0; Text++)
203 Res = ((unsigned long)(*Text) & 0xDF) ^ (Res << 1);
204 return Res & 0xFF;
205 }
206
207 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
208 {
209 const char *End = Start + MaxLength;
210 Stop = Section = Start;
211 memset(AlphaIndexes,0,sizeof(AlphaIndexes));
212
213 if (Stop == 0)
214 return false;
215
216 TagCount = 0;
217 while (TagCount+1 < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
218 {
219 TrimRecord(true,End);
220
221 // Start a new index and add it to the hash
222 if (isspace(Stop[0]) == 0)
223 {
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;
229 }
230
231 Stop = (const char *)memchr(Stop,'\n',End - Stop);
232
233 if (Stop == 0)
234 return false;
235
236 for (; Stop+1 < End && Stop[1] == '\r'; Stop++);
237
238 // Double newline marks the end of the record
239 if (Stop+1 < End && Stop[1] == '\n')
240 {
241 Indexes[TagCount] = Stop - Section;
242 TrimRecord(false,End);
243 return true;
244 }
245
246 Stop++;
247 }
248
249 return false;
250 }
251 /*}}}*/
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)
256 {
257 if (BeforeRecord == true)
258 return;
259 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
260 }
261 /*}}}*/
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()
266 {
267 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
268 }
269 /*}}}*/
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
274 {
275 unsigned int Length = strlen(Tag);
276 unsigned int J = AlphaHash(Tag);
277
278 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
279 J = (J+1)%(sizeof(AlphaIndexes)/sizeof(AlphaIndexes[0])))
280 {
281 unsigned int I = AlphaIndexes[J];
282 if (I == 0)
283 return false;
284 I--;
285
286 const char *St;
287 St = Section + Indexes[I];
288 if (strncasecmp(Tag,St,Length) != 0)
289 continue;
290
291 // Make sure the colon is in the right place
292 const char *C = St + Length;
293 for (; isspace(*C) != 0; C++);
294 if (*C != ':')
295 continue;
296 Pos = I;
297 return true;
298 }
299
300 Pos = 0;
301 return false;
302 }
303 /*}}}*/
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
309 {
310 unsigned int Length = strlen(Tag);
311 unsigned int J = AlphaHash(Tag);
312
313 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
314 J = (J+1)%(sizeof(AlphaIndexes)/sizeof(AlphaIndexes[0])))
315 {
316 unsigned int I = AlphaIndexes[J];
317 if (I == 0)
318 return false;
319 I--;
320
321 const char *St;
322 St = Section + Indexes[I];
323 if (strncasecmp(Tag,St,Length) != 0)
324 continue;
325
326 // Make sure the colon is in the right place
327 const char *C = St + Length;
328 for (; isspace(*C) != 0; C++);
329 if (*C != ':')
330 continue;
331
332 // Strip off the gunk from the start end
333 Start = C;
334 End = Section + Indexes[I+1];
335 if (Start >= End)
336 return _error->Error("Internal parsing error");
337
338 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
339 for (; isspace(End[-1]) != 0 && End > Start; End--);
340
341 return true;
342 }
343
344 Start = End = 0;
345 return false;
346 }
347 /*}}}*/
348 // TagSection::FindS - Find a string /*{{{*/
349 // ---------------------------------------------------------------------
350 /* */
351 string pkgTagSection::FindS(const char *Tag) const
352 {
353 const char *Start;
354 const char *End;
355 if (Find(Tag,Start,End) == false)
356 return string();
357 return string(Start,End);
358 }
359 /*}}}*/
360 // TagSection::FindI - Find an integer /*{{{*/
361 // ---------------------------------------------------------------------
362 /* */
363 signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
364 {
365 const char *Start;
366 const char *Stop;
367 if (Find(Tag,Start,Stop) == false)
368 return Default;
369
370 // Copy it into a temp buffer so we can use strtol
371 char S[300];
372 if ((unsigned)(Stop - Start) >= sizeof(S))
373 return Default;
374 strncpy(S,Start,Stop-Start);
375 S[Stop - Start] = 0;
376
377 char *End;
378 signed long Result = strtol(S,&End,10);
379 if (S == End)
380 return Default;
381 return Result;
382 }
383 /*}}}*/
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
389 {
390 const char *Start;
391 const char *Stop;
392 if (Find(Tag,Start,Stop) == false)
393 return true;
394
395 switch (StringToBool(string(Start,Stop)))
396 {
397 case 0:
398 Flags &= ~Flag;
399 return true;
400
401 case 1:
402 Flags |= Flag;
403 return true;
404
405 default:
406 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
407 return true;
408 }
409 return true;
410 }
411 /*}}}*/
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. */
417
418 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
419 array. */
420 static const char *iTFRewritePackageOrder[] = {
421 "Package",
422 "Essential",
423 "Status",
424 "Priority",
425 "Section",
426 "Installed-Size",
427 "Maintainer",
428 "Architecture",
429 "Source",
430 "Version",
431 "Revision", // Obsolete
432 "Config-Version", // Obsolete
433 "Replaces",
434 "Provides",
435 "Depends",
436 "Pre-Depends",
437 "Recommends",
438 "Suggests",
439 "Conflicts",
440 "Breaks",
441 "Conffiles",
442 "Filename",
443 "Size",
444 "MD5Sum",
445 "SHA1",
446 "SHA256",
447 "MSDOS-Filename", // Obsolete
448 "Description",
449 0};
450 static const char *iTFRewriteSourceOrder[] = {"Package",
451 "Source",
452 "Binary",
453 "Version",
454 "Priority",
455 "Section",
456 "Maintainer",
457 "Build-Depends",
458 "Build-Depends-Indep",
459 "Build-Conflicts",
460 "Build-Conflicts-Indep",
461 "Architecture",
462 "Standards-Version",
463 "Format",
464 "Directory",
465 "Files",
466 0};
467
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;
473
474 bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
475 TFRewriteData *Rewrite)
476 {
477 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
478 for (unsigned I = 0; I != 256; I++)
479 Visited[I] = 0;
480
481 // Set new tag up as necessary.
482 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
483 {
484 if (Rewrite[J].NewTag == 0)
485 Rewrite[J].NewTag = Rewrite[J].Tag;
486 }
487
488 // Write all all of the tags, in order.
489 for (unsigned int I = 0; Order[I] != 0; I++)
490 {
491 bool Rewritten = false;
492
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++)
495 {
496 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
497 {
498 Visited[J] |= 2;
499 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
500 {
501 if (isspace(Rewrite[J].Rewrite[0]))
502 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
503 else
504 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
505 }
506
507 Rewritten = true;
508 break;
509 }
510 }
511
512 // See if it is in the fragment
513 unsigned Pos;
514 if (Tags.Find(Order[I],Pos) == false)
515 continue;
516 Visited[Pos] |= 1;
517
518 if (Rewritten == true)
519 continue;
520
521 /* Write out this element, taking a moment to rewrite the tag
522 in case of changes of case. */
523 const char *Start;
524 const char *Stop;
525 Tags.Get(Start,Stop,Pos);
526
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");
534 }
535
536 // Now write all the old tags that were missed.
537 for (unsigned int I = 0; I != Tags.Count(); I++)
538 {
539 if ((Visited[I] & 1) == 1)
540 continue;
541
542 const char *Start;
543 const char *Stop;
544 Tags.Get(Start,Stop,I);
545 const char *End = Start;
546 for (; End < Stop && *End != ':'; End++);
547
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++)
551 {
552 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
553 {
554 Visited[J] |= 2;
555 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
556 {
557 if (isspace(Rewrite[J].Rewrite[0]))
558 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
559 else
560 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
561 }
562
563 Rewritten = true;
564 break;
565 }
566 }
567
568 if (Rewritten == true)
569 continue;
570
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");
576 }
577
578 // Now write all the rewrites that were missed
579 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
580 {
581 if ((Visited[J] & 2) == 2)
582 continue;
583
584 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
585 {
586 if (isspace(Rewrite[J].Rewrite[0]))
587 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
588 else
589 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
590 }
591 }
592
593 return true;
594 }
595 /*}}}*/