apt  2.2.4
About: Apt (Advanced Package Tool) is a management system for software packages (Debian/Ubuntu). Release series 2.2.
  Fossies Dox: apt-2.2.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

tagfile.cc
Go to the documentation of this file.
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4 
5  Fast scanner for RFC-822 type header information
6 
7  This uses a rotating buffer to load the package information into.
8  The scanner runs over it and isolates and indexes a single section.
9 
10  ##################################################################### */
11  /*}}}*/
12 // Include Files /*{{{*/
13 #include <config.h>
14 
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/fileutl.h>
17 #include <apt-pkg/string_view.h>
18 #include <apt-pkg/strutl.h>
19 #include <apt-pkg/tagfile-keys.h>
20 #include <apt-pkg/tagfile.h>
21 
22 #include <list>
23 
24 #include <string>
25 #include <ctype.h>
26 #include <stdio.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 
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  }
61  char *Buffer;
62  char *Start;
63  char *End;
64  bool Done;
65  unsigned long long iOffset;
66  unsigned long long Size;
68  struct FileChunk
69  {
70  bool const good;
71  size_t length;
72  FileChunk(bool const pgood, size_t const plength) noexcept : good(pgood), length(plength) {}
73  };
74  std::list<FileChunk> chunks;
75 
77  {
78  if (Buffer != NULL)
79  free(Buffer);
80  }
81 };
82  /*}}}*/
84 {
85 public:
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 /*{{{*/
158 {
159  delete d;
160 }
161  /*}}}*/
162 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
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  */
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  */
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  if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) == 0 || Tag.Count() != 0)
254  {
255  Tag.Trim();
256  return true;
257  }
258  return Step(Tag);
259 }
260  /*}}}*/
261 // TagFile::Fill - Top up the buffer /*{{{*/
262 // ---------------------------------------------------------------------
263 /* This takes the bit at the end of the buffer and puts it at the start
264  then fills the rest from the file */
265 static bool FillBuffer(pkgTagFilePrivate * const d)
266 {
267  unsigned long long Actual = 0;
268  // See if only a bit of the file is left
269  unsigned long long const dataSize = d->Size - ((d->End - d->Buffer) + 1);
270  if (d->Fd->Read(d->End, dataSize, &Actual) == false)
271  return false;
272  if (Actual != dataSize)
273  d->Done = true;
274  d->End += Actual;
275  return true;
276 }
278 {
279  // look for valid comments in the buffer
280  char * good_start = nullptr, * bad_start = nullptr;
281  char * current = d->Start;
282  if (d->isCommentedLine == false)
283  {
284  if (d->Start == d->Buffer)
285  {
286  // the start of the buffer is a newline as a record can't start
287  // in the middle of a line by definition.
288  if (*d->Start == '#')
289  {
290  d->isCommentedLine = true;
291  ++current;
292  if (current > d->End)
293  d->chunks.emplace_back(false, 1);
294  }
295  }
296  if (d->isCommentedLine == false)
297  good_start = d->Start;
298  else
299  bad_start = d->Start;
300  }
301  else
302  bad_start = d->Start;
303 
304  std::vector<std::pair<char*, size_t>> good_parts;
305  while (current <= d->End)
306  {
307  size_t const restLength = (d->End - current);
308  if (d->isCommentedLine == false)
309  {
310  current = static_cast<char*>(memchr(current, '#', restLength));
311  if (current == nullptr)
312  {
313  size_t const goodLength = d->End - good_start;
314  d->chunks.emplace_back(true, goodLength);
315  if (good_start != d->Start)
316  good_parts.push_back(std::make_pair(good_start, goodLength));
317  break;
318  }
319  bad_start = current;
320  --current;
321  // ensure that this is really a comment and not a '#' in the middle of a line
322  if (*current == '\n')
323  {
324  size_t const goodLength = (current - good_start) + 1;
325  d->chunks.emplace_back(true, goodLength);
326  good_parts.push_back(std::make_pair(good_start, goodLength));
327  good_start = nullptr;
328  d->isCommentedLine = true;
329  }
330  current += 2;
331  }
332  else // the current line is a comment
333  {
334  current = static_cast<char*>(memchr(current, '\n', restLength));
335  if (current == nullptr)
336  {
337  d->chunks.emplace_back(false, (d->End - bad_start));
338  break;
339  }
340  ++current;
341  // is the next line a comment, too?
342  if (current >= d->End || *current != '#')
343  {
344  d->chunks.emplace_back(false, (current - bad_start));
345  good_start = current;
346  bad_start = nullptr;
347  d->isCommentedLine = false;
348  }
349  ++current;
350  }
351  }
352 
353  if (good_parts.empty() == false)
354  {
355  // we found comments, so move later parts over them
356  current = d->Start;
357  for (auto const &good: good_parts)
358  {
359  memmove(current, good.first, good.second);
360  current += good.second;
361  }
362  d->End = current;
363  }
364 
365  if (d->isCommentedLine == true)
366  {
367  // deal with a buffer containing only comments
368  // or an (unfinished) comment at the end
369  if (good_parts.empty() == true)
370  d->End = d->Start;
371  else
372  d->Start = d->End;
373  }
374  else
375  {
376  // the buffer was all comment, but ended with the buffer
377  if (good_parts.empty() == true && good_start >= d->End)
378  d->End = d->Start;
379  else
380  d->Start = d->End;
381  }
382 }
384 {
385  unsigned long long const EndSize = d->End - d->Start;
386  if (EndSize != 0)
387  {
388  memmove(d->Buffer,d->Start,EndSize);
389  d->Start = d->End = d->Buffer + EndSize;
390  }
391  else
392  d->Start = d->End = d->Buffer;
393 
394  unsigned long long Actual = 0;
395  while (d->Done == false && d->Size > (Actual + 1))
396  {
397  if (FillBuffer(d) == false)
398  return false;
399  if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) != 0)
401  Actual = d->End - d->Buffer;
402  }
403  d->Start = d->Buffer;
404 
405  if (d->Done == true)
406  {
407  if (EndSize <= 3 && Actual == 0)
408  return false;
409  if (d->Size - (d->End - d->Buffer) < 4)
410  return true;
411 
412  // Append a double new line if one does not exist
413  unsigned int LineCount = 0;
414  for (const char *E = d->End - 1; E - d->End < 6 && (*E == '\n' || *E == '\r'); E--)
415  if (*E == '\n')
416  ++LineCount;
417  if (LineCount < 2)
418  {
419  if (static_cast<unsigned long long>(d->End - d->Buffer) >= d->Size)
420  Resize(d->Size + 3);
421  for (; LineCount < 2; ++LineCount)
422  *d->End++ = '\n';
423  }
424  }
425  return true;
426 }
427  /*}}}*/
428 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
429 // ---------------------------------------------------------------------
430 /* This jumps to a pre-recorded file location and reads the record
431  that is there */
432 bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
433 {
434  if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) == 0 &&
435  // We are within a buffer space of the next hit..
436  Offset >= d->iOffset && d->iOffset + (d->End - d->Start) > Offset)
437  {
438  unsigned long long Dist = Offset - d->iOffset;
439  d->Start += Dist;
440  d->iOffset += Dist;
441  // if we have seen the end, don't ask for more
442  if (d->Done == true)
443  return Tag.Scan(d->Start, d->End - d->Start);
444  else
445  return Step(Tag);
446  }
447 
448  // Reposition and reload..
449  d->iOffset = Offset;
450  d->Done = false;
451  if (d->Fd->Seek(Offset) == false)
452  return false;
453  d->End = d->Start = d->Buffer;
454  d->isCommentedLine = false;
455  d->chunks.clear();
456 
457  if (Fill() == false)
458  return false;
459 
460  if (Tag.Scan(d->Start, d->End - d->Start) == true)
461  return true;
462 
463  // This appends a double new line (for the real eof handling)
464  if (Fill() == false)
465  return false;
466 
467  if (Tag.Scan(d->Start, d->End - d->Start, false) == false)
468  return _error->Error(_("Unable to parse package file %s (%d)"),d->Fd->Name().c_str(), 2);
469 
470  return true;
471 }
472  /*}}}*/
473 // pkgTagSection::pkgTagSection - Constructor /*{{{*/
474 // ---------------------------------------------------------------------
475 /* */
477  : Section(0), d(new pkgTagSectionPrivate()), Stop(0)
478 {
479  memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
480  memset(&BetaIndexes, 0, sizeof(BetaIndexes));
481 }
482  /*}}}*/
483 // TagSection::Scan - Scan for the end of the header information /*{{{*/
484 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const Restart)
485 {
486  Section = Start;
487  const char *End = Start + MaxLength;
488 
489  if (Restart == false && d->Tags.empty() == false)
490  {
491  Stop = Section + d->Tags.back().StartTag;
492  if (End <= Stop)
493  return false;
494  Stop = (const char *)memchr(Stop,'\n',End - Stop);
495  if (Stop == NULL)
496  return false;
497  ++Stop;
498  }
499  else
500  {
501  Stop = Section;
502  if (d->Tags.empty() == false)
503  {
504  memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
505  memset(&BetaIndexes, 0, sizeof(BetaIndexes));
506  d->Tags.clear();
507  }
508  d->Tags.reserve(0x100);
509  }
510  unsigned int TagCount = d->Tags.size();
511 
512  if (Stop == 0)
513  return false;
514 
515  pkgTagSectionPrivate::TagData lastTagData(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.StartValue != 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  BetaIndexes[lastTagHash] = TagCount;
539  }
540  d->Tags.push_back(lastTagData);
541  }
542 
543  ++TagCount;
544  lastTagData = pkgTagSectionPrivate::TagData(Stop - Section);
545  // find the colon separating tag and value
546  char const * Colon = (char const *) memchr(Stop, ':', End - Stop);
547  if (Colon == NULL)
548  return false;
549  // find the end of the tag (which might or might not be the colon)
550  char const * EndTag = Colon;
551  --EndTag;
552  for (; EndTag > Stop && isspace_ascii(*EndTag) != 0; --EndTag)
553  ;
554  ++EndTag;
555  lastTagData.EndTag = EndTag - Section;
556  lastTagKey = pkgTagHash(Stop, EndTag - Stop);
557  if (lastTagKey == Key::Unknown)
558  lastTagHash = BetaHash(Stop, EndTag - Stop);
559  // find the beginning of the value
560  Stop = Colon + 1;
561  for (; Stop < End && isspace_ascii(*Stop) != 0; ++Stop)
562  if (*Stop == '\n' && Stop[1] != ' ')
563  break;
564  if (Stop >= End)
565  return false;
566  lastTagData.StartValue = Stop - Section;
567  }
568 
569  Stop = (const char *)memchr(Stop,'\n',End - Stop);
570 
571  if (Stop == 0)
572  return false;
573 
574  for (; Stop+1 < End && Stop[1] == '\r'; Stop++)
575  /* nothing */
576  ;
577 
578  // Double newline marks the end of the record
579  if (Stop+1 < End && Stop[1] == '\n')
580  {
581  if (lastTagData.StartValue != 0)
582  {
583  if (lastTagKey != Key::Unknown) {
584  AlphaIndexes[static_cast<size_t>(lastTagKey)] = TagCount;
585  } else {
586  if (BetaIndexes[lastTagHash] != 0)
587  lastTagData.NextInBucket = BetaIndexes[lastTagHash];
588  BetaIndexes[lastTagHash] = TagCount;
589  }
590  d->Tags.push_back(lastTagData);
591  }
592 
594  d->Tags.push_back(td);
595  TrimRecord(false,End);
596  return true;
597  }
598 
599  Stop++;
600  }
601 
602  return false;
603 }
604  /*}}}*/
605 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
606 // ---------------------------------------------------------------------
607 /* There should be exactly 2 newline at the end of the record, no more. */
608 void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
609 {
610  if (BeforeRecord == true)
611  return;
612  for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
613 }
614  /*}}}*/
615 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
616 // ---------------------------------------------------------------------
617 /* There should be exactly 1 newline at the end of the buffer, no more. */
619 {
620  for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
621 }
622  /*}}}*/
623 // TagSection::Exists - return True if a tag exists /*{{{*/
625 {
626  unsigned int tmp;
627  return Find(Tag, tmp);
628 }
629  /*}}}*/
630 // TagSection::Find - Locate a tag /*{{{*/
631 // ---------------------------------------------------------------------
632 /* This searches the section for a tag that matches the given string. */
633 bool pkgTagSection::Find(Key key,unsigned int &Pos) const
634 {
635  auto Bucket = AlphaIndexes[static_cast<size_t>(key)];
636  Pos = Bucket - 1;
637  return Bucket != 0;
638 }
639 bool pkgTagSection::Find(StringView TagView,unsigned int &Pos) const
640 {
641  const char * const Tag = TagView.data();
642  size_t const Length = TagView.length();
643  auto key = pkgTagHash(Tag, Length);
644  if (key != Key::Unknown)
645  return Find(key, Pos);
646 
647  unsigned int Bucket = BetaIndexes[BetaHash(Tag, Length)];
648  if (Bucket == 0)
649  return false;
650 
651  for (; Bucket != 0; Bucket = d->Tags[Bucket - 1].NextInBucket)
652  {
653  if ((d->Tags[Bucket - 1].EndTag - d->Tags[Bucket - 1].StartTag) != Length)
654  continue;
655 
656  char const * const St = Section + d->Tags[Bucket - 1].StartTag;
657  if (strncasecmp(Tag,St,Length) != 0)
658  continue;
659 
660  Pos = Bucket - 1;
661  return true;
662  }
663 
664  Pos = 0;
665  return false;
666 }
667 
668 bool pkgTagSection::FindInternal(unsigned int Pos, const char *&Start,
669  const char *&End) const
670 {
671  if (unlikely(Pos + 1 >= d->Tags.size() || Pos >= d->Tags.size()))
672  return _error->Error("Internal parsing error");
673 
674  Start = Section + d->Tags[Pos].StartValue;
675  // Strip off the gunk from the end
676  End = Section + d->Tags[Pos + 1].StartTag;
677  if (unlikely(Start > End))
678  return _error->Error("Internal parsing error");
679 
680  for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
681 
682  return true;
683 }
684 bool pkgTagSection::Find(StringView Tag,const char *&Start,
685  const char *&End) const
686 {
687  unsigned int Pos;
688  return Find(Tag, Pos) && FindInternal(Pos, Start, End);
689 }
690 bool pkgTagSection::Find(Key key,const char *&Start,
691  const char *&End) const
692 {
693  unsigned int Pos;
694  return Find(key, Pos) && FindInternal(Pos, Start, End);
695 }
696  /*}}}*/
697 // TagSection::FindS - Find a string /*{{{*/
699 {
700  const char *Start;
701  const char *End;
702  if (Find(Tag,Start,End) == false)
703  return StringView();
704  return StringView(Start, End - Start);
705 }
707 {
708  const char *Start;
709  const char *End;
710  if (Find(key,Start,End) == false)
711  return StringView();
712  return StringView(Start, End - Start);
713 }
714  /*}}}*/
715 // TagSection::FindRawS - Find a string /*{{{*/
717 {
718  if (unlikely(Pos + 1 >= d->Tags.size() || Pos >= d->Tags.size()))
719  return _error->Error("Internal parsing error"), "";
720 
721  char const *Start = (char const *) memchr(Section + d->Tags[Pos].EndTag, ':', d->Tags[Pos].StartValue - d->Tags[Pos].EndTag);
722  char const *End = Section + d->Tags[Pos + 1].StartTag;
723 
724  if (Start == nullptr)
725  return "";
726 
727  ++Start;
728 
729  if (unlikely(Start > End))
730  return "";
731 
732  for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
733 
734  return StringView(Start, End - Start);
735 }
737 {
738  unsigned int Pos;
739  return Find(Tag, Pos) ? FindRawInternal(Pos) : "";
740 }
742 {
743  unsigned int Pos;
744  return Find(key, Pos) ? FindRawInternal(Pos) : "";
745 }
746  /*}}}*/
747 // TagSection::FindI - Find an integer /*{{{*/
748 // ---------------------------------------------------------------------
749 /* */
750 signed int pkgTagSection::FindIInternal(unsigned int Pos,signed long Default) const
751 {
752  const char *Start;
753  const char *Stop;
754  if (FindInternal(Pos,Start,Stop) == false)
755  return Default;
756 
757  // Copy it into a temp buffer so we can use strtol
758  char S[300];
759  if ((unsigned)(Stop - Start) >= sizeof(S))
760  return Default;
761  strncpy(S,Start,Stop-Start);
762  S[Stop - Start] = 0;
763 
764  errno = 0;
765  char *End;
766  signed long Result = strtol(S,&End,10);
767  if (errno == ERANGE ||
768  Result < std::numeric_limits<int>::min() || Result > std::numeric_limits<int>::max()) {
769  errno = ERANGE;
770  _error->Error(_("Cannot convert %s to integer: out of range"), S);
771  }
772  if (S == End)
773  return Default;
774  return Result;
775 }
776 signed int pkgTagSection::FindI(Key key,signed long Default) const
777 {
778  unsigned int Pos;
779 
780  return Find(key, Pos) ? FindIInternal(Pos) : Default;
781 }
782 signed int pkgTagSection::FindI(StringView Tag,signed long Default) const
783 {
784  unsigned int Pos;
785 
786  return Find(Tag, Pos) ? FindIInternal(Pos, Default) : Default;
787 }
788  /*}}}*/
789 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
790 // ---------------------------------------------------------------------
791 /* */
792 unsigned long long pkgTagSection::FindULLInternal(unsigned int Pos, unsigned long long const &Default) const
793 {
794  const char *Start;
795  const char *Stop;
796  if (FindInternal(Pos,Start,Stop) == false)
797  return Default;
798 
799  // Copy it into a temp buffer so we can use strtoull
800  char S[100];
801  if ((unsigned)(Stop - Start) >= sizeof(S))
802  return Default;
803  strncpy(S,Start,Stop-Start);
804  S[Stop - Start] = 0;
805 
806  char *End;
807  unsigned long long Result = strtoull(S,&End,10);
808  if (S == End)
809  return Default;
810  return Result;
811 }
812 unsigned long long pkgTagSection::FindULL(Key key, unsigned long long const &Default) const
813 {
814  unsigned int Pos;
815 
816  return Find(key, Pos) ? FindULLInternal(Pos, Default) : Default;
817 }
818 unsigned long long pkgTagSection::FindULL(StringView Tag, unsigned long long const &Default) const
819 {
820  unsigned int Pos;
821 
822  return Find(Tag, Pos) ? FindULLInternal(Pos, Default) : Default;
823 }
824  /*}}}*/
825 // TagSection::FindB - Find boolean value /*{{{*/
826 // ---------------------------------------------------------------------
827 /* */
828 bool pkgTagSection::FindBInternal(unsigned int Pos, bool Default) const
829 {
830  const char *Start, *Stop;
831  if (FindInternal(Pos, Start, Stop) == false)
832  return Default;
833  return StringToBool(string(Start, Stop));
834 }
835 bool pkgTagSection::FindB(Key key, bool Default) const
836 {
837  unsigned int Pos;
838  return Find(key, Pos) ? FindBInternal(Pos, Default): Default;
839 }
840 bool pkgTagSection::FindB(StringView Tag, bool Default) const
841 {
842  unsigned int Pos;
843  return Find(Tag, Pos) ? FindBInternal(Pos, Default) : Default;
844 }
845  /*}}}*/
846 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
847 // ---------------------------------------------------------------------
848 /* The bits marked in Flag are masked on/off in Flags */
849 bool pkgTagSection::FindFlagInternal(unsigned int Pos, uint8_t &Flags,
850  uint8_t const Flag) const
851 {
852  const char *Start;
853  const char *Stop;
854  if (FindInternal(Pos,Start,Stop) == false)
855  return true;
856  return FindFlag(Flags, Flag, Start, Stop);
857 }
858 bool pkgTagSection::FindFlag(Key key, uint8_t &Flags,
859  uint8_t const Flag) const
860 {
861  unsigned int Pos;
862  if (Find(key,Pos) == false)
863  return true;
864  return FindFlagInternal(Pos, Flags, Flag);
865 }
867  uint8_t const Flag) const
868 {
869  unsigned int Pos;
870  if (Find(Tag,Pos) == false)
871  return true;
872  return FindFlagInternal(Pos, Flags, Flag);
873 }
874 bool pkgTagSection::FindFlag(uint8_t &Flags, uint8_t const Flag,
875  char const* const Start, char const* const Stop)
876 {
877  switch (StringToBool(string(Start, Stop)))
878  {
879  case 0:
880  Flags &= ~~Flag;
881  return true;
882 
883  case 1:
884  Flags |= Flag;
885  return true;
886 
887  default:
888  _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
889  return true;
890  }
891  return true;
892 }
893 bool pkgTagSection::FindFlagInternal(unsigned int Pos,unsigned long &Flags,
894  unsigned long Flag) const
895 {
896  const char *Start;
897  const char *Stop;
898  if (FindInternal(Pos,Start,Stop) == false)
899  return true;
900  return FindFlag(Flags, Flag, Start, Stop);
901 }
902 bool pkgTagSection::FindFlag(Key key,unsigned long &Flags,
903  unsigned long Flag) const
904 {
905  unsigned int Pos;
906  return Find(key, Pos) ? FindFlagInternal(Pos, Flags, Flag) : true;
907 }
909  unsigned long Flag) const
910 {
911  unsigned int Pos;
912  return Find(Tag, Pos) ? FindFlagInternal(Pos, Flags, Flag) : true;
913 }
914 bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
915  char const* Start, char const* Stop)
916 {
917  switch (StringToBool(string(Start, Stop)))
918  {
919  case 0:
920  Flags &= ~~Flag;
921  return true;
922 
923  case 1:
924  Flags |= Flag;
925  return true;
926 
927  default:
928  _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
929  return true;
930  }
931  return true;
932 }
933  /*}}}*/
934 void pkgTagSection::Get(const char *&Start,const char *&Stop,unsigned int I) const/*{{{*/
935 {
936  if (unlikely(I + 1 >= d->Tags.size() || I >= d->Tags.size()))
937  abort();
938  Start = Section + d->Tags[I].StartTag;
939  Stop = Section + d->Tags[I+1].StartTag;
940 }
941  /*}}}*/
942 APT_PURE unsigned int pkgTagSection::Count() const { /*{{{*/
943  if (d->Tags.empty() == true)
944  return 0;
945  // the last element is just marking the end and isn't a real one
946  return d->Tags.size() - 1;
947 }
948  /*}}}*/
949 // TagSection::Write - Ordered (re)writing of fields /*{{{*/
951 {
952  return Tag(REMOVE, Name, "");
953 }
954 pkgTagSection::Tag pkgTagSection::Tag::Rename(std::string const &OldName, std::string const &NewName)
955 {
956  return Tag(RENAME, OldName, NewName);
957 }
958 pkgTagSection::Tag pkgTagSection::Tag::Rewrite(std::string const &Name, std::string const &Data)
959 {
960  if (Data.empty() == true)
961  return Tag(REMOVE, Name, "");
962  else
963  return Tag(REWRITE, Name, Data);
964 }
965 static bool WriteTag(FileFd &File, std::string Tag, StringView Value)
966 {
967  if (Value.empty() || isspace_ascii(Value[0]) != 0)
968  Tag.append(":");
969  else
970  Tag.append(": ");
971  Tag.append(Value.data(), Value.length());
972  Tag.append("\n");
973  return File.Write(Tag.c_str(), Tag.length());
974 }
975 static bool RewriteTags(FileFd &File, pkgTagSection const * const This, char const * const Tag,
976  std::vector<pkgTagSection::Tag>::const_iterator &R,
977  std::vector<pkgTagSection::Tag>::const_iterator const &REnd)
978 {
979  size_t const TagLen = strlen(Tag);
980  for (; R != REnd; ++R)
981  {
982  std::string data;
983  if (R->Name.length() == TagLen && strncasecmp(R->Name.c_str(), Tag, R->Name.length()) == 0)
984  {
985  if (R->Action != pkgTagSection::Tag::REWRITE)
986  break;
987  data = R->Data;
988  }
989  else if(R->Action == pkgTagSection::Tag::RENAME && R->Data.length() == TagLen &&
990  strncasecmp(R->Data.c_str(), Tag, R->Data.length()) == 0)
991  data = This->FindRaw(R->Name.c_str()).to_string();
992  else
993  continue;
994 
995  return WriteTag(File, Tag, data);
996  }
997  return true;
998 }
999 bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::vector<Tag> const &Rewrite) const
1000 {
1001  // first pass: Write everything we have an order for
1002  if (Order != NULL)
1003  {
1004  for (unsigned int I = 0; Order[I] != 0; ++I)
1005  {
1006  std::vector<Tag>::const_iterator R = Rewrite.begin();
1007  if (RewriteTags(File, this, Order[I], R, Rewrite.end()) == false)
1008  return false;
1009  if (R != Rewrite.end())
1010  continue;
1011 
1012  if (Exists(Order[I]) == false)
1013  continue;
1014 
1015  if (WriteTag(File, Order[I], FindRaw(Order[I])) == false)
1016  return false;
1017  }
1018  }
1019  // second pass: See if we have tags which aren't ordered
1020  if (d->Tags.empty() == false)
1021  {
1022  for (std::vector<pkgTagSectionPrivate::TagData>::const_iterator T = d->Tags.begin(); T != d->Tags.end() - 1; ++T)
1023  {
1024  char const * const fieldname = Section + T->StartTag;
1025  size_t fieldnamelen = T->EndTag - T->StartTag;
1026  if (Order != NULL)
1027  {
1028  unsigned int I = 0;
1029  for (; Order[I] != 0; ++I)
1030  {
1031  if (fieldnamelen == strlen(Order[I]) && strncasecmp(fieldname, Order[I], fieldnamelen) == 0)
1032  break;
1033  }
1034  if (Order[I] != 0)
1035  continue;
1036  }
1037 
1038  std::string const name(fieldname, fieldnamelen);
1039  std::vector<Tag>::const_iterator R = Rewrite.begin();
1040  if (RewriteTags(File, this, name.c_str(), R, Rewrite.end()) == false)
1041  return false;
1042  if (R != Rewrite.end())
1043  continue;
1044 
1045  if (WriteTag(File, name, FindRaw(name)) == false)
1046  return false;
1047  }
1048  }
1049  // last pass: see if there are any rewrites remaining we haven't done yet
1050  for (std::vector<Tag>::const_iterator R = Rewrite.begin(); R != Rewrite.end(); ++R)
1051  {
1052  if (R->Action == Tag::REMOVE)
1053  continue;
1054  std::string const name = ((R->Action == Tag::RENAME) ? R->Data : R->Name);
1055  if (Exists(name.c_str()))
1056  continue;
1057  if (Order != NULL)
1058  {
1059  unsigned int I = 0;
1060  for (; Order[I] != 0; ++I)
1061  {
1062  if (strncasecmp(name.c_str(), Order[I], name.length()) == 0 && name.length() == strlen(Order[I]))
1063  break;
1064  }
1065  if (Order[I] != 0)
1066  continue;
1067  }
1068 
1069  if (WriteTag(File, name, ((R->Action == Tag::RENAME) ? FindRaw(R->Name) : R->Data)) == false)
1070  return false;
1071  }
1072  return true;
1073 }
1074  /*}}}*/
1075 
1076 #include "tagfile-order.c"
1077 
static bool std::string const metaIndex const *const pkgAcqMetaClearSig *const pkgAcquire::Item *const I
Simple subset of std::string_view from C++17.
Definition: string_view.h:27
constexpr size_t length() const
Definition: string_view.h:138
constexpr const char * data() const
Definition: string_view.h:133
std::string to_string() const
Definition: string_view.h:106
constexpr bool empty() const
Definition: string_view.h:132
Definition: fileutl.h:39
bool IsOpen()
Definition: fileutl.h:150
bool Write(const void *From, unsigned long long Size)
Definition: fileutl.cc:2819
bool Seek(unsigned long long To)
Definition: fileutl.cc:2875
bool Read(void *To, unsigned long long Size, bool AllowEof)
Definition: fileutl.h:89
std::string & Name()
Definition: fileutl.h:156
pkgTagFile::Flags Flags
Definition: tagfile.cc:60
unsigned long long iOffset
Definition: tagfile.cc:65
unsigned long long Size
Definition: tagfile.cc:66
FileFd * Fd
Definition: tagfile.cc:59
std::list< FileChunk > chunks
Definition: tagfile.cc:74
bool isCommentedLine
Definition: tagfile.cc:67
pkgTagFilePrivate(FileFd *const pFd, unsigned long long const Size, pkgTagFile::Flags const pFlags)
Definition: tagfile.cc:55
void Reset(FileFd *const pFd, unsigned long long const pSize, pkgTagFile::Flags const pFlags)
Definition: tagfile.cc:39
void Init(FileFd *const F, pkgTagFile::Flags const Flags, unsigned long long Size=32 *1024)
Definition: tagfile.cc:127
bool Step(pkgTagSection &Section)
Definition: tagfile.cc:204
APT_HIDDEN bool Resize()
Definition: tagfile.cc:173
pkgTagFile(FileFd *const F, pkgTagFile::Flags const Flags, unsigned long long Size=32 *1024)
Definition: tagfile.cc:118
pkgTagFilePrivate *const d
Definition: tagfile.h:175
@ SUPPORT_COMMENTS
Definition: tagfile.h:190
APT_HIDDEN bool Fill()
Definition: tagfile.cc:383
virtual ~pkgTagFile()
Definition: tagfile.cc:157
unsigned long Offset()
Definition: tagfile.cc:163
bool Jump(pkgTagSection &Tag, unsigned long long Offset)
Definition: tagfile.cc:432
std::vector< TagData > Tags
Definition: tagfile.cc:97
APT_MUSTCHECK bool Scan(const char *Start, unsigned long MaxLength, bool const Restart=true)
searches the boundaries of the current section
Definition: tagfile.cc:484
unsigned int AlphaIndexes[128]
Definition: tagfile.h:47
APT_HIDDEN bool FindB(Key key, bool Default=false) const
Definition: tagfile.cc:835
APT_HIDDEN APT::StringView FindRaw(Key key) const
Definition: tagfile.cc:741
virtual ~pkgTagSection()
Definition: tagfile.cc:1078
APT_HIDDEN bool Exists(Key key) const
const char * Section
Definition: tagfile.h:46
bool Write(FileFd &File, char const *const *const Order=NULL, std::vector< Tag > const &Rewrite=std::vector< Tag >()) const
Definition: tagfile.cc:999
const char * Stop
Definition: tagfile.h:62
APT_HIDDEN APT::StringView FindRawInternal(unsigned int Pos) const
Definition: tagfile.cc:716
APT_HIDDEN bool FindBInternal(unsigned int Pos, bool Default=false) const
Definition: tagfile.cc:828
unsigned int Count() const
amount of Tags in the current section
Definition: tagfile.cc:942
pkgTagSectionPrivate *const d
Definition: tagfile.h:50
void Get(const char *&Start, const char *&Stop, unsigned int I) const
Definition: tagfile.cc:934
APT_HIDDEN bool FindFlagInternal(unsigned int Pos, uint8_t &Flags, uint8_t const Flag) const
Definition: tagfile.cc:849
virtual void TrimRecord(bool BeforeRecord, const char *&End)
Definition: tagfile.cc:608
APT_HIDDEN bool Find(Key key, const char *&Start, const char *&End) const
Definition: tagfile.cc:690
APT_HIDDEN signed int FindI(Key key, signed long Default=0) const
Definition: tagfile.cc:776
APT_HIDDEN bool FindFlag(Key key, uint8_t &Flags, uint8_t const Flag) const
Definition: tagfile.cc:858
void Trim()
Definition: tagfile.cc:618
APT_HIDDEN signed int FindIInternal(unsigned int Pos, signed long Default=0) const
Definition: tagfile.cc:750
APT_HIDDEN unsigned long long FindULL(Key key, unsigned long long const &Default=0) const
Definition: tagfile.cc:812
APT_HIDDEN unsigned long long FindULLInternal(unsigned int Pos, unsigned long long const &Default=0) const
Definition: tagfile.cc:792
unsigned long size() const
Definition: tagfile.h:122
unsigned int BetaIndexes[128]
Definition: tagfile.h:48
APT_HIDDEN bool FindInternal(unsigned int Pos, const char *&Start, const char *&End) const
Definition: tagfile.cc:668
#define APT_PURE
Definition: macros.h:56
#define APT_HIDDEN
Definition: macros.h:78
FileChunk(bool const pgood, size_t const plength) noexcept
Definition: tagfile.cc:72
TagData(unsigned int const StartTag)
Definition: tagfile.cc:95
static Tag Rewrite(std::string const &Name, std::string const &Data)
Definition: tagfile.cc:958
static Tag Remove(std::string const &Name)
Definition: tagfile.cc:950
static Tag Rename(std::string const &OldName, std::string const &NewName)
Definition: tagfile.cc:954
Tag(ActionType const Action, std::string const &Name, std::string const &Data)
Definition: tagfile.h:154
std::string Name
Definition: tagfile.h:147
int StringToBool(const string &Text, int Default)
Definition: strutl.cc:820
int isspace_ascii(int const c) APT_PURE APT_COLD
Definition: strutl.cc:1517
static bool WriteTag(FileFd &File, std::string Tag, StringView Value)
Definition: tagfile.cc:965
static bool RewriteTags(FileFd &File, pkgTagSection const *const This, char const *const Tag, std::vector< pkgTagSection::Tag >::const_iterator &R, std::vector< pkgTagSection::Tag >::const_iterator const &REnd)
Definition: tagfile.cc:975
static void RemoveCommentsFromBuffer(pkgTagFilePrivate *const d)
Definition: tagfile.cc:277
static unsigned long BetaHash(const char *Text, size_t Length)
Definition: tagfile.cc:101
static bool FillBuffer(pkgTagFilePrivate *const d)
Definition: tagfile.cc:265