memset() pkgTagSections data to make coverity happy
[ntk/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<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
28 using std::string;
29
30 class pkgTagFilePrivate
31 {
32 public:
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 /* */
51 pkgTagFile::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 /* */
73 pkgTagFile::~pkgTagFile()
74 {
75 delete [] d->Buffer;
76 delete d;
77 }
78 /*}}}*/
79 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
80 unsigned 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 */
90 bool 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 */
118 bool 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 */
143 bool 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 */
187 bool 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 // pkgTagSection::pkgTagSection - Constructor /*{{{*/
222 // ---------------------------------------------------------------------
223 /* */
224 pkgTagSection::pkgTagSection()
225 : Section(0), TagCount(0), Stop(0), d(NULL)
226 {
227 memset(&Indexes, 0, sizeof(Indexes));
228 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
229 }
230 /*}}}*/
231 // TagSection::Scan - Scan for the end of the header information /*{{{*/
232 // ---------------------------------------------------------------------
233 /* This looks for the first double new line in the data stream.
234 It also indexes the tags in the section. */
235 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
236 {
237 const char *End = Start + MaxLength;
238 Stop = Section = Start;
239 memset(AlphaIndexes,0,sizeof(AlphaIndexes));
240
241 if (Stop == 0)
242 return false;
243
244 TagCount = 0;
245 while (TagCount+1 < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
246 {
247 TrimRecord(true,End);
248
249 // Start a new index and add it to the hash
250 if (isspace(Stop[0]) == 0)
251 {
252 Indexes[TagCount++] = Stop - Section;
253 AlphaIndexes[AlphaHash(Stop,End)] = TagCount;
254 }
255
256 Stop = (const char *)memchr(Stop,'\n',End - Stop);
257
258 if (Stop == 0)
259 return false;
260
261 for (; Stop+1 < End && Stop[1] == '\r'; Stop++);
262
263 // Double newline marks the end of the record
264 if (Stop+1 < End && Stop[1] == '\n')
265 {
266 Indexes[TagCount] = Stop - Section;
267 TrimRecord(false,End);
268 return true;
269 }
270
271 Stop++;
272 }
273
274 return false;
275 }
276 /*}}}*/
277 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
278 // ---------------------------------------------------------------------
279 /* There should be exactly 2 newline at the end of the record, no more. */
280 void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
281 {
282 if (BeforeRecord == true)
283 return;
284 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
285 }
286 /*}}}*/
287 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
288 // ---------------------------------------------------------------------
289 /* There should be exactly 1 newline at the end of the buffer, no more. */
290 void pkgTagSection::Trim()
291 {
292 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
293 }
294 /*}}}*/
295 // TagSection::Exists - return True if a tag exists /*{{{*/
296 bool pkgTagSection::Exists(const char* const Tag)
297 {
298 unsigned int tmp;
299 return Find(Tag, tmp);
300 }
301 /*}}}*/
302 // TagSection::Find - Locate a tag /*{{{*/
303 // ---------------------------------------------------------------------
304 /* This searches the section for a tag that matches the given string. */
305 bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
306 {
307 unsigned int Length = strlen(Tag);
308 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
309 if (I == 0)
310 return false;
311 I--;
312
313 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
314 I = (I+1)%TagCount)
315 {
316 const char *St;
317 St = Section + Indexes[I];
318 if (strncasecmp(Tag,St,Length) != 0)
319 continue;
320
321 // Make sure the colon is in the right place
322 const char *C = St + Length;
323 for (; isspace(*C) != 0; C++);
324 if (*C != ':')
325 continue;
326 Pos = I;
327 return true;
328 }
329
330 Pos = 0;
331 return false;
332 }
333 /*}}}*/
334 // TagSection::Find - Locate a tag /*{{{*/
335 // ---------------------------------------------------------------------
336 /* This searches the section for a tag that matches the given string. */
337 bool pkgTagSection::Find(const char *Tag,const char *&Start,
338 const char *&End) const
339 {
340 unsigned int Length = strlen(Tag);
341 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
342 if (I == 0)
343 return false;
344 I--;
345
346 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
347 I = (I+1)%TagCount)
348 {
349 const char *St;
350 St = Section + Indexes[I];
351 if (strncasecmp(Tag,St,Length) != 0)
352 continue;
353
354 // Make sure the colon is in the right place
355 const char *C = St + Length;
356 for (; isspace(*C) != 0; C++);
357 if (*C != ':')
358 continue;
359
360 // Strip off the gunk from the start end
361 Start = C;
362 End = Section + Indexes[I+1];
363 if (Start >= End)
364 return _error->Error("Internal parsing error");
365
366 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
367 for (; isspace(End[-1]) != 0 && End > Start; End--);
368
369 return true;
370 }
371
372 Start = End = 0;
373 return false;
374 }
375 /*}}}*/
376 // TagSection::FindS - Find a string /*{{{*/
377 // ---------------------------------------------------------------------
378 /* */
379 string pkgTagSection::FindS(const char *Tag) const
380 {
381 const char *Start;
382 const char *End;
383 if (Find(Tag,Start,End) == false)
384 return string();
385 return string(Start,End);
386 }
387 /*}}}*/
388 // TagSection::FindI - Find an integer /*{{{*/
389 // ---------------------------------------------------------------------
390 /* */
391 signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
392 {
393 const char *Start;
394 const char *Stop;
395 if (Find(Tag,Start,Stop) == false)
396 return Default;
397
398 // Copy it into a temp buffer so we can use strtol
399 char S[300];
400 if ((unsigned)(Stop - Start) >= sizeof(S))
401 return Default;
402 strncpy(S,Start,Stop-Start);
403 S[Stop - Start] = 0;
404
405 char *End;
406 signed long Result = strtol(S,&End,10);
407 if (S == End)
408 return Default;
409 return Result;
410 }
411 /*}}}*/
412 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
413 // ---------------------------------------------------------------------
414 /* */
415 unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long const &Default) const
416 {
417 const char *Start;
418 const char *Stop;
419 if (Find(Tag,Start,Stop) == false)
420 return Default;
421
422 // Copy it into a temp buffer so we can use strtoull
423 char S[100];
424 if ((unsigned)(Stop - Start) >= sizeof(S))
425 return Default;
426 strncpy(S,Start,Stop-Start);
427 S[Stop - Start] = 0;
428
429 char *End;
430 unsigned long long Result = strtoull(S,&End,10);
431 if (S == End)
432 return Default;
433 return Result;
434 }
435 /*}}}*/
436 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
437 // ---------------------------------------------------------------------
438 /* The bits marked in Flag are masked on/off in Flags */
439 bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
440 unsigned long Flag) const
441 {
442 const char *Start;
443 const char *Stop;
444 if (Find(Tag,Start,Stop) == false)
445 return true;
446 return FindFlag(Flags, Flag, Start, Stop);
447 }
448 bool const pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
449 char const* Start, char const* Stop)
450 {
451 switch (StringToBool(string(Start, Stop)))
452 {
453 case 0:
454 Flags &= ~Flag;
455 return true;
456
457 case 1:
458 Flags |= Flag;
459 return true;
460
461 default:
462 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
463 return true;
464 }
465 return true;
466 }
467 /*}}}*/
468 // TFRewrite - Rewrite a control record /*{{{*/
469 // ---------------------------------------------------------------------
470 /* This writes the control record to stdout rewriting it as necessary. The
471 override map item specificies the rewriting rules to follow. This also
472 takes the time to sort the feild list. */
473
474 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
475 array. */
476 static const char *iTFRewritePackageOrder[] = {
477 "Package",
478 "Essential",
479 "Status",
480 "Priority",
481 "Section",
482 "Installed-Size",
483 "Maintainer",
484 "Original-Maintainer",
485 "Architecture",
486 "Source",
487 "Version",
488 "Revision", // Obsolete
489 "Config-Version", // Obsolete
490 "Replaces",
491 "Provides",
492 "Depends",
493 "Pre-Depends",
494 "Recommends",
495 "Suggests",
496 "Conflicts",
497 "Breaks",
498 "Conffiles",
499 "Filename",
500 "Size",
501 "MD5Sum",
502 "SHA1",
503 "SHA256",
504 "SHA512",
505 "MSDOS-Filename", // Obsolete
506 "Description",
507 0};
508 static const char *iTFRewriteSourceOrder[] = {"Package",
509 "Source",
510 "Binary",
511 "Version",
512 "Priority",
513 "Section",
514 "Maintainer",
515 "Original-Maintainer",
516 "Build-Depends",
517 "Build-Depends-Indep",
518 "Build-Conflicts",
519 "Build-Conflicts-Indep",
520 "Architecture",
521 "Standards-Version",
522 "Format",
523 "Directory",
524 "Files",
525 0};
526
527 /* Two levels of initialization are used because gcc will set the symbol
528 size of an array to the length of the array, causing dynamic relinking
529 errors. Doing this makes the symbol size constant */
530 const char **TFRewritePackageOrder = iTFRewritePackageOrder;
531 const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
532
533 bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
534 TFRewriteData *Rewrite)
535 {
536 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
537 for (unsigned I = 0; I != 256; I++)
538 Visited[I] = 0;
539
540 // Set new tag up as necessary.
541 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
542 {
543 if (Rewrite[J].NewTag == 0)
544 Rewrite[J].NewTag = Rewrite[J].Tag;
545 }
546
547 // Write all all of the tags, in order.
548 for (unsigned int I = 0; Order[I] != 0; I++)
549 {
550 bool Rewritten = false;
551
552 // See if this is a field that needs to be rewritten
553 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
554 {
555 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
556 {
557 Visited[J] |= 2;
558 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
559 {
560 if (isspace(Rewrite[J].Rewrite[0]))
561 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
562 else
563 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
564 }
565
566 Rewritten = true;
567 break;
568 }
569 }
570
571 // See if it is in the fragment
572 unsigned Pos;
573 if (Tags.Find(Order[I],Pos) == false)
574 continue;
575 Visited[Pos] |= 1;
576
577 if (Rewritten == true)
578 continue;
579
580 /* Write out this element, taking a moment to rewrite the tag
581 in case of changes of case. */
582 const char *Start;
583 const char *Stop;
584 Tags.Get(Start,Stop,Pos);
585
586 if (fputs(Order[I],Output) < 0)
587 return _error->Errno("fputs","IO Error to output");
588 Start += strlen(Order[I]);
589 if (fwrite(Start,Stop - Start,1,Output) != 1)
590 return _error->Errno("fwrite","IO Error to output");
591 if (Stop[-1] != '\n')
592 fprintf(Output,"\n");
593 }
594
595 // Now write all the old tags that were missed.
596 for (unsigned int I = 0; I != Tags.Count(); I++)
597 {
598 if ((Visited[I] & 1) == 1)
599 continue;
600
601 const char *Start;
602 const char *Stop;
603 Tags.Get(Start,Stop,I);
604 const char *End = Start;
605 for (; End < Stop && *End != ':'; End++);
606
607 // See if this is a field that needs to be rewritten
608 bool Rewritten = false;
609 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
610 {
611 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
612 {
613 Visited[J] |= 2;
614 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
615 {
616 if (isspace(Rewrite[J].Rewrite[0]))
617 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
618 else
619 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
620 }
621
622 Rewritten = true;
623 break;
624 }
625 }
626
627 if (Rewritten == true)
628 continue;
629
630 // Write out this element
631 if (fwrite(Start,Stop - Start,1,Output) != 1)
632 return _error->Errno("fwrite","IO Error to output");
633 if (Stop[-1] != '\n')
634 fprintf(Output,"\n");
635 }
636
637 // Now write all the rewrites that were missed
638 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
639 {
640 if ((Visited[J] & 2) == 2)
641 continue;
642
643 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
644 {
645 if (isspace(Rewrite[J].Rewrite[0]))
646 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
647 else
648 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
649 }
650 }
651
652 return true;
653 }
654 /*}}}*/