]> git.saurik.com Git - apt.git/blob - apt-pkg/tagfile.cc
TagSection: Extract Find() methods taking Pos instead of Key
[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 BetaHash(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 & 0x7F;
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 memset(&BetaIndexes, 0, sizeof(BetaIndexes));
478 }
479 APT_IGNORE_DEPRECATED_POP
480 /*}}}*/
481 // TagSection::Scan - Scan for the end of the header information /*{{{*/
482 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const Restart)
483 {
484 Section = Start;
485 const char *End = Start + MaxLength;
486
487 if (Restart == false && d->Tags.empty() == false)
488 {
489 Stop = Section + d->Tags.back().StartTag;
490 if (End <= Stop)
491 return false;
492 Stop = (const char *)memchr(Stop,'\n',End - Stop);
493 if (Stop == NULL)
494 return false;
495 ++Stop;
496 }
497 else
498 {
499 Stop = Section;
500 if (d->Tags.empty() == false)
501 {
502 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
503 memset(&BetaIndexes, 0, sizeof(BetaIndexes));
504 d->Tags.clear();
505 }
506 d->Tags.reserve(0x100);
507 }
508 unsigned int TagCount = d->Tags.size();
509
510 if (Stop == 0)
511 return false;
512
513 pkgTagSectionPrivate::TagData lastTagData(0);
514 lastTagData.EndTag = 0;
515 unsigned long lastTagHash = 0;
516 while (Stop < End)
517 {
518 TrimRecord(true,End);
519
520 // this can happen when TrimRecord trims away the entire Record
521 // (e.g. because it just contains comments)
522 if(Stop == End)
523 return true;
524
525 // Start a new index and add it to the hash
526 if (isspace_ascii(Stop[0]) == 0)
527 {
528 // store the last found tag
529 if (lastTagData.EndTag != 0)
530 {
531 if (BetaIndexes[lastTagHash] != 0)
532 lastTagData.NextInBucket = BetaIndexes[lastTagHash];
533 APT_IGNORE_DEPRECATED_PUSH
534 BetaIndexes[lastTagHash] = TagCount;
535 APT_IGNORE_DEPRECATED_POP
536 d->Tags.push_back(lastTagData);
537 }
538
539 APT_IGNORE_DEPRECATED(++TagCount;)
540 lastTagData = pkgTagSectionPrivate::TagData(Stop - Section);
541 // find the colon separating tag and value
542 char const * Colon = (char const *) memchr(Stop, ':', End - Stop);
543 if (Colon == NULL)
544 return false;
545 // find the end of the tag (which might or might not be the colon)
546 char const * EndTag = Colon;
547 --EndTag;
548 for (; EndTag > Stop && isspace_ascii(*EndTag) != 0; --EndTag)
549 ;
550 ++EndTag;
551 lastTagData.EndTag = EndTag - Section;
552 lastTagHash = BetaHash(Stop, EndTag - Stop);
553 // find the beginning of the value
554 Stop = Colon + 1;
555 for (; Stop < End && isspace_ascii(*Stop) != 0; ++Stop)
556 if (*Stop == '\n' && Stop[1] != ' ')
557 break;
558 if (Stop >= End)
559 return false;
560 lastTagData.StartValue = Stop - Section;
561 }
562
563 Stop = (const char *)memchr(Stop,'\n',End - Stop);
564
565 if (Stop == 0)
566 return false;
567
568 for (; Stop+1 < End && Stop[1] == '\r'; Stop++)
569 /* nothing */
570 ;
571
572 // Double newline marks the end of the record
573 if (Stop+1 < End && Stop[1] == '\n')
574 {
575 if (lastTagData.EndTag != 0)
576 {
577 if (BetaIndexes[lastTagHash] != 0)
578 lastTagData.NextInBucket = BetaIndexes[lastTagHash];
579 APT_IGNORE_DEPRECATED(BetaIndexes[lastTagHash] = TagCount;)
580 d->Tags.push_back(lastTagData);
581 }
582
583 pkgTagSectionPrivate::TagData const td(Stop - Section);
584 d->Tags.push_back(td);
585 TrimRecord(false,End);
586 return true;
587 }
588
589 Stop++;
590 }
591
592 return false;
593 }
594 /*}}}*/
595 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
596 // ---------------------------------------------------------------------
597 /* There should be exactly 2 newline at the end of the record, no more. */
598 void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
599 {
600 if (BeforeRecord == true)
601 return;
602 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
603 }
604 /*}}}*/
605 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
606 // ---------------------------------------------------------------------
607 /* There should be exactly 1 newline at the end of the buffer, no more. */
608 void pkgTagSection::Trim()
609 {
610 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
611 }
612 /*}}}*/
613 // TagSection::Exists - return True if a tag exists /*{{{*/
614 bool pkgTagSection::Exists(StringView Tag) const
615 {
616 unsigned int tmp;
617 return Find(Tag, tmp);
618 }
619 /*}}}*/
620 // TagSection::Find - Locate a tag /*{{{*/
621 // ---------------------------------------------------------------------
622 /* This searches the section for a tag that matches the given string. */
623 bool pkgTagSection::Find(StringView TagView,unsigned int &Pos) const
624 {
625 const char * const Tag = TagView.data();
626 size_t const Length = TagView.length();
627 unsigned int Bucket = BetaIndexes[BetaHash(Tag, Length)];
628 if (Bucket == 0)
629 return false;
630
631 for (; Bucket != 0; Bucket = d->Tags[Bucket - 1].NextInBucket)
632 {
633 if ((d->Tags[Bucket - 1].EndTag - d->Tags[Bucket - 1].StartTag) != Length)
634 continue;
635
636 char const * const St = Section + d->Tags[Bucket - 1].StartTag;
637 if (strncasecmp(Tag,St,Length) != 0)
638 continue;
639
640 Pos = Bucket - 1;
641 return true;
642 }
643
644 Pos = 0;
645 return false;
646 }
647
648 bool pkgTagSection::FindInternal(unsigned int Pos, const char *&Start,
649 const char *&End) const
650 {
651 Start = Section + d->Tags[Pos].StartValue;
652 // Strip off the gunk from the end
653 End = Section + d->Tags[Pos + 1].StartTag;
654 if (unlikely(Start > End))
655 return _error->Error("Internal parsing error");
656
657 for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
658
659 return true;
660 }
661 bool pkgTagSection::Find(StringView Tag,const char *&Start,
662 const char *&End) const
663 {
664 unsigned int Pos;
665 return Find(Tag, Pos) && FindInternal(Pos, Start, End);
666 }
667 /*}}}*/
668 // TagSection::FindS - Find a string /*{{{*/
669 StringView pkgTagSection::Find(StringView Tag) const
670 {
671 const char *Start;
672 const char *End;
673 if (Find(Tag,Start,End) == false)
674 return StringView();
675 return StringView(Start, End - Start);
676 }
677 /*}}}*/
678 // TagSection::FindRawS - Find a string /*{{{*/
679 StringView pkgTagSection::FindRawInternal(unsigned int Pos) const
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 StringView pkgTagSection::FindRaw(StringView Tag) const
692 {
693 unsigned int Pos;
694 return Find(Tag, Pos) ? FindRawInternal(Pos) : "";
695 }
696 /*}}}*/
697 // TagSection::FindI - Find an integer /*{{{*/
698 // ---------------------------------------------------------------------
699 /* */
700 signed int pkgTagSection::FindIInternal(unsigned int Pos,signed long Default) const
701 {
702 const char *Start;
703 const char *Stop;
704 if (FindInternal(Pos,Start,Stop) == false)
705 return Default;
706
707 // Copy it into a temp buffer so we can use strtol
708 char S[300];
709 if ((unsigned)(Stop - Start) >= sizeof(S))
710 return Default;
711 strncpy(S,Start,Stop-Start);
712 S[Stop - Start] = 0;
713
714 errno = 0;
715 char *End;
716 signed long Result = strtol(S,&End,10);
717 if (errno == ERANGE ||
718 Result < std::numeric_limits<int>::min() || Result > std::numeric_limits<int>::max()) {
719 errno = ERANGE;
720 _error->Error(_("Cannot convert %s to integer: out of range"), S);
721 }
722 if (S == End)
723 return Default;
724 return Result;
725 }
726 signed int pkgTagSection::FindI(StringView Tag,signed long Default) const
727 {
728 unsigned int Pos;
729
730 return Find(Tag, Pos) ? FindIInternal(Pos, Default) : Default;
731 }
732 /*}}}*/
733 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
734 // ---------------------------------------------------------------------
735 /* */
736 unsigned long long pkgTagSection::FindULLInternal(unsigned int Pos, unsigned long long const &Default) const
737 {
738 const char *Start;
739 const char *Stop;
740 if (FindInternal(Pos,Start,Stop) == false)
741 return Default;
742
743 // Copy it into a temp buffer so we can use strtoull
744 char S[100];
745 if ((unsigned)(Stop - Start) >= sizeof(S))
746 return Default;
747 strncpy(S,Start,Stop-Start);
748 S[Stop - Start] = 0;
749
750 char *End;
751 unsigned long long Result = strtoull(S,&End,10);
752 if (S == End)
753 return Default;
754 return Result;
755 }
756 unsigned long long pkgTagSection::FindULL(StringView Tag, unsigned long long const &Default) const
757 {
758 unsigned int Pos;
759
760 return Find(Tag, Pos) ? FindULLInternal(Pos, Default) : Default;
761 }
762 /*}}}*/
763 // TagSection::FindB - Find boolean value /*{{{*/
764 // ---------------------------------------------------------------------
765 /* */
766 bool pkgTagSection::FindBInternal(unsigned int Pos, bool Default) const
767 {
768 const char *Start, *Stop;
769 if (FindInternal(Pos, Start, Stop) == false)
770 return Default;
771 return StringToBool(string(Start, Stop));
772 }
773 bool pkgTagSection::FindB(StringView Tag, bool Default) const
774 {
775 unsigned int Pos;
776 return Find(Tag, Pos) ? FindBInternal(Pos, Default) : Default;
777 }
778 /*}}}*/
779 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
780 // ---------------------------------------------------------------------
781 /* The bits marked in Flag are masked on/off in Flags */
782 bool pkgTagSection::FindFlagInternal(unsigned int Pos, uint8_t &Flags,
783 uint8_t const Flag) const
784 {
785 const char *Start;
786 const char *Stop;
787 if (FindInternal(Pos,Start,Stop) == false)
788 return true;
789 return FindFlag(Flags, Flag, Start, Stop);
790 }
791 bool pkgTagSection::FindFlag(StringView Tag, uint8_t &Flags,
792 uint8_t const Flag) const
793 {
794 unsigned int Pos;
795 if (Find(Tag,Pos) == false)
796 return true;
797 return FindFlagInternal(Pos, Flags, Flag);
798 }
799 bool pkgTagSection::FindFlag(uint8_t &Flags, uint8_t const Flag,
800 char const* const Start, char const* const Stop)
801 {
802 switch (StringToBool(string(Start, Stop)))
803 {
804 case 0:
805 Flags &= ~Flag;
806 return true;
807
808 case 1:
809 Flags |= Flag;
810 return true;
811
812 default:
813 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
814 return true;
815 }
816 return true;
817 }
818 bool pkgTagSection::FindFlagInternal(unsigned int Pos,unsigned long &Flags,
819 unsigned long Flag) const
820 {
821 const char *Start;
822 const char *Stop;
823 if (FindInternal(Pos,Start,Stop) == false)
824 return true;
825 return FindFlag(Flags, Flag, Start, Stop);
826 }
827 bool pkgTagSection::FindFlag(StringView Tag,unsigned long &Flags,
828 unsigned long Flag) const
829 {
830 unsigned int Pos;
831 return Find(Tag, Pos) ? FindFlagInternal(Pos, Flags, Flag) : true;
832 }
833 bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
834 char const* Start, char const* Stop)
835 {
836 switch (StringToBool(string(Start, Stop)))
837 {
838 case 0:
839 Flags &= ~Flag;
840 return true;
841
842 case 1:
843 Flags |= Flag;
844 return true;
845
846 default:
847 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
848 return true;
849 }
850 return true;
851 }
852 /*}}}*/
853 void pkgTagSection::Get(const char *&Start,const char *&Stop,unsigned int I) const/*{{{*/
854 {
855 Start = Section + d->Tags[I].StartTag;
856 Stop = Section + d->Tags[I+1].StartTag;
857 }
858 /*}}}*/
859 APT_PURE unsigned int pkgTagSection::Count() const { /*{{{*/
860 if (d->Tags.empty() == true)
861 return 0;
862 // the last element is just marking the end and isn't a real one
863 return d->Tags.size() - 1;
864 }
865 /*}}}*/
866 // TagSection::Write - Ordered (re)writing of fields /*{{{*/
867 pkgTagSection::Tag pkgTagSection::Tag::Remove(std::string const &Name)
868 {
869 return Tag(REMOVE, Name, "");
870 }
871 pkgTagSection::Tag pkgTagSection::Tag::Rename(std::string const &OldName, std::string const &NewName)
872 {
873 return Tag(RENAME, OldName, NewName);
874 }
875 pkgTagSection::Tag pkgTagSection::Tag::Rewrite(std::string const &Name, std::string const &Data)
876 {
877 if (Data.empty() == true)
878 return Tag(REMOVE, Name, "");
879 else
880 return Tag(REWRITE, Name, Data);
881 }
882 static bool WriteTag(FileFd &File, std::string Tag, StringView Value)
883 {
884 if (Value.empty() || isspace_ascii(Value[0]) != 0)
885 Tag.append(":");
886 else
887 Tag.append(": ");
888 Tag.append(Value.data(), Value.length());
889 Tag.append("\n");
890 return File.Write(Tag.c_str(), Tag.length());
891 }
892 static bool RewriteTags(FileFd &File, pkgTagSection const * const This, char const * const Tag,
893 std::vector<pkgTagSection::Tag>::const_iterator &R,
894 std::vector<pkgTagSection::Tag>::const_iterator const &REnd)
895 {
896 size_t const TagLen = strlen(Tag);
897 for (; R != REnd; ++R)
898 {
899 std::string data;
900 if (R->Name.length() == TagLen && strncasecmp(R->Name.c_str(), Tag, R->Name.length()) == 0)
901 {
902 if (R->Action != pkgTagSection::Tag::REWRITE)
903 break;
904 data = R->Data;
905 }
906 else if(R->Action == pkgTagSection::Tag::RENAME && R->Data.length() == TagLen &&
907 strncasecmp(R->Data.c_str(), Tag, R->Data.length()) == 0)
908 data = This->FindRaw(R->Name.c_str()).to_string();
909 else
910 continue;
911
912 return WriteTag(File, Tag, data);
913 }
914 return true;
915 }
916 bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::vector<Tag> const &Rewrite) const
917 {
918 // first pass: Write everything we have an order for
919 if (Order != NULL)
920 {
921 for (unsigned int I = 0; Order[I] != 0; ++I)
922 {
923 std::vector<Tag>::const_iterator R = Rewrite.begin();
924 if (RewriteTags(File, this, Order[I], R, Rewrite.end()) == false)
925 return false;
926 if (R != Rewrite.end())
927 continue;
928
929 if (Exists(Order[I]) == false)
930 continue;
931
932 if (WriteTag(File, Order[I], FindRaw(Order[I])) == false)
933 return false;
934 }
935 }
936 // second pass: See if we have tags which aren't ordered
937 if (d->Tags.empty() == false)
938 {
939 for (std::vector<pkgTagSectionPrivate::TagData>::const_iterator T = d->Tags.begin(); T != d->Tags.end() - 1; ++T)
940 {
941 char const * const fieldname = Section + T->StartTag;
942 size_t fieldnamelen = T->EndTag - T->StartTag;
943 if (Order != NULL)
944 {
945 unsigned int I = 0;
946 for (; Order[I] != 0; ++I)
947 {
948 if (fieldnamelen == strlen(Order[I]) && strncasecmp(fieldname, Order[I], fieldnamelen) == 0)
949 break;
950 }
951 if (Order[I] != 0)
952 continue;
953 }
954
955 std::string const name(fieldname, fieldnamelen);
956 std::vector<Tag>::const_iterator R = Rewrite.begin();
957 if (RewriteTags(File, this, name.c_str(), R, Rewrite.end()) == false)
958 return false;
959 if (R != Rewrite.end())
960 continue;
961
962 if (WriteTag(File, name, FindRaw(name)) == false)
963 return false;
964 }
965 }
966 // last pass: see if there are any rewrites remaining we haven't done yet
967 for (std::vector<Tag>::const_iterator R = Rewrite.begin(); R != Rewrite.end(); ++R)
968 {
969 if (R->Action == Tag::REMOVE)
970 continue;
971 std::string const name = ((R->Action == Tag::RENAME) ? R->Data : R->Name);
972 if (Exists(name.c_str()))
973 continue;
974 if (Order != NULL)
975 {
976 unsigned int I = 0;
977 for (; Order[I] != 0; ++I)
978 {
979 if (strncasecmp(name.c_str(), Order[I], name.length()) == 0 && name.length() == strlen(Order[I]))
980 break;
981 }
982 if (Order[I] != 0)
983 continue;
984 }
985
986 if (WriteTag(File, name, ((R->Action == Tag::RENAME) ? FindRaw(R->Name) : R->Data)) == false)
987 return false;
988 }
989 return true;
990 }
991 /*}}}*/
992
993 void pkgUserTagSection::TrimRecord(bool /*BeforeRecord*/, const char* &End)/*{{{*/
994 {
995 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r' || Stop[0] == '#'); Stop++)
996 if (Stop[0] == '#')
997 Stop = (const char*) memchr(Stop,'\n',End-Stop);
998 }
999 /*}}}*/
1000
1001 #include "tagfile-order.c"
1002
1003 // TFRewrite - Rewrite a control record /*{{{*/
1004 // ---------------------------------------------------------------------
1005 /* This writes the control record to stdout rewriting it as necessary. The
1006 override map item specificies the rewriting rules to follow. This also
1007 takes the time to sort the feild list. */
1008 APT_IGNORE_DEPRECATED_PUSH
1009 bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
1010 TFRewriteData *Rewrite)
1011 {
1012 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
1013 for (unsigned I = 0; I != 256; I++)
1014 Visited[I] = 0;
1015
1016 // Set new tag up as necessary.
1017 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1018 {
1019 if (Rewrite[J].NewTag == 0)
1020 Rewrite[J].NewTag = Rewrite[J].Tag;
1021 }
1022
1023 // Write all all of the tags, in order.
1024 if (Order != NULL)
1025 {
1026 for (unsigned int I = 0; Order[I] != 0; I++)
1027 {
1028 bool Rewritten = false;
1029
1030 // See if this is a field that needs to be rewritten
1031 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1032 {
1033 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
1034 {
1035 Visited[J] |= 2;
1036 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
1037 {
1038 if (isspace_ascii(Rewrite[J].Rewrite[0]))
1039 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1040 else
1041 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1042 }
1043 Rewritten = true;
1044 break;
1045 }
1046 }
1047
1048 // See if it is in the fragment
1049 unsigned Pos;
1050 if (Tags.Find(StringView(Order[I]),Pos) == false)
1051 continue;
1052 Visited[Pos] |= 1;
1053
1054 if (Rewritten == true)
1055 continue;
1056
1057 /* Write out this element, taking a moment to rewrite the tag
1058 in case of changes of case. */
1059 const char *Start;
1060 const char *Stop;
1061 Tags.Get(Start,Stop,Pos);
1062
1063 if (fputs(Order[I],Output) < 0)
1064 return _error->Errno("fputs","IO Error to output");
1065 Start += strlen(Order[I]);
1066 if (fwrite(Start,Stop - Start,1,Output) != 1)
1067 return _error->Errno("fwrite","IO Error to output");
1068 if (Stop[-1] != '\n')
1069 fprintf(Output,"\n");
1070 }
1071 }
1072
1073 // Now write all the old tags that were missed.
1074 for (unsigned int I = 0; I != Tags.Count(); I++)
1075 {
1076 if ((Visited[I] & 1) == 1)
1077 continue;
1078
1079 const char *Start;
1080 const char *Stop;
1081 Tags.Get(Start,Stop,I);
1082 const char *End = Start;
1083 for (; End < Stop && *End != ':'; End++);
1084
1085 // See if this is a field that needs to be rewritten
1086 bool Rewritten = false;
1087 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1088 {
1089 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
1090 {
1091 Visited[J] |= 2;
1092 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
1093 {
1094 if (isspace_ascii(Rewrite[J].Rewrite[0]))
1095 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1096 else
1097 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1098 }
1099
1100 Rewritten = true;
1101 break;
1102 }
1103 }
1104
1105 if (Rewritten == true)
1106 continue;
1107
1108 // Write out this element
1109 if (fwrite(Start,Stop - Start,1,Output) != 1)
1110 return _error->Errno("fwrite","IO Error to output");
1111 if (Stop[-1] != '\n')
1112 fprintf(Output,"\n");
1113 }
1114
1115 // Now write all the rewrites that were missed
1116 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1117 {
1118 if ((Visited[J] & 2) == 2)
1119 continue;
1120
1121 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
1122 {
1123 if (isspace_ascii(Rewrite[J].Rewrite[0]))
1124 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1125 else
1126 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1127 }
1128 }
1129
1130 return true;
1131 }
1132 APT_IGNORE_DEPRECATED_POP
1133 /*}}}*/
1134
1135 pkgTagSection::~pkgTagSection() { delete d; }