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