]> git.saurik.com Git - apt.git/blob - apt-pkg/tagfile.cc
Increase APT::Cache-HashTableSize default to 50503
[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 <list>
22
23 #include <string>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <apti18n.h>
30 /*}}}*/
31
32 using std::string;
33
34 class APT_HIDDEN pkgTagFilePrivate /*{{{*/
35 {
36 public:
37 void Reset(FileFd * const pFd, unsigned long long const pSize, pkgTagFile::Flags const pFlags)
38 {
39 if (Buffer != NULL)
40 free(Buffer);
41 Buffer = NULL;
42 Fd = pFd;
43 Flags = pFlags;
44 Start = NULL;
45 End = NULL;
46 Done = false;
47 iOffset = 0;
48 Size = pSize;
49 isCommentedLine = false;
50 chunks.clear();
51 }
52
53 pkgTagFilePrivate(FileFd * const pFd, unsigned long long const Size, pkgTagFile::Flags const pFlags) : Buffer(NULL)
54 {
55 Reset(pFd, Size, pFlags);
56 }
57 FileFd * Fd;
58 pkgTagFile::Flags Flags;
59 char *Buffer;
60 char *Start;
61 char *End;
62 bool Done;
63 unsigned long long iOffset;
64 unsigned long long Size;
65 bool isCommentedLine;
66 struct FileChunk
67 {
68 bool const good;
69 size_t length;
70 FileChunk(bool const pgood, size_t const plength) : good(pgood), length(plength) {}
71 };
72 std::list<FileChunk> chunks;
73
74 ~pkgTagFilePrivate()
75 {
76 if (Buffer != NULL)
77 free(Buffer);
78 }
79 };
80 /*}}}*/
81 class APT_HIDDEN pkgTagSectionPrivate /*{{{*/
82 {
83 public:
84 pkgTagSectionPrivate()
85 {
86 }
87 struct TagData {
88 unsigned int StartTag;
89 unsigned int EndTag;
90 unsigned int StartValue;
91 unsigned int NextInBucket;
92
93 explicit TagData(unsigned int const StartTag) : StartTag(StartTag), EndTag(0), StartValue(0), NextInBucket(0) {}
94 };
95 std::vector<TagData> Tags;
96 };
97 /*}}}*/
98
99 static unsigned long AlphaHash(const char *Text, size_t Length) /*{{{*/
100 {
101 /* This very simple hash function for the last 8 letters gives
102 very good performance on the debian package files */
103 if (Length > 8)
104 {
105 Text += (Length - 8);
106 Length = 8;
107 }
108 unsigned long Res = 0;
109 for (size_t i = 0; i < Length; ++i)
110 Res = ((unsigned long)(Text[i]) & 0xDF) ^ (Res << 1);
111 return Res & 0xFF;
112 }
113 /*}}}*/
114
115 // TagFile::pkgTagFile - Constructor /*{{{*/
116 pkgTagFile::pkgTagFile(FileFd * const pFd,pkgTagFile::Flags const pFlags, unsigned long long const Size)
117 : d(new pkgTagFilePrivate(pFd, Size + 4, pFlags))
118 {
119 Init(pFd, pFlags, Size);
120 }
121 pkgTagFile::pkgTagFile(FileFd * const pFd,unsigned long long const Size)
122 : pkgTagFile(pFd, pkgTagFile::STRICT, Size)
123 {
124 }
125 void pkgTagFile::Init(FileFd * const pFd, pkgTagFile::Flags const pFlags, unsigned long long Size)
126 {
127 /* The size is increased by 4 because if we start with the Size of the
128 filename we need to try to read 1 char more to see an EOF faster, 1
129 char the end-pointer can be on and maybe 2 newlines need to be added
130 to the end of the file -> 4 extra chars */
131 Size += 4;
132 d->Reset(pFd, Size, pFlags);
133
134 if (d->Fd->IsOpen() == false)
135 d->Start = d->End = d->Buffer = 0;
136 else
137 d->Buffer = (char*)malloc(sizeof(char) * Size);
138
139 if (d->Buffer == NULL)
140 d->Done = true;
141 else
142 d->Done = false;
143
144 d->Start = d->End = d->Buffer;
145 d->iOffset = 0;
146 if (d->Done == false)
147 Fill();
148 }
149 void pkgTagFile::Init(FileFd * const pFd,unsigned long long Size)
150 {
151 Init(pFd, pkgTagFile::STRICT, Size);
152 }
153 /*}}}*/
154 // TagFile::~pkgTagFile - Destructor /*{{{*/
155 pkgTagFile::~pkgTagFile()
156 {
157 delete d;
158 }
159 /*}}}*/
160 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
161 APT_PURE unsigned long pkgTagFile::Offset()
162 {
163 return d->iOffset;
164 }
165 /*}}}*/
166 // TagFile::Resize - Resize the internal buffer /*{{{*/
167 // ---------------------------------------------------------------------
168 /* Resize the internal buffer (double it in size). Fail if a maximum size
169 * size is reached.
170 */
171 bool pkgTagFile::Resize()
172 {
173 // fail is the buffer grows too big
174 if(d->Size > 1024*1024+1)
175 return false;
176
177 return Resize(d->Size * 2);
178 }
179 bool pkgTagFile::Resize(unsigned long long const newSize)
180 {
181 unsigned long long const EndSize = d->End - d->Start;
182
183 // get new buffer and use it
184 char* const newBuffer = static_cast<char*>(realloc(d->Buffer, sizeof(char) * newSize));
185 if (newBuffer == NULL)
186 return false;
187 d->Buffer = newBuffer;
188 d->Size = newSize;
189
190 // update the start/end pointers to the new buffer
191 d->Start = d->Buffer;
192 d->End = d->Start + EndSize;
193 return true;
194 }
195 /*}}}*/
196 // TagFile::Step - Advance to the next section /*{{{*/
197 // ---------------------------------------------------------------------
198 /* If the Section Scanner fails we refill the buffer and try again.
199 * If that fails too, double the buffer size and try again until a
200 * maximum buffer is reached.
201 */
202 bool pkgTagFile::Step(pkgTagSection &Tag)
203 {
204 if(Tag.Scan(d->Start,d->End - d->Start) == false)
205 {
206 do
207 {
208 if (Fill() == false)
209 return false;
210
211 if(Tag.Scan(d->Start,d->End - d->Start, false))
212 break;
213
214 if (Resize() == false)
215 return _error->Error(_("Unable to parse package file %s (%d)"),
216 d->Fd->Name().c_str(), 1);
217
218 } while (Tag.Scan(d->Start,d->End - d->Start, false) == false);
219 }
220
221 size_t tagSize = Tag.size();
222 d->Start += tagSize;
223
224 if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) == 0)
225 d->iOffset += tagSize;
226 else
227 {
228 auto first = d->chunks.begin();
229 for (; first != d->chunks.end(); ++first)
230 {
231 if (first->good == false)
232 d->iOffset += first->length;
233 else
234 {
235 if (tagSize < first->length)
236 {
237 first->length -= tagSize;
238 d->iOffset += tagSize;
239 break;
240 }
241 else
242 {
243 tagSize -= first->length;
244 d->iOffset += first->length;
245 }
246 }
247 }
248 d->chunks.erase(d->chunks.begin(), first);
249 }
250
251 Tag.Trim();
252 return true;
253 }
254 /*}}}*/
255 // TagFile::Fill - Top up the buffer /*{{{*/
256 // ---------------------------------------------------------------------
257 /* This takes the bit at the end of the buffer and puts it at the start
258 then fills the rest from the file */
259 static bool FillBuffer(pkgTagFilePrivate * const d)
260 {
261 unsigned long long Actual = 0;
262 // See if only a bit of the file is left
263 unsigned long long const dataSize = d->Size - ((d->End - d->Buffer) + 1);
264 if (d->Fd->Read(d->End, dataSize, &Actual) == false)
265 return false;
266 if (Actual != dataSize)
267 d->Done = true;
268 d->End += Actual;
269 return true;
270 }
271 static void RemoveCommentsFromBuffer(pkgTagFilePrivate * const d)
272 {
273 // look for valid comments in the buffer
274 char * good_start = nullptr, * bad_start = nullptr;
275 char * current = d->Start;
276 if (d->isCommentedLine == false)
277 {
278 if (d->Start == d->Buffer)
279 {
280 // the start of the buffer is a newline as a record can't start
281 // in the middle of a line by definition.
282 if (*d->Start == '#')
283 {
284 d->isCommentedLine = true;
285 ++current;
286 if (current > d->End)
287 d->chunks.emplace_back(false, 1);
288 }
289 }
290 if (d->isCommentedLine == false)
291 good_start = d->Start;
292 else
293 bad_start = d->Start;
294 }
295 else
296 bad_start = d->Start;
297
298 std::vector<std::pair<char*, size_t>> good_parts;
299 while (current <= d->End)
300 {
301 size_t const restLength = (d->End - current) + 1;
302 if (d->isCommentedLine == false)
303 {
304 current = static_cast<char*>(memchr(current, '#', restLength));
305 if (current == nullptr)
306 {
307 size_t const goodLength = d->End - good_start;
308 d->chunks.emplace_back(true, goodLength);
309 if (good_start != d->Start)
310 good_parts.push_back(std::make_pair(good_start, goodLength));
311 break;
312 }
313 bad_start = current;
314 --current;
315 // ensure that this is really a comment and not a '#' in the middle of a line
316 if (*current == '\n')
317 {
318 size_t const goodLength = (current - good_start) + 1;
319 d->chunks.emplace_back(true, goodLength);
320 good_parts.push_back(std::make_pair(good_start, goodLength));
321 good_start = nullptr;
322 d->isCommentedLine = true;
323 }
324 current += 2;
325 }
326 else // the current line is a comment
327 {
328 current = static_cast<char*>(memchr(current, '\n', restLength));
329 if (current == nullptr)
330 {
331 d->chunks.emplace_back(false, (d->End - bad_start));
332 break;
333 }
334 ++current;
335 // is the next line a comment, too?
336 if (current > d->End || *current != '#')
337 {
338 d->chunks.emplace_back(false, (current - bad_start));
339 good_start = current;
340 bad_start = nullptr;
341 d->isCommentedLine = false;
342 }
343 ++current;
344 }
345 }
346
347 if (good_parts.empty() == false)
348 {
349 // we found comments, so move later parts over them
350 current = d->Start;
351 for (auto const &good: good_parts)
352 {
353 memmove(current, good.first, good.second);
354 current += good.second;
355 }
356 d->End = current;
357 }
358
359 if (d->isCommentedLine == true)
360 {
361 // deal with a buffer containing only comments
362 // or an (unfinished) comment at the end
363 if (good_parts.empty() == true)
364 d->End = d->Start;
365 else
366 d->Start = d->End;
367 }
368 else
369 {
370 // the buffer was all comment, but ended with the buffer
371 if (good_parts.empty() == true && good_start >= d->End)
372 d->End = d->Start;
373 else
374 d->Start = d->End;
375 }
376 }
377 bool pkgTagFile::Fill()
378 {
379 unsigned long long const EndSize = d->End - d->Start;
380 if (EndSize != 0)
381 {
382 memmove(d->Buffer,d->Start,EndSize);
383 d->Start = d->End = d->Buffer + EndSize;
384 }
385 else
386 d->Start = d->End = d->Buffer;
387
388 unsigned long long Actual = 0;
389 while (d->Done == false && d->Size > (Actual + 1))
390 {
391 if (FillBuffer(d) == false)
392 return false;
393 if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) != 0)
394 RemoveCommentsFromBuffer(d);
395 Actual = d->End - d->Buffer;
396 }
397 d->Start = d->Buffer;
398
399 if (d->Done == true)
400 {
401 if (EndSize <= 3 && Actual == 0)
402 return false;
403 if (d->Size - (d->End - d->Buffer) < 4)
404 return true;
405
406 // Append a double new line if one does not exist
407 unsigned int LineCount = 0;
408 for (const char *E = d->End - 1; E - d->End < 6 && (*E == '\n' || *E == '\r'); E--)
409 if (*E == '\n')
410 ++LineCount;
411 if (LineCount < 2)
412 {
413 if (static_cast<unsigned long long>(d->End - d->Buffer) >= d->Size)
414 Resize(d->Size + 3);
415 for (; LineCount < 2; ++LineCount)
416 *d->End++ = '\n';
417 }
418 }
419 return true;
420 }
421 /*}}}*/
422 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
423 // ---------------------------------------------------------------------
424 /* This jumps to a pre-recorded file location and reads the record
425 that is there */
426 bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
427 {
428 if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) == 0 &&
429 // We are within a buffer space of the next hit..
430 Offset >= d->iOffset && d->iOffset + (d->End - d->Start) > Offset)
431 {
432 unsigned long long Dist = Offset - d->iOffset;
433 d->Start += Dist;
434 d->iOffset += Dist;
435 // if we have seen the end, don't ask for more
436 if (d->Done == true)
437 return Tag.Scan(d->Start, d->End - d->Start);
438 else
439 return Step(Tag);
440 }
441
442 // Reposition and reload..
443 d->iOffset = Offset;
444 d->Done = false;
445 if (d->Fd->Seek(Offset) == false)
446 return false;
447 d->End = d->Start = d->Buffer;
448 d->isCommentedLine = false;
449 d->chunks.clear();
450
451 if (Fill() == false)
452 return false;
453
454 if (Tag.Scan(d->Start, d->End - d->Start) == true)
455 return true;
456
457 // This appends a double new line (for the real eof handling)
458 if (Fill() == false)
459 return false;
460
461 if (Tag.Scan(d->Start, d->End - d->Start, false) == false)
462 return _error->Error(_("Unable to parse package file %s (%d)"),d->Fd->Name().c_str(), 2);
463
464 return true;
465 }
466 /*}}}*/
467 // pkgTagSection::pkgTagSection - Constructor /*{{{*/
468 // ---------------------------------------------------------------------
469 /* */
470 APT_IGNORE_DEPRECATED_PUSH
471 pkgTagSection::pkgTagSection()
472 : Section(0), d(new pkgTagSectionPrivate()), Stop(0)
473 {
474 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
475 }
476 APT_IGNORE_DEPRECATED_POP
477 /*}}}*/
478 // TagSection::Scan - Scan for the end of the header information /*{{{*/
479 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const Restart)
480 {
481 Section = Start;
482 const char *End = Start + MaxLength;
483
484 if (Restart == false && d->Tags.empty() == false)
485 {
486 Stop = Section + d->Tags.back().StartTag;
487 if (End <= Stop)
488 return false;
489 Stop = (const char *)memchr(Stop,'\n',End - Stop);
490 if (Stop == NULL)
491 return false;
492 ++Stop;
493 }
494 else
495 {
496 Stop = Section;
497 if (d->Tags.empty() == false)
498 {
499 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
500 d->Tags.clear();
501 }
502 d->Tags.reserve(0x100);
503 }
504 unsigned int TagCount = d->Tags.size();
505
506 if (Stop == 0)
507 return false;
508
509 pkgTagSectionPrivate::TagData lastTagData(0);
510 lastTagData.EndTag = 0;
511 unsigned long lastTagHash = 0;
512 while (Stop < End)
513 {
514 TrimRecord(true,End);
515
516 // this can happen when TrimRecord trims away the entire Record
517 // (e.g. because it just contains comments)
518 if(Stop == End)
519 return true;
520
521 // Start a new index and add it to the hash
522 if (isspace_ascii(Stop[0]) == 0)
523 {
524 // store the last found tag
525 if (lastTagData.EndTag != 0)
526 {
527 if (AlphaIndexes[lastTagHash] != 0)
528 lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
529 APT_IGNORE_DEPRECATED_PUSH
530 AlphaIndexes[lastTagHash] = TagCount;
531 APT_IGNORE_DEPRECATED_POP
532 d->Tags.push_back(lastTagData);
533 }
534
535 APT_IGNORE_DEPRECATED(++TagCount;)
536 lastTagData = pkgTagSectionPrivate::TagData(Stop - Section);
537 // find the colon separating tag and value
538 char const * Colon = (char const *) memchr(Stop, ':', End - Stop);
539 if (Colon == NULL)
540 return false;
541 // find the end of the tag (which might or might not be the colon)
542 char const * EndTag = Colon;
543 --EndTag;
544 for (; EndTag > Stop && isspace_ascii(*EndTag) != 0; --EndTag)
545 ;
546 ++EndTag;
547 lastTagData.EndTag = EndTag - Section;
548 lastTagHash = AlphaHash(Stop, EndTag - Stop);
549 // find the beginning of the value
550 Stop = Colon + 1;
551 for (; Stop < End && isspace_ascii(*Stop) != 0; ++Stop)
552 if (*Stop == '\n' && Stop[1] != ' ')
553 break;
554 if (Stop >= End)
555 return false;
556 lastTagData.StartValue = Stop - Section;
557 }
558
559 Stop = (const char *)memchr(Stop,'\n',End - Stop);
560
561 if (Stop == 0)
562 return false;
563
564 for (; Stop+1 < End && Stop[1] == '\r'; Stop++)
565 /* nothing */
566 ;
567
568 // Double newline marks the end of the record
569 if (Stop+1 < End && Stop[1] == '\n')
570 {
571 if (lastTagData.EndTag != 0)
572 {
573 if (AlphaIndexes[lastTagHash] != 0)
574 lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
575 APT_IGNORE_DEPRECATED(AlphaIndexes[lastTagHash] = TagCount;)
576 d->Tags.push_back(lastTagData);
577 }
578
579 pkgTagSectionPrivate::TagData const td(Stop - Section);
580 d->Tags.push_back(td);
581 TrimRecord(false,End);
582 return true;
583 }
584
585 Stop++;
586 }
587
588 return false;
589 }
590 /*}}}*/
591 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
592 // ---------------------------------------------------------------------
593 /* There should be exactly 2 newline at the end of the record, no more. */
594 void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
595 {
596 if (BeforeRecord == true)
597 return;
598 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
599 }
600 /*}}}*/
601 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
602 // ---------------------------------------------------------------------
603 /* There should be exactly 1 newline at the end of the buffer, no more. */
604 void pkgTagSection::Trim()
605 {
606 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
607 }
608 /*}}}*/
609 // TagSection::Exists - return True if a tag exists /*{{{*/
610 bool pkgTagSection::Exists(const char* const Tag) const
611 {
612 unsigned int tmp;
613 return Find(Tag, tmp);
614 }
615 /*}}}*/
616 // TagSection::Find - Locate a tag /*{{{*/
617 // ---------------------------------------------------------------------
618 /* This searches the section for a tag that matches the given string. */
619 bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
620 {
621 size_t const Length = strlen(Tag);
622 unsigned int Bucket = AlphaIndexes[AlphaHash(Tag, Length)];
623 if (Bucket == 0)
624 return false;
625
626 for (; Bucket != 0; Bucket = d->Tags[Bucket - 1].NextInBucket)
627 {
628 if ((d->Tags[Bucket - 1].EndTag - d->Tags[Bucket - 1].StartTag) != Length)
629 continue;
630
631 char const * const St = Section + d->Tags[Bucket - 1].StartTag;
632 if (strncasecmp(Tag,St,Length) != 0)
633 continue;
634
635 Pos = Bucket - 1;
636 return true;
637 }
638
639 Pos = 0;
640 return false;
641 }
642 bool pkgTagSection::Find(const char *Tag,const char *&Start,
643 const char *&End) const
644 {
645 unsigned int Pos;
646 if (Find(Tag, Pos) == false)
647 return false;
648
649 Start = Section + d->Tags[Pos].StartValue;
650 // Strip off the gunk from the end
651 End = Section + d->Tags[Pos + 1].StartTag;
652 if (unlikely(Start > End))
653 return _error->Error("Internal parsing error");
654
655 for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
656
657 return true;
658 }
659 /*}}}*/
660 // TagSection::FindS - Find a string /*{{{*/
661 string pkgTagSection::FindS(const char *Tag) const
662 {
663 const char *Start;
664 const char *End;
665 if (Find(Tag,Start,End) == false)
666 return string();
667 return string(Start,End);
668 }
669 /*}}}*/
670 // TagSection::FindRawS - Find a string /*{{{*/
671 string pkgTagSection::FindRawS(const char *Tag) const
672 {
673 unsigned int Pos;
674 if (Find(Tag, Pos) == false)
675 return "";
676
677 char const *Start = (char const *) memchr(Section + d->Tags[Pos].EndTag, ':', d->Tags[Pos].StartValue - d->Tags[Pos].EndTag);
678 ++Start;
679 char const *End = Section + d->Tags[Pos + 1].StartTag;
680 if (unlikely(Start > End))
681 return "";
682
683 for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
684
685 return std::string(Start, End - Start);
686 }
687 /*}}}*/
688 // TagSection::FindI - Find an integer /*{{{*/
689 // ---------------------------------------------------------------------
690 /* */
691 signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
692 {
693 const char *Start;
694 const char *Stop;
695 if (Find(Tag,Start,Stop) == false)
696 return Default;
697
698 // Copy it into a temp buffer so we can use strtol
699 char S[300];
700 if ((unsigned)(Stop - Start) >= sizeof(S))
701 return Default;
702 strncpy(S,Start,Stop-Start);
703 S[Stop - Start] = 0;
704
705 errno = 0;
706 char *End;
707 signed long Result = strtol(S,&End,10);
708 if (errno == ERANGE ||
709 Result < std::numeric_limits<int>::min() || Result > std::numeric_limits<int>::max()) {
710 errno = ERANGE;
711 _error->Error(_("Cannot convert %s to integer: out of range"), S);
712 }
713 if (S == End)
714 return Default;
715 return Result;
716 }
717 /*}}}*/
718 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
719 // ---------------------------------------------------------------------
720 /* */
721 unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long const &Default) const
722 {
723 const char *Start;
724 const char *Stop;
725 if (Find(Tag,Start,Stop) == false)
726 return Default;
727
728 // Copy it into a temp buffer so we can use strtoull
729 char S[100];
730 if ((unsigned)(Stop - Start) >= sizeof(S))
731 return Default;
732 strncpy(S,Start,Stop-Start);
733 S[Stop - Start] = 0;
734
735 char *End;
736 unsigned long long Result = strtoull(S,&End,10);
737 if (S == End)
738 return Default;
739 return Result;
740 }
741 /*}}}*/
742 // TagSection::FindB - Find boolean value /*{{{*/
743 // ---------------------------------------------------------------------
744 /* */
745 bool pkgTagSection::FindB(const char *Tag, bool const &Default) const
746 {
747 const char *Start, *Stop;
748 if (Find(Tag, Start, Stop) == false)
749 return Default;
750 return StringToBool(string(Start, Stop));
751 }
752 /*}}}*/
753 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
754 // ---------------------------------------------------------------------
755 /* The bits marked in Flag are masked on/off in Flags */
756 bool pkgTagSection::FindFlag(const char * const Tag, uint8_t &Flags,
757 uint8_t const Flag) const
758 {
759 const char *Start;
760 const char *Stop;
761 if (Find(Tag,Start,Stop) == false)
762 return true;
763 return FindFlag(Flags, Flag, Start, Stop);
764 }
765 bool pkgTagSection::FindFlag(uint8_t &Flags, uint8_t const Flag,
766 char const* const Start, char const* const Stop)
767 {
768 switch (StringToBool(string(Start, Stop)))
769 {
770 case 0:
771 Flags &= ~Flag;
772 return true;
773
774 case 1:
775 Flags |= Flag;
776 return true;
777
778 default:
779 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
780 return true;
781 }
782 return true;
783 }
784 bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
785 unsigned long Flag) const
786 {
787 const char *Start;
788 const char *Stop;
789 if (Find(Tag,Start,Stop) == false)
790 return true;
791 return FindFlag(Flags, Flag, Start, Stop);
792 }
793 bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
794 char const* Start, char const* Stop)
795 {
796 switch (StringToBool(string(Start, Stop)))
797 {
798 case 0:
799 Flags &= ~Flag;
800 return true;
801
802 case 1:
803 Flags |= Flag;
804 return true;
805
806 default:
807 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
808 return true;
809 }
810 return true;
811 }
812 /*}}}*/
813 void pkgTagSection::Get(const char *&Start,const char *&Stop,unsigned int I) const/*{{{*/
814 {
815 Start = Section + d->Tags[I].StartTag;
816 Stop = Section + d->Tags[I+1].StartTag;
817 }
818 /*}}}*/
819 APT_PURE unsigned int pkgTagSection::Count() const { /*{{{*/
820 if (d->Tags.empty() == true)
821 return 0;
822 // the last element is just marking the end and isn't a real one
823 return d->Tags.size() - 1;
824 }
825 /*}}}*/
826 // TagSection::Write - Ordered (re)writing of fields /*{{{*/
827 pkgTagSection::Tag pkgTagSection::Tag::Remove(std::string const &Name)
828 {
829 return Tag(REMOVE, Name, "");
830 }
831 pkgTagSection::Tag pkgTagSection::Tag::Rename(std::string const &OldName, std::string const &NewName)
832 {
833 return Tag(RENAME, OldName, NewName);
834 }
835 pkgTagSection::Tag pkgTagSection::Tag::Rewrite(std::string const &Name, std::string const &Data)
836 {
837 if (Data.empty() == true)
838 return Tag(REMOVE, Name, "");
839 else
840 return Tag(REWRITE, Name, Data);
841 }
842 static bool WriteTag(FileFd &File, std::string Tag, std::string const &Value)
843 {
844 if (Value.empty() || isspace_ascii(Value[0]) != 0)
845 Tag.append(":");
846 else
847 Tag.append(": ");
848 Tag.append(Value);
849 Tag.append("\n");
850 return File.Write(Tag.c_str(), Tag.length());
851 }
852 static bool RewriteTags(FileFd &File, pkgTagSection const * const This, char const * const Tag,
853 std::vector<pkgTagSection::Tag>::const_iterator &R,
854 std::vector<pkgTagSection::Tag>::const_iterator const &REnd)
855 {
856 size_t const TagLen = strlen(Tag);
857 for (; R != REnd; ++R)
858 {
859 std::string data;
860 if (R->Name.length() == TagLen && strncasecmp(R->Name.c_str(), Tag, R->Name.length()) == 0)
861 {
862 if (R->Action != pkgTagSection::Tag::REWRITE)
863 break;
864 data = R->Data;
865 }
866 else if(R->Action == pkgTagSection::Tag::RENAME && R->Data.length() == TagLen &&
867 strncasecmp(R->Data.c_str(), Tag, R->Data.length()) == 0)
868 data = This->FindRawS(R->Name.c_str());
869 else
870 continue;
871
872 return WriteTag(File, Tag, data);
873 }
874 return true;
875 }
876 bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::vector<Tag> const &Rewrite) const
877 {
878 // first pass: Write everything we have an order for
879 if (Order != NULL)
880 {
881 for (unsigned int I = 0; Order[I] != 0; ++I)
882 {
883 std::vector<Tag>::const_iterator R = Rewrite.begin();
884 if (RewriteTags(File, this, Order[I], R, Rewrite.end()) == false)
885 return false;
886 if (R != Rewrite.end())
887 continue;
888
889 if (Exists(Order[I]) == false)
890 continue;
891
892 if (WriteTag(File, Order[I], FindRawS(Order[I])) == false)
893 return false;
894 }
895 }
896 // second pass: See if we have tags which aren't ordered
897 if (d->Tags.empty() == false)
898 {
899 for (std::vector<pkgTagSectionPrivate::TagData>::const_iterator T = d->Tags.begin(); T != d->Tags.end() - 1; ++T)
900 {
901 char const * const fieldname = Section + T->StartTag;
902 size_t fieldnamelen = T->EndTag - T->StartTag;
903 if (Order != NULL)
904 {
905 unsigned int I = 0;
906 for (; Order[I] != 0; ++I)
907 {
908 if (fieldnamelen == strlen(Order[I]) && strncasecmp(fieldname, Order[I], fieldnamelen) == 0)
909 break;
910 }
911 if (Order[I] != 0)
912 continue;
913 }
914
915 std::string const name(fieldname, fieldnamelen);
916 std::vector<Tag>::const_iterator R = Rewrite.begin();
917 if (RewriteTags(File, this, name.c_str(), R, Rewrite.end()) == false)
918 return false;
919 if (R != Rewrite.end())
920 continue;
921
922 if (WriteTag(File, name, FindRawS(name.c_str())) == false)
923 return false;
924 }
925 }
926 // last pass: see if there are any rewrites remaining we haven't done yet
927 for (std::vector<Tag>::const_iterator R = Rewrite.begin(); R != Rewrite.end(); ++R)
928 {
929 if (R->Action == Tag::REMOVE)
930 continue;
931 std::string const name = ((R->Action == Tag::RENAME) ? R->Data : R->Name);
932 if (Exists(name.c_str()))
933 continue;
934 if (Order != NULL)
935 {
936 unsigned int I = 0;
937 for (; Order[I] != 0; ++I)
938 {
939 if (strncasecmp(name.c_str(), Order[I], name.length()) == 0 && name.length() == strlen(Order[I]))
940 break;
941 }
942 if (Order[I] != 0)
943 continue;
944 }
945
946 if (WriteTag(File, name, ((R->Action == Tag::RENAME) ? FindRawS(R->Name.c_str()) : R->Data)) == false)
947 return false;
948 }
949 return true;
950 }
951 /*}}}*/
952
953 void pkgUserTagSection::TrimRecord(bool /*BeforeRecord*/, const char* &End)/*{{{*/
954 {
955 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r' || Stop[0] == '#'); Stop++)
956 if (Stop[0] == '#')
957 Stop = (const char*) memchr(Stop,'\n',End-Stop);
958 }
959 /*}}}*/
960
961 #include "tagfile-order.c"
962
963 // TFRewrite - Rewrite a control record /*{{{*/
964 // ---------------------------------------------------------------------
965 /* This writes the control record to stdout rewriting it as necessary. The
966 override map item specificies the rewriting rules to follow. This also
967 takes the time to sort the feild list. */
968 APT_IGNORE_DEPRECATED_PUSH
969 bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
970 TFRewriteData *Rewrite)
971 {
972 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
973 for (unsigned I = 0; I != 256; I++)
974 Visited[I] = 0;
975
976 // Set new tag up as necessary.
977 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
978 {
979 if (Rewrite[J].NewTag == 0)
980 Rewrite[J].NewTag = Rewrite[J].Tag;
981 }
982
983 // Write all all of the tags, in order.
984 if (Order != NULL)
985 {
986 for (unsigned int I = 0; Order[I] != 0; I++)
987 {
988 bool Rewritten = false;
989
990 // See if this is a field that needs to be rewritten
991 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
992 {
993 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
994 {
995 Visited[J] |= 2;
996 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
997 {
998 if (isspace_ascii(Rewrite[J].Rewrite[0]))
999 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1000 else
1001 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1002 }
1003 Rewritten = true;
1004 break;
1005 }
1006 }
1007
1008 // See if it is in the fragment
1009 unsigned Pos;
1010 if (Tags.Find(Order[I],Pos) == false)
1011 continue;
1012 Visited[Pos] |= 1;
1013
1014 if (Rewritten == true)
1015 continue;
1016
1017 /* Write out this element, taking a moment to rewrite the tag
1018 in case of changes of case. */
1019 const char *Start;
1020 const char *Stop;
1021 Tags.Get(Start,Stop,Pos);
1022
1023 if (fputs(Order[I],Output) < 0)
1024 return _error->Errno("fputs","IO Error to output");
1025 Start += strlen(Order[I]);
1026 if (fwrite(Start,Stop - Start,1,Output) != 1)
1027 return _error->Errno("fwrite","IO Error to output");
1028 if (Stop[-1] != '\n')
1029 fprintf(Output,"\n");
1030 }
1031 }
1032
1033 // Now write all the old tags that were missed.
1034 for (unsigned int I = 0; I != Tags.Count(); I++)
1035 {
1036 if ((Visited[I] & 1) == 1)
1037 continue;
1038
1039 const char *Start;
1040 const char *Stop;
1041 Tags.Get(Start,Stop,I);
1042 const char *End = Start;
1043 for (; End < Stop && *End != ':'; End++);
1044
1045 // See if this is a field that needs to be rewritten
1046 bool Rewritten = false;
1047 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1048 {
1049 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
1050 {
1051 Visited[J] |= 2;
1052 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
1053 {
1054 if (isspace_ascii(Rewrite[J].Rewrite[0]))
1055 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1056 else
1057 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1058 }
1059
1060 Rewritten = true;
1061 break;
1062 }
1063 }
1064
1065 if (Rewritten == true)
1066 continue;
1067
1068 // Write out this element
1069 if (fwrite(Start,Stop - Start,1,Output) != 1)
1070 return _error->Errno("fwrite","IO Error to output");
1071 if (Stop[-1] != '\n')
1072 fprintf(Output,"\n");
1073 }
1074
1075 // Now write all the rewrites that were missed
1076 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1077 {
1078 if ((Visited[J] & 2) == 2)
1079 continue;
1080
1081 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
1082 {
1083 if (isspace_ascii(Rewrite[J].Rewrite[0]))
1084 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1085 else
1086 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1087 }
1088 }
1089
1090 return true;
1091 }
1092 APT_IGNORE_DEPRECATED_POP
1093 /*}}}*/
1094
1095 pkgTagSection::~pkgTagSection() { delete d; }