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 AlphaHash(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
));
478 APT_IGNORE_DEPRECATED_POP
480 // TagSection::Scan - Scan for the end of the header information /*{{{*/
481 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
, bool const Restart
)
484 const char *End
= Start
+ MaxLength
;
486 if (Restart
== false && d
->Tags
.empty() == false)
488 Stop
= Section
+ d
->Tags
.back().StartTag
;
491 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
499 if (d
->Tags
.empty() == false)
501 memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
));
504 d
->Tags
.reserve(0x100);
506 unsigned int TagCount
= d
->Tags
.size();
511 pkgTagSectionPrivate::TagData
lastTagData(0);
512 lastTagData
.EndTag
= 0;
513 unsigned long lastTagHash
= 0;
516 TrimRecord(true,End
);
518 // this can happen when TrimRecord trims away the entire Record
519 // (e.g. because it just contains comments)
523 // Start a new index and add it to the hash
524 if (isspace_ascii(Stop
[0]) == 0)
526 // store the last found tag
527 if (lastTagData
.EndTag
!= 0)
529 if (AlphaIndexes
[lastTagHash
] != 0)
530 lastTagData
.NextInBucket
= AlphaIndexes
[lastTagHash
];
531 APT_IGNORE_DEPRECATED_PUSH
532 AlphaIndexes
[lastTagHash
] = TagCount
;
533 APT_IGNORE_DEPRECATED_POP
534 d
->Tags
.push_back(lastTagData
);
537 APT_IGNORE_DEPRECATED(++TagCount
;)
538 lastTagData
= pkgTagSectionPrivate::TagData(Stop
- Section
);
539 // find the colon separating tag and value
540 char const * Colon
= (char const *) memchr(Stop
, ':', End
- Stop
);
543 // find the end of the tag (which might or might not be the colon)
544 char const * EndTag
= Colon
;
546 for (; EndTag
> Stop
&& isspace_ascii(*EndTag
) != 0; --EndTag
)
549 lastTagData
.EndTag
= EndTag
- Section
;
550 lastTagHash
= AlphaHash(Stop
, EndTag
- Stop
);
551 // find the beginning of the value
553 for (; Stop
< End
&& isspace_ascii(*Stop
) != 0; ++Stop
)
554 if (*Stop
== '\n' && Stop
[1] != ' ')
558 lastTagData
.StartValue
= Stop
- Section
;
561 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
566 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++)
570 // Double newline marks the end of the record
571 if (Stop
+1 < End
&& Stop
[1] == '\n')
573 if (lastTagData
.EndTag
!= 0)
575 if (AlphaIndexes
[lastTagHash
] != 0)
576 lastTagData
.NextInBucket
= AlphaIndexes
[lastTagHash
];
577 APT_IGNORE_DEPRECATED(AlphaIndexes
[lastTagHash
] = TagCount
;)
578 d
->Tags
.push_back(lastTagData
);
581 pkgTagSectionPrivate::TagData
const td(Stop
- Section
);
582 d
->Tags
.push_back(td
);
583 TrimRecord(false,End
);
593 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
594 // ---------------------------------------------------------------------
595 /* There should be exactly 2 newline at the end of the record, no more. */
596 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
)
598 if (BeforeRecord
== true)
600 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
603 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
604 // ---------------------------------------------------------------------
605 /* There should be exactly 1 newline at the end of the buffer, no more. */
606 void pkgTagSection::Trim()
608 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
611 // TagSection::Exists - return True if a tag exists /*{{{*/
612 bool pkgTagSection::Exists(StringView Tag
) const
615 return Find(Tag
, tmp
);
618 // TagSection::Find - Locate a tag /*{{{*/
619 // ---------------------------------------------------------------------
620 /* This searches the section for a tag that matches the given string. */
621 bool pkgTagSection::Find(StringView TagView
,unsigned int &Pos
) const
623 const char * const Tag
= TagView
.data();
624 size_t const Length
= TagView
.length();
625 unsigned int Bucket
= AlphaIndexes
[AlphaHash(Tag
, Length
)];
629 for (; Bucket
!= 0; Bucket
= d
->Tags
[Bucket
- 1].NextInBucket
)
631 if ((d
->Tags
[Bucket
- 1].EndTag
- d
->Tags
[Bucket
- 1].StartTag
) != Length
)
634 char const * const St
= Section
+ d
->Tags
[Bucket
- 1].StartTag
;
635 if (strncasecmp(Tag
,St
,Length
) != 0)
646 bool pkgTagSection::Find(StringView Tag
,const char *&Start
,
647 const char *&End
) const
650 if (Find(Tag
, Pos
) == false)
653 Start
= Section
+ d
->Tags
[Pos
].StartValue
;
654 // Strip off the gunk from the end
655 End
= Section
+ d
->Tags
[Pos
+ 1].StartTag
;
656 if (unlikely(Start
> End
))
657 return _error
->Error("Internal parsing error");
659 for (; isspace_ascii(End
[-1]) != 0 && End
> Start
; --End
);
664 // TagSection::FindS - Find a string /*{{{*/
665 StringView
pkgTagSection::Find(StringView Tag
) const
669 if (Find(Tag
,Start
,End
) == false)
671 return StringView(Start
, End
- Start
);
674 // TagSection::FindRawS - Find a string /*{{{*/
675 StringView
pkgTagSection::FindRaw(StringView Tag
) const
678 if (Find(Tag
, Pos
) == false)
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
);
692 // TagSection::FindI - Find an integer /*{{{*/
693 // ---------------------------------------------------------------------
695 signed int pkgTagSection::FindI(StringView Tag
,signed long Default
) const
699 if (Find(Tag
,Start
,Stop
) == false)
702 // Copy it into a temp buffer so we can use strtol
704 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
706 strncpy(S
,Start
,Stop
-Start
);
711 signed long Result
= strtol(S
,&End
,10);
712 if (errno
== ERANGE
||
713 Result
< std::numeric_limits
<int>::min() || Result
> std::numeric_limits
<int>::max()) {
715 _error
->Error(_("Cannot convert %s to integer: out of range"), S
);
722 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
723 // ---------------------------------------------------------------------
725 unsigned long long pkgTagSection::FindULL(StringView Tag
, unsigned long long const &Default
) const
729 if (Find(Tag
,Start
,Stop
) == false)
732 // Copy it into a temp buffer so we can use strtoull
734 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
736 strncpy(S
,Start
,Stop
-Start
);
740 unsigned long long Result
= strtoull(S
,&End
,10);
746 // TagSection::FindB - Find boolean value /*{{{*/
747 // ---------------------------------------------------------------------
749 bool pkgTagSection::FindB(StringView Tag
, bool Default
) const
751 const char *Start
, *Stop
;
752 if (Find(Tag
, Start
, Stop
) == false)
754 return StringToBool(string(Start
, Stop
));
757 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
758 // ---------------------------------------------------------------------
759 /* The bits marked in Flag are masked on/off in Flags */
760 bool pkgTagSection::FindFlag(StringView Tag
, uint8_t &Flags
,
761 uint8_t const Flag
) const
765 if (Find(Tag
,Start
,Stop
) == false)
767 return FindFlag(Flags
, Flag
, Start
, Stop
);
769 bool pkgTagSection::FindFlag(uint8_t &Flags
, uint8_t const Flag
,
770 char const* const Start
, char const* const Stop
)
772 switch (StringToBool(string(Start
, Stop
)))
783 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
788 bool pkgTagSection::FindFlag(StringView Tag
,unsigned long &Flags
,
789 unsigned long Flag
) const
793 if (Find(Tag
,Start
,Stop
) == false)
795 return FindFlag(Flags
, Flag
, Start
, Stop
);
797 bool pkgTagSection::FindFlag(unsigned long &Flags
, unsigned long Flag
,
798 char const* Start
, char const* Stop
)
800 switch (StringToBool(string(Start
, Stop
)))
811 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
817 void pkgTagSection::Get(const char *&Start
,const char *&Stop
,unsigned int I
) const/*{{{*/
819 Start
= Section
+ d
->Tags
[I
].StartTag
;
820 Stop
= Section
+ d
->Tags
[I
+1].StartTag
;
823 APT_PURE
unsigned int pkgTagSection::Count() const { /*{{{*/
824 if (d
->Tags
.empty() == true)
826 // the last element is just marking the end and isn't a real one
827 return d
->Tags
.size() - 1;
830 // TagSection::Write - Ordered (re)writing of fields /*{{{*/
831 pkgTagSection::Tag
pkgTagSection::Tag::Remove(std::string
const &Name
)
833 return Tag(REMOVE
, Name
, "");
835 pkgTagSection::Tag
pkgTagSection::Tag::Rename(std::string
const &OldName
, std::string
const &NewName
)
837 return Tag(RENAME
, OldName
, NewName
);
839 pkgTagSection::Tag
pkgTagSection::Tag::Rewrite(std::string
const &Name
, std::string
const &Data
)
841 if (Data
.empty() == true)
842 return Tag(REMOVE
, Name
, "");
844 return Tag(REWRITE
, Name
, Data
);
846 static bool WriteTag(FileFd
&File
, std::string Tag
, StringView Value
)
848 if (Value
.empty() || isspace_ascii(Value
[0]) != 0)
852 Tag
.append(Value
.data(), Value
.length());
854 return File
.Write(Tag
.c_str(), Tag
.length());
856 static bool RewriteTags(FileFd
&File
, pkgTagSection
const * const This
, char const * const Tag
,
857 std::vector
<pkgTagSection::Tag
>::const_iterator
&R
,
858 std::vector
<pkgTagSection::Tag
>::const_iterator
const &REnd
)
860 size_t const TagLen
= strlen(Tag
);
861 for (; R
!= REnd
; ++R
)
864 if (R
->Name
.length() == TagLen
&& strncasecmp(R
->Name
.c_str(), Tag
, R
->Name
.length()) == 0)
866 if (R
->Action
!= pkgTagSection::Tag::REWRITE
)
870 else if(R
->Action
== pkgTagSection::Tag::RENAME
&& R
->Data
.length() == TagLen
&&
871 strncasecmp(R
->Data
.c_str(), Tag
, R
->Data
.length()) == 0)
872 data
= This
->FindRaw(R
->Name
.c_str()).to_string();
876 return WriteTag(File
, Tag
, data
);
880 bool pkgTagSection::Write(FileFd
&File
, char const * const * const Order
, std::vector
<Tag
> const &Rewrite
) const
882 // first pass: Write everything we have an order for
885 for (unsigned int I
= 0; Order
[I
] != 0; ++I
)
887 std::vector
<Tag
>::const_iterator R
= Rewrite
.begin();
888 if (RewriteTags(File
, this, Order
[I
], R
, Rewrite
.end()) == false)
890 if (R
!= Rewrite
.end())
893 if (Exists(Order
[I
]) == false)
896 if (WriteTag(File
, Order
[I
], FindRaw(Order
[I
])) == false)
900 // second pass: See if we have tags which aren't ordered
901 if (d
->Tags
.empty() == false)
903 for (std::vector
<pkgTagSectionPrivate::TagData
>::const_iterator T
= d
->Tags
.begin(); T
!= d
->Tags
.end() - 1; ++T
)
905 char const * const fieldname
= Section
+ T
->StartTag
;
906 size_t fieldnamelen
= T
->EndTag
- T
->StartTag
;
910 for (; Order
[I
] != 0; ++I
)
912 if (fieldnamelen
== strlen(Order
[I
]) && strncasecmp(fieldname
, Order
[I
], fieldnamelen
) == 0)
919 std::string
const name(fieldname
, fieldnamelen
);
920 std::vector
<Tag
>::const_iterator R
= Rewrite
.begin();
921 if (RewriteTags(File
, this, name
.c_str(), R
, Rewrite
.end()) == false)
923 if (R
!= Rewrite
.end())
926 if (WriteTag(File
, name
, FindRaw(name
)) == false)
930 // last pass: see if there are any rewrites remaining we haven't done yet
931 for (std::vector
<Tag
>::const_iterator R
= Rewrite
.begin(); R
!= Rewrite
.end(); ++R
)
933 if (R
->Action
== Tag::REMOVE
)
935 std::string
const name
= ((R
->Action
== Tag::RENAME
) ? R
->Data
: R
->Name
);
936 if (Exists(name
.c_str()))
941 for (; Order
[I
] != 0; ++I
)
943 if (strncasecmp(name
.c_str(), Order
[I
], name
.length()) == 0 && name
.length() == strlen(Order
[I
]))
950 if (WriteTag(File
, name
, ((R
->Action
== Tag::RENAME
) ? FindRaw(R
->Name
) : R
->Data
)) == false)
957 void pkgUserTagSection::TrimRecord(bool /*BeforeRecord*/, const char* &End
)/*{{{*/
959 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r' || Stop
[0] == '#'); Stop
++)
961 Stop
= (const char*) memchr(Stop
,'\n',End
-Stop
);
965 #include "tagfile-order.c"
967 // TFRewrite - Rewrite a control record /*{{{*/
968 // ---------------------------------------------------------------------
969 /* This writes the control record to stdout rewriting it as necessary. The
970 override map item specificies the rewriting rules to follow. This also
971 takes the time to sort the feild list. */
972 APT_IGNORE_DEPRECATED_PUSH
973 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
974 TFRewriteData
*Rewrite
)
976 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
977 for (unsigned I
= 0; I
!= 256; I
++)
980 // Set new tag up as necessary.
981 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
983 if (Rewrite
[J
].NewTag
== 0)
984 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
987 // Write all all of the tags, in order.
990 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
992 bool Rewritten
= false;
994 // See if this is a field that needs to be rewritten
995 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
997 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
1000 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
1002 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
1003 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1005 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1012 // See if it is in the fragment
1014 if (Tags
.Find(StringView(Order
[I
]),Pos
) == false)
1018 if (Rewritten
== true)
1021 /* Write out this element, taking a moment to rewrite the tag
1022 in case of changes of case. */
1025 Tags
.Get(Start
,Stop
,Pos
);
1027 if (fputs(Order
[I
],Output
) < 0)
1028 return _error
->Errno("fputs","IO Error to output");
1029 Start
+= strlen(Order
[I
]);
1030 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
1031 return _error
->Errno("fwrite","IO Error to output");
1032 if (Stop
[-1] != '\n')
1033 fprintf(Output
,"\n");
1037 // Now write all the old tags that were missed.
1038 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
1040 if ((Visited
[I
] & 1) == 1)
1045 Tags
.Get(Start
,Stop
,I
);
1046 const char *End
= Start
;
1047 for (; End
< Stop
&& *End
!= ':'; End
++);
1049 // See if this is a field that needs to be rewritten
1050 bool Rewritten
= false;
1051 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
1053 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
1056 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
1058 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
1059 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1061 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1069 if (Rewritten
== true)
1072 // Write out this element
1073 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
1074 return _error
->Errno("fwrite","IO Error to output");
1075 if (Stop
[-1] != '\n')
1076 fprintf(Output
,"\n");
1079 // Now write all the rewrites that were missed
1080 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
1082 if ((Visited
[J
] & 2) == 2)
1085 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
1087 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
1088 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1090 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1096 APT_IGNORE_DEPRECATED_POP
1099 pkgTagSection::~pkgTagSection() { delete d
; }