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