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/error.h>
18 #include <apt-pkg/strutl.h>
19 #include <apt-pkg/fileutl.h>
20 #include <apt-pkg/string_view.h>
34 using APT::StringView
;
36 class APT_HIDDEN pkgTagFilePrivate
/*{{{*/
39 void Reset(FileFd
* const pFd
, unsigned long long const pSize
, pkgTagFile::Flags
const pFlags
)
51 isCommentedLine
= false;
55 pkgTagFilePrivate(FileFd
* const pFd
, unsigned long long const Size
, pkgTagFile::Flags
const pFlags
) : Buffer(NULL
)
57 Reset(pFd
, Size
, pFlags
);
60 pkgTagFile::Flags Flags
;
65 unsigned long long iOffset
;
66 unsigned long long Size
;
72 FileChunk(bool const pgood
, size_t const plength
) : good(pgood
), length(plength
) {}
74 std::list
<FileChunk
> chunks
;
83 class APT_HIDDEN pkgTagSectionPrivate
/*{{{*/
86 pkgTagSectionPrivate()
90 unsigned int StartTag
;
92 unsigned int StartValue
;
93 unsigned int NextInBucket
;
95 explicit TagData(unsigned int const StartTag
) : StartTag(StartTag
), EndTag(0), StartValue(0), NextInBucket(0) {}
97 std::vector
<TagData
> Tags
;
101 static unsigned long BetaHash(const char *Text
, size_t Length
) /*{{{*/
103 /* This very simple hash function for the last 8 letters gives
104 very good performance on the debian package files */
107 Text
+= (Length
- 8);
110 unsigned long Res
= 0;
111 for (size_t i
= 0; i
< Length
; ++i
)
112 Res
= ((unsigned long)(Text
[i
]) & 0xDF) ^ (Res
<< 1);
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
))
121 Init(pFd
, pFlags
, Size
);
123 pkgTagFile::pkgTagFile(FileFd
* const pFd
,unsigned long long const Size
)
124 : pkgTagFile(pFd
, pkgTagFile::STRICT
, Size
)
127 void pkgTagFile::Init(FileFd
* const pFd
, pkgTagFile::Flags
const pFlags
, unsigned long long Size
)
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 */
134 d
->Reset(pFd
, Size
, pFlags
);
136 if (d
->Fd
->IsOpen() == false)
137 d
->Start
= d
->End
= d
->Buffer
= 0;
139 d
->Buffer
= (char*)malloc(sizeof(char) * Size
);
141 if (d
->Buffer
== NULL
)
146 d
->Start
= d
->End
= d
->Buffer
;
148 if (d
->Done
== false)
151 void pkgTagFile::Init(FileFd
* const pFd
,unsigned long long Size
)
153 Init(pFd
, pkgTagFile::STRICT
, Size
);
156 // TagFile::~pkgTagFile - Destructor /*{{{*/
157 pkgTagFile::~pkgTagFile()
162 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
163 APT_PURE
unsigned long pkgTagFile::Offset()
168 // TagFile::Resize - Resize the internal buffer /*{{{*/
169 // ---------------------------------------------------------------------
170 /* Resize the internal buffer (double it in size). Fail if a maximum size
173 bool pkgTagFile::Resize()
175 // fail is the buffer grows too big
176 if(d
->Size
> 1024*1024+1)
179 return Resize(d
->Size
* 2);
181 bool pkgTagFile::Resize(unsigned long long const newSize
)
183 unsigned long long const EndSize
= d
->End
- d
->Start
;
185 // get new buffer and use it
186 char* const newBuffer
= static_cast<char*>(realloc(d
->Buffer
, sizeof(char) * newSize
));
187 if (newBuffer
== NULL
)
189 d
->Buffer
= newBuffer
;
192 // update the start/end pointers to the new buffer
193 d
->Start
= d
->Buffer
;
194 d
->End
= d
->Start
+ EndSize
;
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.
204 bool pkgTagFile::Step(pkgTagSection
&Tag
)
206 if(Tag
.Scan(d
->Start
,d
->End
- d
->Start
) == false)
213 if(Tag
.Scan(d
->Start
,d
->End
- d
->Start
, false))
216 if (Resize() == false)
217 return _error
->Error(_("Unable to parse package file %s (%d)"),
218 d
->Fd
->Name().c_str(), 1);
220 } while (Tag
.Scan(d
->Start
,d
->End
- d
->Start
, false) == false);
223 size_t tagSize
= Tag
.size();
226 if ((d
->Flags
& pkgTagFile::SUPPORT_COMMENTS
) == 0)
227 d
->iOffset
+= tagSize
;
230 auto first
= d
->chunks
.begin();
231 for (; first
!= d
->chunks
.end(); ++first
)
233 if (first
->good
== false)
234 d
->iOffset
+= first
->length
;
237 if (tagSize
< first
->length
)
239 first
->length
-= tagSize
;
240 d
->iOffset
+= tagSize
;
245 tagSize
-= first
->length
;
246 d
->iOffset
+= first
->length
;
250 d
->chunks
.erase(d
->chunks
.begin(), first
);
257 // TagFile::Fill - Top up the buffer /*{{{*/
258 // ---------------------------------------------------------------------
259 /* This takes the bit at the end of the buffer and puts it at the start
260 then fills the rest from the file */
261 static bool FillBuffer(pkgTagFilePrivate
* const d
)
263 unsigned long long Actual
= 0;
264 // See if only a bit of the file is left
265 unsigned long long const dataSize
= d
->Size
- ((d
->End
- d
->Buffer
) + 1);
266 if (d
->Fd
->Read(d
->End
, dataSize
, &Actual
) == false)
268 if (Actual
!= dataSize
)
273 static void RemoveCommentsFromBuffer(pkgTagFilePrivate
* const d
)
275 // look for valid comments in the buffer
276 char * good_start
= nullptr, * bad_start
= nullptr;
277 char * current
= d
->Start
;
278 if (d
->isCommentedLine
== false)
280 if (d
->Start
== d
->Buffer
)
282 // the start of the buffer is a newline as a record can't start
283 // in the middle of a line by definition.
284 if (*d
->Start
== '#')
286 d
->isCommentedLine
= true;
288 if (current
> d
->End
)
289 d
->chunks
.emplace_back(false, 1);
292 if (d
->isCommentedLine
== false)
293 good_start
= d
->Start
;
295 bad_start
= d
->Start
;
298 bad_start
= d
->Start
;
300 std::vector
<std::pair
<char*, size_t>> good_parts
;
301 while (current
<= d
->End
)
303 size_t const restLength
= (d
->End
- current
);
304 if (d
->isCommentedLine
== false)
306 current
= static_cast<char*>(memchr(current
, '#', restLength
));
307 if (current
== nullptr)
309 size_t const goodLength
= d
->End
- good_start
;
310 d
->chunks
.emplace_back(true, goodLength
);
311 if (good_start
!= d
->Start
)
312 good_parts
.push_back(std::make_pair(good_start
, goodLength
));
317 // ensure that this is really a comment and not a '#' in the middle of a line
318 if (*current
== '\n')
320 size_t const goodLength
= (current
- good_start
) + 1;
321 d
->chunks
.emplace_back(true, goodLength
);
322 good_parts
.push_back(std::make_pair(good_start
, goodLength
));
323 good_start
= nullptr;
324 d
->isCommentedLine
= true;
328 else // the current line is a comment
330 current
= static_cast<char*>(memchr(current
, '\n', restLength
));
331 if (current
== nullptr)
333 d
->chunks
.emplace_back(false, (d
->End
- bad_start
));
337 // is the next line a comment, too?
338 if (current
>= d
->End
|| *current
!= '#')
340 d
->chunks
.emplace_back(false, (current
- bad_start
));
341 good_start
= current
;
343 d
->isCommentedLine
= false;
349 if (good_parts
.empty() == false)
351 // we found comments, so move later parts over them
353 for (auto const &good
: good_parts
)
355 memmove(current
, good
.first
, good
.second
);
356 current
+= good
.second
;
361 if (d
->isCommentedLine
== true)
363 // deal with a buffer containing only comments
364 // or an (unfinished) comment at the end
365 if (good_parts
.empty() == true)
372 // the buffer was all comment, but ended with the buffer
373 if (good_parts
.empty() == true && good_start
>= d
->End
)
379 bool pkgTagFile::Fill()
381 unsigned long long const EndSize
= d
->End
- d
->Start
;
384 memmove(d
->Buffer
,d
->Start
,EndSize
);
385 d
->Start
= d
->End
= d
->Buffer
+ EndSize
;
388 d
->Start
= d
->End
= d
->Buffer
;
390 unsigned long long Actual
= 0;
391 while (d
->Done
== false && d
->Size
> (Actual
+ 1))
393 if (FillBuffer(d
) == false)
395 if ((d
->Flags
& pkgTagFile::SUPPORT_COMMENTS
) != 0)
396 RemoveCommentsFromBuffer(d
);
397 Actual
= d
->End
- d
->Buffer
;
399 d
->Start
= d
->Buffer
;
403 if (EndSize
<= 3 && Actual
== 0)
405 if (d
->Size
- (d
->End
- d
->Buffer
) < 4)
408 // Append a double new line if one does not exist
409 unsigned int LineCount
= 0;
410 for (const char *E
= d
->End
- 1; E
- d
->End
< 6 && (*E
== '\n' || *E
== '\r'); E
--)
415 if (static_cast<unsigned long long>(d
->End
- d
->Buffer
) >= d
->Size
)
417 for (; LineCount
< 2; ++LineCount
)
424 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
425 // ---------------------------------------------------------------------
426 /* This jumps to a pre-recorded file location and reads the record
428 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long long Offset
)
430 if ((d
->Flags
& pkgTagFile::SUPPORT_COMMENTS
) == 0 &&
431 // We are within a buffer space of the next hit..
432 Offset
>= d
->iOffset
&& d
->iOffset
+ (d
->End
- d
->Start
) > Offset
)
434 unsigned long long Dist
= Offset
- d
->iOffset
;
437 // if we have seen the end, don't ask for more
439 return Tag
.Scan(d
->Start
, d
->End
- d
->Start
);
444 // Reposition and reload..
447 if (d
->Fd
->Seek(Offset
) == false)
449 d
->End
= d
->Start
= d
->Buffer
;
450 d
->isCommentedLine
= false;
456 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
) == true)
459 // This appends a double new line (for the real eof handling)
463 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
, false) == false)
464 return _error
->Error(_("Unable to parse package file %s (%d)"),d
->Fd
->Name().c_str(), 2);
469 // pkgTagSection::pkgTagSection - Constructor /*{{{*/
470 // ---------------------------------------------------------------------
472 APT_IGNORE_DEPRECATED_PUSH
473 pkgTagSection::pkgTagSection()
474 : Section(0), d(new pkgTagSectionPrivate()), Stop(0)
476 memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
));
477 memset(&BetaIndexes
, 0, sizeof(BetaIndexes
));
479 APT_IGNORE_DEPRECATED_POP
481 // TagSection::Scan - Scan for the end of the header information /*{{{*/
482 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
, bool const Restart
)
485 const char *End
= Start
+ MaxLength
;
487 if (Restart
== false && d
->Tags
.empty() == false)
489 Stop
= Section
+ d
->Tags
.back().StartTag
;
492 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
500 if (d
->Tags
.empty() == false)
502 memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
));
503 memset(&BetaIndexes
, 0, sizeof(BetaIndexes
));
506 d
->Tags
.reserve(0x100);
508 unsigned int TagCount
= d
->Tags
.size();
513 pkgTagSectionPrivate::TagData
lastTagData(0);
514 lastTagData
.EndTag
= 0;
515 unsigned long lastTagHash
= 0;
518 TrimRecord(true,End
);
520 // this can happen when TrimRecord trims away the entire Record
521 // (e.g. because it just contains comments)
525 // Start a new index and add it to the hash
526 if (isspace_ascii(Stop
[0]) == 0)
528 // store the last found tag
529 if (lastTagData
.EndTag
!= 0)
531 if (BetaIndexes
[lastTagHash
] != 0)
532 lastTagData
.NextInBucket
= BetaIndexes
[lastTagHash
];
533 APT_IGNORE_DEPRECATED_PUSH
534 BetaIndexes
[lastTagHash
] = TagCount
;
535 APT_IGNORE_DEPRECATED_POP
536 d
->Tags
.push_back(lastTagData
);
539 APT_IGNORE_DEPRECATED(++TagCount
;)
540 lastTagData
= pkgTagSectionPrivate::TagData(Stop
- Section
);
541 // find the colon separating tag and value
542 char const * Colon
= (char const *) memchr(Stop
, ':', End
- Stop
);
545 // find the end of the tag (which might or might not be the colon)
546 char const * EndTag
= Colon
;
548 for (; EndTag
> Stop
&& isspace_ascii(*EndTag
) != 0; --EndTag
)
551 lastTagData
.EndTag
= EndTag
- Section
;
552 lastTagHash
= BetaHash(Stop
, EndTag
- Stop
);
553 // find the beginning of the value
555 for (; Stop
< End
&& isspace_ascii(*Stop
) != 0; ++Stop
)
556 if (*Stop
== '\n' && Stop
[1] != ' ')
560 lastTagData
.StartValue
= Stop
- Section
;
563 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
568 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++)
572 // Double newline marks the end of the record
573 if (Stop
+1 < End
&& Stop
[1] == '\n')
575 if (lastTagData
.EndTag
!= 0)
577 if (BetaIndexes
[lastTagHash
] != 0)
578 lastTagData
.NextInBucket
= BetaIndexes
[lastTagHash
];
579 APT_IGNORE_DEPRECATED(BetaIndexes
[lastTagHash
] = TagCount
;)
580 d
->Tags
.push_back(lastTagData
);
583 pkgTagSectionPrivate::TagData
const td(Stop
- Section
);
584 d
->Tags
.push_back(td
);
585 TrimRecord(false,End
);
595 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
596 // ---------------------------------------------------------------------
597 /* There should be exactly 2 newline at the end of the record, no more. */
598 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
)
600 if (BeforeRecord
== true)
602 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
605 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
606 // ---------------------------------------------------------------------
607 /* There should be exactly 1 newline at the end of the buffer, no more. */
608 void pkgTagSection::Trim()
610 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
613 // TagSection::Exists - return True if a tag exists /*{{{*/
614 bool pkgTagSection::Exists(StringView Tag
) const
617 return Find(Tag
, tmp
);
620 // TagSection::Find - Locate a tag /*{{{*/
621 // ---------------------------------------------------------------------
622 /* This searches the section for a tag that matches the given string. */
623 bool pkgTagSection::Find(StringView TagView
,unsigned int &Pos
) const
625 const char * const Tag
= TagView
.data();
626 size_t const Length
= TagView
.length();
627 unsigned int Bucket
= BetaIndexes
[BetaHash(Tag
, Length
)];
631 for (; Bucket
!= 0; Bucket
= d
->Tags
[Bucket
- 1].NextInBucket
)
633 if ((d
->Tags
[Bucket
- 1].EndTag
- d
->Tags
[Bucket
- 1].StartTag
) != Length
)
636 char const * const St
= Section
+ d
->Tags
[Bucket
- 1].StartTag
;
637 if (strncasecmp(Tag
,St
,Length
) != 0)
648 bool pkgTagSection::FindInternal(unsigned int Pos
, const char *&Start
,
649 const char *&End
) const
651 Start
= Section
+ d
->Tags
[Pos
].StartValue
;
652 // Strip off the gunk from the end
653 End
= Section
+ d
->Tags
[Pos
+ 1].StartTag
;
654 if (unlikely(Start
> End
))
655 return _error
->Error("Internal parsing error");
657 for (; isspace_ascii(End
[-1]) != 0 && End
> Start
; --End
);
661 bool pkgTagSection::Find(StringView Tag
,const char *&Start
,
662 const char *&End
) const
665 return Find(Tag
, Pos
) && FindInternal(Pos
, Start
, End
);
668 // TagSection::FindS - Find a string /*{{{*/
669 StringView
pkgTagSection::Find(StringView Tag
) const
673 if (Find(Tag
,Start
,End
) == false)
675 return StringView(Start
, End
- Start
);
678 // TagSection::FindRawS - Find a string /*{{{*/
679 StringView
pkgTagSection::FindRawInternal(unsigned int Pos
) const
681 char const *Start
= (char const *) memchr(Section
+ d
->Tags
[Pos
].EndTag
, ':', d
->Tags
[Pos
].StartValue
- d
->Tags
[Pos
].EndTag
);
683 char const *End
= Section
+ d
->Tags
[Pos
+ 1].StartTag
;
684 if (unlikely(Start
> End
))
687 for (; isspace_ascii(End
[-1]) != 0 && End
> Start
; --End
);
689 return StringView(Start
, End
- Start
);
691 StringView
pkgTagSection::FindRaw(StringView Tag
) const
694 return Find(Tag
, Pos
) ? FindRawInternal(Pos
) : "";
697 // TagSection::FindI - Find an integer /*{{{*/
698 // ---------------------------------------------------------------------
700 signed int pkgTagSection::FindIInternal(unsigned int Pos
,signed long Default
) const
704 if (FindInternal(Pos
,Start
,Stop
) == false)
707 // Copy it into a temp buffer so we can use strtol
709 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
711 strncpy(S
,Start
,Stop
-Start
);
716 signed long Result
= strtol(S
,&End
,10);
717 if (errno
== ERANGE
||
718 Result
< std::numeric_limits
<int>::min() || Result
> std::numeric_limits
<int>::max()) {
720 _error
->Error(_("Cannot convert %s to integer: out of range"), S
);
726 signed int pkgTagSection::FindI(StringView Tag
,signed long Default
) const
730 return Find(Tag
, Pos
) ? FindIInternal(Pos
, Default
) : Default
;
733 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
734 // ---------------------------------------------------------------------
736 unsigned long long pkgTagSection::FindULLInternal(unsigned int Pos
, unsigned long long const &Default
) const
740 if (FindInternal(Pos
,Start
,Stop
) == false)
743 // Copy it into a temp buffer so we can use strtoull
745 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
747 strncpy(S
,Start
,Stop
-Start
);
751 unsigned long long Result
= strtoull(S
,&End
,10);
756 unsigned long long pkgTagSection::FindULL(StringView Tag
, unsigned long long const &Default
) const
760 return Find(Tag
, Pos
) ? FindULLInternal(Pos
, Default
) : Default
;
763 // TagSection::FindB - Find boolean value /*{{{*/
764 // ---------------------------------------------------------------------
766 bool pkgTagSection::FindBInternal(unsigned int Pos
, bool Default
) const
768 const char *Start
, *Stop
;
769 if (FindInternal(Pos
, Start
, Stop
) == false)
771 return StringToBool(string(Start
, Stop
));
773 bool pkgTagSection::FindB(StringView Tag
, bool Default
) const
776 return Find(Tag
, Pos
) ? FindBInternal(Pos
, Default
) : Default
;
779 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
780 // ---------------------------------------------------------------------
781 /* The bits marked in Flag are masked on/off in Flags */
782 bool pkgTagSection::FindFlagInternal(unsigned int Pos
, uint8_t &Flags
,
783 uint8_t const Flag
) const
787 if (FindInternal(Pos
,Start
,Stop
) == false)
789 return FindFlag(Flags
, Flag
, Start
, Stop
);
791 bool pkgTagSection::FindFlag(StringView Tag
, uint8_t &Flags
,
792 uint8_t const Flag
) const
795 if (Find(Tag
,Pos
) == false)
797 return FindFlagInternal(Pos
, Flags
, Flag
);
799 bool pkgTagSection::FindFlag(uint8_t &Flags
, uint8_t const Flag
,
800 char const* const Start
, char const* const Stop
)
802 switch (StringToBool(string(Start
, Stop
)))
813 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
818 bool pkgTagSection::FindFlagInternal(unsigned int Pos
,unsigned long &Flags
,
819 unsigned long Flag
) const
823 if (FindInternal(Pos
,Start
,Stop
) == false)
825 return FindFlag(Flags
, Flag
, Start
, Stop
);
827 bool pkgTagSection::FindFlag(StringView Tag
,unsigned long &Flags
,
828 unsigned long Flag
) const
831 return Find(Tag
, Pos
) ? FindFlagInternal(Pos
, Flags
, Flag
) : true;
833 bool pkgTagSection::FindFlag(unsigned long &Flags
, unsigned long Flag
,
834 char const* Start
, char const* Stop
)
836 switch (StringToBool(string(Start
, Stop
)))
847 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
853 void pkgTagSection::Get(const char *&Start
,const char *&Stop
,unsigned int I
) const/*{{{*/
855 Start
= Section
+ d
->Tags
[I
].StartTag
;
856 Stop
= Section
+ d
->Tags
[I
+1].StartTag
;
859 APT_PURE
unsigned int pkgTagSection::Count() const { /*{{{*/
860 if (d
->Tags
.empty() == true)
862 // the last element is just marking the end and isn't a real one
863 return d
->Tags
.size() - 1;
866 // TagSection::Write - Ordered (re)writing of fields /*{{{*/
867 pkgTagSection::Tag
pkgTagSection::Tag::Remove(std::string
const &Name
)
869 return Tag(REMOVE
, Name
, "");
871 pkgTagSection::Tag
pkgTagSection::Tag::Rename(std::string
const &OldName
, std::string
const &NewName
)
873 return Tag(RENAME
, OldName
, NewName
);
875 pkgTagSection::Tag
pkgTagSection::Tag::Rewrite(std::string
const &Name
, std::string
const &Data
)
877 if (Data
.empty() == true)
878 return Tag(REMOVE
, Name
, "");
880 return Tag(REWRITE
, Name
, Data
);
882 static bool WriteTag(FileFd
&File
, std::string Tag
, StringView Value
)
884 if (Value
.empty() || isspace_ascii(Value
[0]) != 0)
888 Tag
.append(Value
.data(), Value
.length());
890 return File
.Write(Tag
.c_str(), Tag
.length());
892 static bool RewriteTags(FileFd
&File
, pkgTagSection
const * const This
, char const * const Tag
,
893 std::vector
<pkgTagSection::Tag
>::const_iterator
&R
,
894 std::vector
<pkgTagSection::Tag
>::const_iterator
const &REnd
)
896 size_t const TagLen
= strlen(Tag
);
897 for (; R
!= REnd
; ++R
)
900 if (R
->Name
.length() == TagLen
&& strncasecmp(R
->Name
.c_str(), Tag
, R
->Name
.length()) == 0)
902 if (R
->Action
!= pkgTagSection::Tag::REWRITE
)
906 else if(R
->Action
== pkgTagSection::Tag::RENAME
&& R
->Data
.length() == TagLen
&&
907 strncasecmp(R
->Data
.c_str(), Tag
, R
->Data
.length()) == 0)
908 data
= This
->FindRaw(R
->Name
.c_str()).to_string();
912 return WriteTag(File
, Tag
, data
);
916 bool pkgTagSection::Write(FileFd
&File
, char const * const * const Order
, std::vector
<Tag
> const &Rewrite
) const
918 // first pass: Write everything we have an order for
921 for (unsigned int I
= 0; Order
[I
] != 0; ++I
)
923 std::vector
<Tag
>::const_iterator R
= Rewrite
.begin();
924 if (RewriteTags(File
, this, Order
[I
], R
, Rewrite
.end()) == false)
926 if (R
!= Rewrite
.end())
929 if (Exists(Order
[I
]) == false)
932 if (WriteTag(File
, Order
[I
], FindRaw(Order
[I
])) == false)
936 // second pass: See if we have tags which aren't ordered
937 if (d
->Tags
.empty() == false)
939 for (std::vector
<pkgTagSectionPrivate::TagData
>::const_iterator T
= d
->Tags
.begin(); T
!= d
->Tags
.end() - 1; ++T
)
941 char const * const fieldname
= Section
+ T
->StartTag
;
942 size_t fieldnamelen
= T
->EndTag
- T
->StartTag
;
946 for (; Order
[I
] != 0; ++I
)
948 if (fieldnamelen
== strlen(Order
[I
]) && strncasecmp(fieldname
, Order
[I
], fieldnamelen
) == 0)
955 std::string
const name(fieldname
, fieldnamelen
);
956 std::vector
<Tag
>::const_iterator R
= Rewrite
.begin();
957 if (RewriteTags(File
, this, name
.c_str(), R
, Rewrite
.end()) == false)
959 if (R
!= Rewrite
.end())
962 if (WriteTag(File
, name
, FindRaw(name
)) == false)
966 // last pass: see if there are any rewrites remaining we haven't done yet
967 for (std::vector
<Tag
>::const_iterator R
= Rewrite
.begin(); R
!= Rewrite
.end(); ++R
)
969 if (R
->Action
== Tag::REMOVE
)
971 std::string
const name
= ((R
->Action
== Tag::RENAME
) ? R
->Data
: R
->Name
);
972 if (Exists(name
.c_str()))
977 for (; Order
[I
] != 0; ++I
)
979 if (strncasecmp(name
.c_str(), Order
[I
], name
.length()) == 0 && name
.length() == strlen(Order
[I
]))
986 if (WriteTag(File
, name
, ((R
->Action
== Tag::RENAME
) ? FindRaw(R
->Name
) : R
->Data
)) == false)
993 void pkgUserTagSection::TrimRecord(bool /*BeforeRecord*/, const char* &End
)/*{{{*/
995 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r' || Stop
[0] == '#'); Stop
++)
997 Stop
= (const char*) memchr(Stop
,'\n',End
-Stop
);
1001 #include "tagfile-order.c"
1003 // TFRewrite - Rewrite a control record /*{{{*/
1004 // ---------------------------------------------------------------------
1005 /* This writes the control record to stdout rewriting it as necessary. The
1006 override map item specificies the rewriting rules to follow. This also
1007 takes the time to sort the feild list. */
1008 APT_IGNORE_DEPRECATED_PUSH
1009 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
1010 TFRewriteData
*Rewrite
)
1012 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
1013 for (unsigned I
= 0; I
!= 256; I
++)
1016 // Set new tag up as necessary.
1017 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
1019 if (Rewrite
[J
].NewTag
== 0)
1020 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
1023 // Write all all of the tags, in order.
1026 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
1028 bool Rewritten
= false;
1030 // See if this is a field that needs to be rewritten
1031 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
1033 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
1036 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
1038 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
1039 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1041 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1048 // See if it is in the fragment
1050 if (Tags
.Find(StringView(Order
[I
]),Pos
) == false)
1054 if (Rewritten
== true)
1057 /* Write out this element, taking a moment to rewrite the tag
1058 in case of changes of case. */
1061 Tags
.Get(Start
,Stop
,Pos
);
1063 if (fputs(Order
[I
],Output
) < 0)
1064 return _error
->Errno("fputs","IO Error to output");
1065 Start
+= strlen(Order
[I
]);
1066 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
1067 return _error
->Errno("fwrite","IO Error to output");
1068 if (Stop
[-1] != '\n')
1069 fprintf(Output
,"\n");
1073 // Now write all the old tags that were missed.
1074 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
1076 if ((Visited
[I
] & 1) == 1)
1081 Tags
.Get(Start
,Stop
,I
);
1082 const char *End
= Start
;
1083 for (; End
< Stop
&& *End
!= ':'; End
++);
1085 // See if this is a field that needs to be rewritten
1086 bool Rewritten
= false;
1087 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
1089 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
1092 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
1094 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
1095 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1097 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1105 if (Rewritten
== true)
1108 // Write out this element
1109 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
1110 return _error
->Errno("fwrite","IO Error to output");
1111 if (Stop
[-1] != '\n')
1112 fprintf(Output
,"\n");
1115 // Now write all the rewrites that were missed
1116 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
1118 if ((Visited
[J
] & 2) == 2)
1121 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
1123 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
1124 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1126 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1132 APT_IGNORE_DEPRECATED_POP
1135 pkgTagSection::~pkgTagSection() { delete d
; }