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