1 // -*- mode: cpp; mode: fold -*-
3 // $Id: tagfile.cc,v 1.37.2.2 2003/12/31 16:02:30 mdz Exp $
4 /* ######################################################################
6 Fast scanner for RFC-822 type header information
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.
11 ##################################################################### */
13 // Include Files /*{{{*/
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>
35 using APT::StringView
;
37 class APT_HIDDEN pkgTagFilePrivate
/*{{{*/
40 void Reset(FileFd
* const pFd
, unsigned long long const pSize
, pkgTagFile::Flags
const pFlags
)
52 isCommentedLine
= false;
56 pkgTagFilePrivate(FileFd
* const pFd
, unsigned long long const Size
, pkgTagFile::Flags
const pFlags
) : Buffer(NULL
)
58 Reset(pFd
, Size
, pFlags
);
61 pkgTagFile::Flags Flags
;
66 unsigned long long iOffset
;
67 unsigned long long Size
;
73 FileChunk(bool const pgood
, size_t const plength
) : good(pgood
), length(plength
) {}
75 std::list
<FileChunk
> chunks
;
84 class APT_HIDDEN pkgTagSectionPrivate
/*{{{*/
87 pkgTagSectionPrivate()
91 unsigned int StartTag
;
93 unsigned int StartValue
;
94 unsigned int NextInBucket
;
96 explicit TagData(unsigned int const StartTag
) : StartTag(StartTag
), EndTag(0), StartValue(0), NextInBucket(0) {}
98 std::vector
<TagData
> Tags
;
102 static unsigned long BetaHash(const char *Text
, size_t Length
) /*{{{*/
104 /* This very simple hash function for the last 8 letters gives
105 very good performance on the debian package files */
108 Text
+= (Length
- 8);
111 unsigned long Res
= 0;
112 for (size_t i
= 0; i
< Length
; ++i
)
113 Res
= ((unsigned long)(Text
[i
]) & 0xDF) ^ (Res
<< 1);
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
))
122 Init(pFd
, pFlags
, Size
);
124 pkgTagFile::pkgTagFile(FileFd
* const pFd
,unsigned long long const Size
)
125 : pkgTagFile(pFd
, pkgTagFile::STRICT
, Size
)
128 void pkgTagFile::Init(FileFd
* const pFd
, pkgTagFile::Flags
const pFlags
, unsigned long long Size
)
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 */
135 d
->Reset(pFd
, Size
, pFlags
);
137 if (d
->Fd
->IsOpen() == false)
138 d
->Start
= d
->End
= d
->Buffer
= 0;
140 d
->Buffer
= (char*)malloc(sizeof(char) * Size
);
142 if (d
->Buffer
== NULL
)
147 d
->Start
= d
->End
= d
->Buffer
;
149 if (d
->Done
== false)
152 void pkgTagFile::Init(FileFd
* const pFd
,unsigned long long Size
)
154 Init(pFd
, pkgTagFile::STRICT
, Size
);
157 // TagFile::~pkgTagFile - Destructor /*{{{*/
158 pkgTagFile::~pkgTagFile()
163 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
164 APT_PURE
unsigned long pkgTagFile::Offset()
169 // TagFile::Resize - Resize the internal buffer /*{{{*/
170 // ---------------------------------------------------------------------
171 /* Resize the internal buffer (double it in size). Fail if a maximum size
174 bool pkgTagFile::Resize()
176 // fail is the buffer grows too big
177 if(d
->Size
> 1024*1024+1)
180 return Resize(d
->Size
* 2);
182 bool pkgTagFile::Resize(unsigned long long const newSize
)
184 unsigned long long const EndSize
= d
->End
- d
->Start
;
186 // get new buffer and use it
187 char* const newBuffer
= static_cast<char*>(realloc(d
->Buffer
, sizeof(char) * newSize
));
188 if (newBuffer
== NULL
)
190 d
->Buffer
= newBuffer
;
193 // update the start/end pointers to the new buffer
194 d
->Start
= d
->Buffer
;
195 d
->End
= d
->Start
+ EndSize
;
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.
205 bool pkgTagFile::Step(pkgTagSection
&Tag
)
207 if(Tag
.Scan(d
->Start
,d
->End
- d
->Start
) == false)
214 if(Tag
.Scan(d
->Start
,d
->End
- d
->Start
, false))
217 if (Resize() == false)
218 return _error
->Error(_("Unable to parse package file %s (%d)"),
219 d
->Fd
->Name().c_str(), 1);
221 } while (Tag
.Scan(d
->Start
,d
->End
- d
->Start
, false) == false);
224 size_t tagSize
= Tag
.size();
227 if ((d
->Flags
& pkgTagFile::SUPPORT_COMMENTS
) == 0)
228 d
->iOffset
+= tagSize
;
231 auto first
= d
->chunks
.begin();
232 for (; first
!= d
->chunks
.end(); ++first
)
234 if (first
->good
== false)
235 d
->iOffset
+= first
->length
;
238 if (tagSize
< first
->length
)
240 first
->length
-= tagSize
;
241 d
->iOffset
+= tagSize
;
246 tagSize
-= first
->length
;
247 d
->iOffset
+= first
->length
;
251 d
->chunks
.erase(d
->chunks
.begin(), first
);
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
)
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)
269 if (Actual
!= dataSize
)
274 static void RemoveCommentsFromBuffer(pkgTagFilePrivate
* const d
)
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)
281 if (d
->Start
== d
->Buffer
)
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
== '#')
287 d
->isCommentedLine
= true;
289 if (current
> d
->End
)
290 d
->chunks
.emplace_back(false, 1);
293 if (d
->isCommentedLine
== false)
294 good_start
= d
->Start
;
296 bad_start
= d
->Start
;
299 bad_start
= d
->Start
;
301 std::vector
<std::pair
<char*, size_t>> good_parts
;
302 while (current
<= d
->End
)
304 size_t const restLength
= (d
->End
- current
);
305 if (d
->isCommentedLine
== false)
307 current
= static_cast<char*>(memchr(current
, '#', restLength
));
308 if (current
== nullptr)
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
));
318 // ensure that this is really a comment and not a '#' in the middle of a line
319 if (*current
== '\n')
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;
329 else // the current line is a comment
331 current
= static_cast<char*>(memchr(current
, '\n', restLength
));
332 if (current
== nullptr)
334 d
->chunks
.emplace_back(false, (d
->End
- bad_start
));
338 // is the next line a comment, too?
339 if (current
>= d
->End
|| *current
!= '#')
341 d
->chunks
.emplace_back(false, (current
- bad_start
));
342 good_start
= current
;
344 d
->isCommentedLine
= false;
350 if (good_parts
.empty() == false)
352 // we found comments, so move later parts over them
354 for (auto const &good
: good_parts
)
356 memmove(current
, good
.first
, good
.second
);
357 current
+= good
.second
;
362 if (d
->isCommentedLine
== true)
364 // deal with a buffer containing only comments
365 // or an (unfinished) comment at the end
366 if (good_parts
.empty() == true)
373 // the buffer was all comment, but ended with the buffer
374 if (good_parts
.empty() == true && good_start
>= d
->End
)
380 bool pkgTagFile::Fill()
382 unsigned long long const EndSize
= d
->End
- d
->Start
;
385 memmove(d
->Buffer
,d
->Start
,EndSize
);
386 d
->Start
= d
->End
= d
->Buffer
+ EndSize
;
389 d
->Start
= d
->End
= d
->Buffer
;
391 unsigned long long Actual
= 0;
392 while (d
->Done
== false && d
->Size
> (Actual
+ 1))
394 if (FillBuffer(d
) == false)
396 if ((d
->Flags
& pkgTagFile::SUPPORT_COMMENTS
) != 0)
397 RemoveCommentsFromBuffer(d
);
398 Actual
= d
->End
- d
->Buffer
;
400 d
->Start
= d
->Buffer
;
404 if (EndSize
<= 3 && Actual
== 0)
406 if (d
->Size
- (d
->End
- d
->Buffer
) < 4)
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
--)
416 if (static_cast<unsigned long long>(d
->End
- d
->Buffer
) >= d
->Size
)
418 for (; LineCount
< 2; ++LineCount
)
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
429 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long long Offset
)
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
)
435 unsigned long long Dist
= Offset
- d
->iOffset
;
438 // if we have seen the end, don't ask for more
440 return Tag
.Scan(d
->Start
, d
->End
- d
->Start
);
445 // Reposition and reload..
448 if (d
->Fd
->Seek(Offset
) == false)
450 d
->End
= d
->Start
= d
->Buffer
;
451 d
->isCommentedLine
= false;
457 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
) == true)
460 // This appends a double new line (for the real eof handling)
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);
470 // pkgTagSection::pkgTagSection - Constructor /*{{{*/
471 // ---------------------------------------------------------------------
473 APT_IGNORE_DEPRECATED_PUSH
474 pkgTagSection::pkgTagSection()
475 : Section(0), d(new pkgTagSectionPrivate()), Stop(0)
477 memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
));
478 memset(&BetaIndexes
, 0, sizeof(BetaIndexes
));
480 APT_IGNORE_DEPRECATED_POP
482 // TagSection::Scan - Scan for the end of the header information /*{{{*/
483 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
, bool const Restart
)
486 const char *End
= Start
+ MaxLength
;
488 if (Restart
== false && d
->Tags
.empty() == false)
490 Stop
= Section
+ d
->Tags
.back().StartTag
;
493 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
501 if (d
->Tags
.empty() == false)
503 memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
));
504 memset(&BetaIndexes
, 0, sizeof(BetaIndexes
));
507 d
->Tags
.reserve(0x100);
509 unsigned int TagCount
= d
->Tags
.size();
514 pkgTagSectionPrivate::TagData
lastTagData(0);
515 lastTagData
.EndTag
= 0;
516 Key lastTagKey
= Key::Unknown
;
517 unsigned int lastTagHash
= 0;
520 TrimRecord(true,End
);
522 // this can happen when TrimRecord trims away the entire Record
523 // (e.g. because it just contains comments)
527 // Start a new index and add it to the hash
528 if (isspace_ascii(Stop
[0]) == 0)
530 // store the last found tag
531 if (lastTagData
.EndTag
!= 0)
533 if (lastTagKey
!= Key::Unknown
) {
534 AlphaIndexes
[static_cast<size_t>(lastTagKey
)] = TagCount
;
536 if (BetaIndexes
[lastTagHash
] != 0)
537 lastTagData
.NextInBucket
= BetaIndexes
[lastTagHash
];
538 APT_IGNORE_DEPRECATED_PUSH
539 BetaIndexes
[lastTagHash
] = TagCount
;
540 APT_IGNORE_DEPRECATED_POP
542 d
->Tags
.push_back(lastTagData
);
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
);
551 // find the end of the tag (which might or might not be the colon)
552 char const * EndTag
= Colon
;
554 for (; EndTag
> Stop
&& isspace_ascii(*EndTag
) != 0; --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
563 for (; Stop
< End
&& isspace_ascii(*Stop
) != 0; ++Stop
)
564 if (*Stop
== '\n' && Stop
[1] != ' ')
568 lastTagData
.StartValue
= Stop
- Section
;
571 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
576 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++)
580 // Double newline marks the end of the record
581 if (Stop
+1 < End
&& Stop
[1] == '\n')
583 if (lastTagData
.EndTag
!= 0)
585 if (lastTagKey
!= Key::Unknown
) {
586 AlphaIndexes
[static_cast<size_t>(lastTagKey
)] = TagCount
;
588 if (BetaIndexes
[lastTagHash
] != 0)
589 lastTagData
.NextInBucket
= BetaIndexes
[lastTagHash
];
590 APT_IGNORE_DEPRECATED(BetaIndexes
[lastTagHash
] = TagCount
;)
592 d
->Tags
.push_back(lastTagData
);
595 pkgTagSectionPrivate::TagData
const td(Stop
- Section
);
596 d
->Tags
.push_back(td
);
597 TrimRecord(false,End
);
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
)
612 if (BeforeRecord
== true)
614 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
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()
622 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
625 // TagSection::Exists - return True if a tag exists /*{{{*/
626 bool pkgTagSection::Exists(StringView Tag
) const
629 return Find(Tag
, tmp
);
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
637 auto Bucket
= AlphaIndexes
[static_cast<size_t>(key
)];
641 bool pkgTagSection::Find(StringView TagView
,unsigned int &Pos
) const
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
);
649 unsigned int Bucket
= BetaIndexes
[BetaHash(Tag
, Length
)];
653 for (; Bucket
!= 0; Bucket
= d
->Tags
[Bucket
- 1].NextInBucket
)
655 if ((d
->Tags
[Bucket
- 1].EndTag
- d
->Tags
[Bucket
- 1].StartTag
) != Length
)
658 char const * const St
= Section
+ d
->Tags
[Bucket
- 1].StartTag
;
659 if (strncasecmp(Tag
,St
,Length
) != 0)
670 bool pkgTagSection::FindInternal(unsigned int Pos
, const char *&Start
,
671 const char *&End
) const
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");
679 for (; isspace_ascii(End
[-1]) != 0 && End
> Start
; --End
);
683 bool pkgTagSection::Find(StringView Tag
,const char *&Start
,
684 const char *&End
) const
687 return Find(Tag
, Pos
) && FindInternal(Pos
, Start
, End
);
689 bool pkgTagSection::Find(Key key
,const char *&Start
,
690 const char *&End
) const
693 return Find(key
, Pos
) && FindInternal(Pos
, Start
, End
);
696 // TagSection::FindS - Find a string /*{{{*/
697 StringView
pkgTagSection::Find(StringView Tag
) const
701 if (Find(Tag
,Start
,End
) == false)
703 return StringView(Start
, End
- Start
);
705 StringView
pkgTagSection::Find(Key key
) const
709 if (Find(key
,Start
,End
) == false)
711 return StringView(Start
, End
- Start
);
714 // TagSection::FindRawS - Find a string /*{{{*/
715 StringView
pkgTagSection::FindRawInternal(unsigned int Pos
) const
717 char const *Start
= (char const *) memchr(Section
+ d
->Tags
[Pos
].EndTag
, ':', d
->Tags
[Pos
].StartValue
- d
->Tags
[Pos
].EndTag
);
719 char const *End
= Section
+ d
->Tags
[Pos
+ 1].StartTag
;
720 if (unlikely(Start
> End
))
723 for (; isspace_ascii(End
[-1]) != 0 && End
> Start
; --End
);
725 return StringView(Start
, End
- Start
);
727 StringView
pkgTagSection::FindRaw(StringView Tag
) const
730 return Find(Tag
, Pos
) ? FindRawInternal(Pos
) : "";
732 StringView
pkgTagSection::FindRaw(Key key
) const
735 return Find(key
, Pos
) ? FindRawInternal(Pos
) : "";
738 // TagSection::FindI - Find an integer /*{{{*/
739 // ---------------------------------------------------------------------
741 signed int pkgTagSection::FindIInternal(unsigned int Pos
,signed long Default
) const
745 if (FindInternal(Pos
,Start
,Stop
) == false)
748 // Copy it into a temp buffer so we can use strtol
750 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
752 strncpy(S
,Start
,Stop
-Start
);
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()) {
761 _error
->Error(_("Cannot convert %s to integer: out of range"), S
);
767 signed int pkgTagSection::FindI(Key key
,signed long Default
) const
771 return Find(key
, Pos
) ? FindIInternal(Pos
) : Default
;
773 signed int pkgTagSection::FindI(StringView Tag
,signed long Default
) const
777 return Find(Tag
, Pos
) ? FindIInternal(Pos
, Default
) : Default
;
780 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
781 // ---------------------------------------------------------------------
783 unsigned long long pkgTagSection::FindULLInternal(unsigned int Pos
, unsigned long long const &Default
) const
787 if (FindInternal(Pos
,Start
,Stop
) == false)
790 // Copy it into a temp buffer so we can use strtoull
792 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
794 strncpy(S
,Start
,Stop
-Start
);
798 unsigned long long Result
= strtoull(S
,&End
,10);
803 unsigned long long pkgTagSection::FindULL(Key key
, unsigned long long const &Default
) const
807 return Find(key
, Pos
) ? FindULLInternal(Pos
, Default
) : Default
;
809 unsigned long long pkgTagSection::FindULL(StringView Tag
, unsigned long long const &Default
) const
813 return Find(Tag
, Pos
) ? FindULLInternal(Pos
, Default
) : Default
;
816 // TagSection::FindB - Find boolean value /*{{{*/
817 // ---------------------------------------------------------------------
819 bool pkgTagSection::FindBInternal(unsigned int Pos
, bool Default
) const
821 const char *Start
, *Stop
;
822 if (FindInternal(Pos
, Start
, Stop
) == false)
824 return StringToBool(string(Start
, Stop
));
826 bool pkgTagSection::FindB(Key key
, bool Default
) const
829 return Find(key
, Pos
) ? FindBInternal(Pos
, Default
): Default
;
831 bool pkgTagSection::FindB(StringView Tag
, bool Default
) const
834 return Find(Tag
, Pos
) ? FindBInternal(Pos
, Default
) : Default
;
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
845 if (FindInternal(Pos
,Start
,Stop
) == false)
847 return FindFlag(Flags
, Flag
, Start
, Stop
);
849 bool pkgTagSection::FindFlag(Key key
, uint8_t &Flags
,
850 uint8_t const Flag
) const
853 if (Find(key
,Pos
) == false)
855 return FindFlagInternal(Pos
, Flags
, Flag
);
857 bool pkgTagSection::FindFlag(StringView Tag
, uint8_t &Flags
,
858 uint8_t const Flag
) const
861 if (Find(Tag
,Pos
) == false)
863 return FindFlagInternal(Pos
, Flags
, Flag
);
865 bool pkgTagSection::FindFlag(uint8_t &Flags
, uint8_t const Flag
,
866 char const* const Start
, char const* const Stop
)
868 switch (StringToBool(string(Start
, Stop
)))
879 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
884 bool pkgTagSection::FindFlagInternal(unsigned int Pos
,unsigned long &Flags
,
885 unsigned long Flag
) const
889 if (FindInternal(Pos
,Start
,Stop
) == false)
891 return FindFlag(Flags
, Flag
, Start
, Stop
);
893 bool pkgTagSection::FindFlag(Key key
,unsigned long &Flags
,
894 unsigned long Flag
) const
897 return Find(key
, Pos
) ? FindFlagInternal(Pos
, Flags
, Flag
) : true;
899 bool pkgTagSection::FindFlag(StringView Tag
,unsigned long &Flags
,
900 unsigned long Flag
) const
903 return Find(Tag
, Pos
) ? FindFlagInternal(Pos
, Flags
, Flag
) : true;
905 bool pkgTagSection::FindFlag(unsigned long &Flags
, unsigned long Flag
,
906 char const* Start
, char const* Stop
)
908 switch (StringToBool(string(Start
, Stop
)))
919 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
925 void pkgTagSection::Get(const char *&Start
,const char *&Stop
,unsigned int I
) const/*{{{*/
927 Start
= Section
+ d
->Tags
[I
].StartTag
;
928 Stop
= Section
+ d
->Tags
[I
+1].StartTag
;
931 APT_PURE
unsigned int pkgTagSection::Count() const { /*{{{*/
932 if (d
->Tags
.empty() == true)
934 // the last element is just marking the end and isn't a real one
935 return d
->Tags
.size() - 1;
938 // TagSection::Write - Ordered (re)writing of fields /*{{{*/
939 pkgTagSection::Tag
pkgTagSection::Tag::Remove(std::string
const &Name
)
941 return Tag(REMOVE
, Name
, "");
943 pkgTagSection::Tag
pkgTagSection::Tag::Rename(std::string
const &OldName
, std::string
const &NewName
)
945 return Tag(RENAME
, OldName
, NewName
);
947 pkgTagSection::Tag
pkgTagSection::Tag::Rewrite(std::string
const &Name
, std::string
const &Data
)
949 if (Data
.empty() == true)
950 return Tag(REMOVE
, Name
, "");
952 return Tag(REWRITE
, Name
, Data
);
954 static bool WriteTag(FileFd
&File
, std::string Tag
, StringView Value
)
956 if (Value
.empty() || isspace_ascii(Value
[0]) != 0)
960 Tag
.append(Value
.data(), Value
.length());
962 return File
.Write(Tag
.c_str(), Tag
.length());
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
)
968 size_t const TagLen
= strlen(Tag
);
969 for (; R
!= REnd
; ++R
)
972 if (R
->Name
.length() == TagLen
&& strncasecmp(R
->Name
.c_str(), Tag
, R
->Name
.length()) == 0)
974 if (R
->Action
!= pkgTagSection::Tag::REWRITE
)
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();
984 return WriteTag(File
, Tag
, data
);
988 bool pkgTagSection::Write(FileFd
&File
, char const * const * const Order
, std::vector
<Tag
> const &Rewrite
) const
990 // first pass: Write everything we have an order for
993 for (unsigned int I
= 0; Order
[I
] != 0; ++I
)
995 std::vector
<Tag
>::const_iterator R
= Rewrite
.begin();
996 if (RewriteTags(File
, this, Order
[I
], R
, Rewrite
.end()) == false)
998 if (R
!= Rewrite
.end())
1001 if (Exists(Order
[I
]) == false)
1004 if (WriteTag(File
, Order
[I
], FindRaw(Order
[I
])) == false)
1008 // second pass: See if we have tags which aren't ordered
1009 if (d
->Tags
.empty() == false)
1011 for (std::vector
<pkgTagSectionPrivate::TagData
>::const_iterator T
= d
->Tags
.begin(); T
!= d
->Tags
.end() - 1; ++T
)
1013 char const * const fieldname
= Section
+ T
->StartTag
;
1014 size_t fieldnamelen
= T
->EndTag
- T
->StartTag
;
1018 for (; Order
[I
] != 0; ++I
)
1020 if (fieldnamelen
== strlen(Order
[I
]) && strncasecmp(fieldname
, Order
[I
], fieldnamelen
) == 0)
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)
1031 if (R
!= Rewrite
.end())
1034 if (WriteTag(File
, name
, FindRaw(name
)) == false)
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
)
1041 if (R
->Action
== Tag::REMOVE
)
1043 std::string
const name
= ((R
->Action
== Tag::RENAME
) ? R
->Data
: R
->Name
);
1044 if (Exists(name
.c_str()))
1049 for (; Order
[I
] != 0; ++I
)
1051 if (strncasecmp(name
.c_str(), Order
[I
], name
.length()) == 0 && name
.length() == strlen(Order
[I
]))
1058 if (WriteTag(File
, name
, ((R
->Action
== Tag::RENAME
) ? FindRaw(R
->Name
) : R
->Data
)) == false)
1065 void pkgUserTagSection::TrimRecord(bool /*BeforeRecord*/, const char* &End
)/*{{{*/
1067 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r' || Stop
[0] == '#'); Stop
++)
1069 Stop
= (const char*) memchr(Stop
,'\n',End
-Stop
);
1073 #include "tagfile-order.c"
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
)
1084 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
1085 for (unsigned I
= 0; I
!= 256; I
++)
1088 // Set new tag up as necessary.
1089 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
1091 if (Rewrite
[J
].NewTag
== 0)
1092 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
1095 // Write all all of the tags, in order.
1098 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
1100 bool Rewritten
= false;
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
++)
1105 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
1108 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
1110 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
1111 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1113 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1120 // See if it is in the fragment
1122 if (Tags
.Find(StringView(Order
[I
]),Pos
) == false)
1126 if (Rewritten
== true)
1129 /* Write out this element, taking a moment to rewrite the tag
1130 in case of changes of case. */
1133 Tags
.Get(Start
,Stop
,Pos
);
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");
1145 // Now write all the old tags that were missed.
1146 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
1148 if ((Visited
[I
] & 1) == 1)
1153 Tags
.Get(Start
,Stop
,I
);
1154 const char *End
= Start
;
1155 for (; End
< Stop
&& *End
!= ':'; End
++);
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
++)
1161 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
1164 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
1166 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
1167 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1169 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1177 if (Rewritten
== true)
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");
1187 // Now write all the rewrites that were missed
1188 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
1190 if ((Visited
[J
] & 2) == 2)
1193 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
1195 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
1196 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1198 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1204 APT_IGNORE_DEPRECATED_POP
1207 pkgTagSection::~pkgTagSection() { delete d
; }