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>
34 class APT_HIDDEN pkgTagFilePrivate
/*{{{*/
37 void Reset(FileFd
* const pFd
, unsigned long long const pSize
, pkgTagFile::Flags
const pFlags
)
49 isCommentedLine
= false;
53 pkgTagFilePrivate(FileFd
* const pFd
, unsigned long long const Size
, pkgTagFile::Flags
const pFlags
) : Buffer(NULL
)
55 Reset(pFd
, Size
, pFlags
);
58 pkgTagFile::Flags Flags
;
63 unsigned long long iOffset
;
64 unsigned long long Size
;
70 FileChunk(bool const pgood
, size_t const plength
) : good(pgood
), length(plength
) {}
72 std::list
<FileChunk
> chunks
;
81 class APT_HIDDEN pkgTagSectionPrivate
/*{{{*/
84 pkgTagSectionPrivate()
88 unsigned int StartTag
;
90 unsigned int StartValue
;
91 unsigned int NextInBucket
;
93 explicit TagData(unsigned int const StartTag
) : StartTag(StartTag
), EndTag(0), StartValue(0), NextInBucket(0) {}
95 std::vector
<TagData
> Tags
;
99 static unsigned long AlphaHash(const char *Text
, size_t Length
) /*{{{*/
101 /* This very simple hash function for the last 8 letters gives
102 very good performance on the debian package files */
105 Text
+= (Length
- 8);
108 unsigned long Res
= 0;
109 for (size_t i
= 0; i
< Length
; ++i
)
110 Res
= ((unsigned long)(Text
[i
]) & 0xDF) ^ (Res
<< 1);
115 // TagFile::pkgTagFile - Constructor /*{{{*/
116 pkgTagFile::pkgTagFile(FileFd
* const pFd
,pkgTagFile::Flags
const pFlags
, unsigned long long const Size
)
117 : d(new pkgTagFilePrivate(pFd
, Size
+ 4, pFlags
))
119 Init(pFd
, pFlags
, Size
);
121 pkgTagFile::pkgTagFile(FileFd
* const pFd
,unsigned long long const Size
)
122 : pkgTagFile(pFd
, pkgTagFile::STRICT
, Size
)
125 void pkgTagFile::Init(FileFd
* const pFd
, pkgTagFile::Flags
const pFlags
, unsigned long long Size
)
127 /* The size is increased by 4 because if we start with the Size of the
128 filename we need to try to read 1 char more to see an EOF faster, 1
129 char the end-pointer can be on and maybe 2 newlines need to be added
130 to the end of the file -> 4 extra chars */
132 d
->Reset(pFd
, Size
, pFlags
);
134 if (d
->Fd
->IsOpen() == false)
135 d
->Start
= d
->End
= d
->Buffer
= 0;
137 d
->Buffer
= (char*)malloc(sizeof(char) * Size
);
139 if (d
->Buffer
== NULL
)
144 d
->Start
= d
->End
= d
->Buffer
;
146 if (d
->Done
== false)
149 void pkgTagFile::Init(FileFd
* const pFd
,unsigned long long Size
)
151 Init(pFd
, pkgTagFile::STRICT
, Size
);
154 // TagFile::~pkgTagFile - Destructor /*{{{*/
155 pkgTagFile::~pkgTagFile()
160 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
161 APT_PURE
unsigned long pkgTagFile::Offset()
166 // TagFile::Resize - Resize the internal buffer /*{{{*/
167 // ---------------------------------------------------------------------
168 /* Resize the internal buffer (double it in size). Fail if a maximum size
171 bool pkgTagFile::Resize()
173 // fail is the buffer grows too big
174 if(d
->Size
> 1024*1024+1)
177 return Resize(d
->Size
* 2);
179 bool pkgTagFile::Resize(unsigned long long const newSize
)
181 unsigned long long const EndSize
= d
->End
- d
->Start
;
183 // get new buffer and use it
184 char* const newBuffer
= static_cast<char*>(realloc(d
->Buffer
, sizeof(char) * newSize
));
185 if (newBuffer
== NULL
)
187 d
->Buffer
= newBuffer
;
190 // update the start/end pointers to the new buffer
191 d
->Start
= d
->Buffer
;
192 d
->End
= d
->Start
+ EndSize
;
196 // TagFile::Step - Advance to the next section /*{{{*/
197 // ---------------------------------------------------------------------
198 /* If the Section Scanner fails we refill the buffer and try again.
199 * If that fails too, double the buffer size and try again until a
200 * maximum buffer is reached.
202 bool pkgTagFile::Step(pkgTagSection
&Tag
)
204 if(Tag
.Scan(d
->Start
,d
->End
- d
->Start
) == false)
211 if(Tag
.Scan(d
->Start
,d
->End
- d
->Start
, false))
214 if (Resize() == false)
215 return _error
->Error(_("Unable to parse package file %s (%d)"),
216 d
->Fd
->Name().c_str(), 1);
218 } while (Tag
.Scan(d
->Start
,d
->End
- d
->Start
, false) == false);
221 size_t tagSize
= Tag
.size();
224 if ((d
->Flags
& pkgTagFile::SUPPORT_COMMENTS
) == 0)
225 d
->iOffset
+= tagSize
;
228 auto first
= d
->chunks
.begin();
229 for (; first
!= d
->chunks
.end(); ++first
)
231 if (first
->good
== false)
232 d
->iOffset
+= first
->length
;
235 if (tagSize
< first
->length
)
237 first
->length
-= tagSize
;
238 d
->iOffset
+= tagSize
;
243 tagSize
-= first
->length
;
244 d
->iOffset
+= first
->length
;
248 d
->chunks
.erase(d
->chunks
.begin(), first
);
255 // TagFile::Fill - Top up the buffer /*{{{*/
256 // ---------------------------------------------------------------------
257 /* This takes the bit at the end of the buffer and puts it at the start
258 then fills the rest from the file */
259 static bool FillBuffer(pkgTagFilePrivate
* const d
)
261 unsigned long long Actual
= 0;
262 // See if only a bit of the file is left
263 unsigned long long const dataSize
= d
->Size
- ((d
->End
- d
->Buffer
) + 1);
264 if (d
->Fd
->Read(d
->End
, dataSize
, &Actual
) == false)
266 if (Actual
!= dataSize
)
271 static void RemoveCommentsFromBuffer(pkgTagFilePrivate
* const d
)
273 // look for valid comments in the buffer
274 char * good_start
= nullptr, * bad_start
= nullptr;
275 char * current
= d
->Start
;
276 if (d
->isCommentedLine
== false)
278 if (d
->Start
== d
->Buffer
)
280 // the start of the buffer is a newline as a record can't start
281 // in the middle of a line by definition.
282 if (*d
->Start
== '#')
284 d
->isCommentedLine
= true;
286 if (current
> d
->End
)
287 d
->chunks
.emplace_back(false, 1);
290 if (d
->isCommentedLine
== false)
291 good_start
= d
->Start
;
293 bad_start
= d
->Start
;
296 bad_start
= d
->Start
;
298 std::vector
<std::pair
<char*, size_t>> good_parts
;
299 while (current
<= d
->End
)
301 size_t const restLength
= (d
->End
- current
) + 1;
302 if (d
->isCommentedLine
== false)
304 current
= static_cast<char*>(memchr(current
, '#', restLength
));
305 if (current
== nullptr)
307 size_t const goodLength
= d
->End
- good_start
;
308 d
->chunks
.emplace_back(true, goodLength
);
309 if (good_start
!= d
->Start
)
310 good_parts
.push_back(std::make_pair(good_start
, goodLength
));
315 // ensure that this is really a comment and not a '#' in the middle of a line
316 if (*current
== '\n')
318 size_t const goodLength
= (current
- good_start
) + 1;
319 d
->chunks
.emplace_back(true, goodLength
);
320 good_parts
.push_back(std::make_pair(good_start
, goodLength
));
321 good_start
= nullptr;
322 d
->isCommentedLine
= true;
326 else // the current line is a comment
328 current
= static_cast<char*>(memchr(current
, '\n', restLength
));
329 if (current
== nullptr)
331 d
->chunks
.emplace_back(false, (d
->End
- bad_start
));
335 // is the next line a comment, too?
336 if (current
> d
->End
|| *current
!= '#')
338 d
->chunks
.emplace_back(false, (current
- bad_start
));
339 good_start
= current
;
341 d
->isCommentedLine
= false;
347 if (good_parts
.empty() == false)
349 // we found comments, so move later parts over them
351 for (auto const &good
: good_parts
)
353 memmove(current
, good
.first
, good
.second
);
354 current
+= good
.second
;
359 if (d
->isCommentedLine
== true)
361 // deal with a buffer containing only comments
362 // or an (unfinished) comment at the end
363 if (good_parts
.empty() == true)
370 // the buffer was all comment, but ended with the buffer
371 if (good_parts
.empty() == true && good_start
>= d
->End
)
377 bool pkgTagFile::Fill()
379 unsigned long long const EndSize
= d
->End
- d
->Start
;
382 memmove(d
->Buffer
,d
->Start
,EndSize
);
383 d
->Start
= d
->End
= d
->Buffer
+ EndSize
;
386 d
->Start
= d
->End
= d
->Buffer
;
388 unsigned long long Actual
= 0;
389 while (d
->Done
== false && d
->Size
> (Actual
+ 1))
391 if (FillBuffer(d
) == false)
393 if ((d
->Flags
& pkgTagFile::SUPPORT_COMMENTS
) != 0)
394 RemoveCommentsFromBuffer(d
);
395 Actual
= d
->End
- d
->Buffer
;
397 d
->Start
= d
->Buffer
;
401 if (EndSize
<= 3 && Actual
== 0)
403 if (d
->Size
- (d
->End
- d
->Buffer
) < 4)
406 // Append a double new line if one does not exist
407 unsigned int LineCount
= 0;
408 for (const char *E
= d
->End
- 1; E
- d
->End
< 6 && (*E
== '\n' || *E
== '\r'); E
--)
413 if (static_cast<unsigned long long>(d
->End
- d
->Buffer
) >= d
->Size
)
415 for (; LineCount
< 2; ++LineCount
)
422 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
423 // ---------------------------------------------------------------------
424 /* This jumps to a pre-recorded file location and reads the record
426 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long long Offset
)
428 if ((d
->Flags
& pkgTagFile::SUPPORT_COMMENTS
) == 0 &&
429 // We are within a buffer space of the next hit..
430 Offset
>= d
->iOffset
&& d
->iOffset
+ (d
->End
- d
->Start
) > Offset
)
432 unsigned long long Dist
= Offset
- d
->iOffset
;
435 // if we have seen the end, don't ask for more
437 return Tag
.Scan(d
->Start
, d
->End
- d
->Start
);
442 // Reposition and reload..
445 if (d
->Fd
->Seek(Offset
) == false)
447 d
->End
= d
->Start
= d
->Buffer
;
448 d
->isCommentedLine
= false;
454 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
) == true)
457 // This appends a double new line (for the real eof handling)
461 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
, false) == false)
462 return _error
->Error(_("Unable to parse package file %s (%d)"),d
->Fd
->Name().c_str(), 2);
467 // pkgTagSection::pkgTagSection - Constructor /*{{{*/
468 // ---------------------------------------------------------------------
470 APT_IGNORE_DEPRECATED_PUSH
471 pkgTagSection::pkgTagSection()
472 : Section(0), d(new pkgTagSectionPrivate()), Stop(0)
474 memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
));
476 APT_IGNORE_DEPRECATED_POP
478 // TagSection::Scan - Scan for the end of the header information /*{{{*/
479 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
, bool const Restart
)
482 const char *End
= Start
+ MaxLength
;
484 if (Restart
== false && d
->Tags
.empty() == false)
486 Stop
= Section
+ d
->Tags
.back().StartTag
;
489 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
497 if (d
->Tags
.empty() == false)
499 memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
));
502 d
->Tags
.reserve(0x100);
504 unsigned int TagCount
= d
->Tags
.size();
509 pkgTagSectionPrivate::TagData
lastTagData(0);
510 lastTagData
.EndTag
= 0;
511 unsigned long lastTagHash
= 0;
514 TrimRecord(true,End
);
516 // this can happen when TrimRecord trims away the entire Record
517 // (e.g. because it just contains comments)
521 // Start a new index and add it to the hash
522 if (isspace_ascii(Stop
[0]) == 0)
524 // store the last found tag
525 if (lastTagData
.EndTag
!= 0)
527 if (AlphaIndexes
[lastTagHash
] != 0)
528 lastTagData
.NextInBucket
= AlphaIndexes
[lastTagHash
];
529 APT_IGNORE_DEPRECATED_PUSH
530 AlphaIndexes
[lastTagHash
] = TagCount
;
531 APT_IGNORE_DEPRECATED_POP
532 d
->Tags
.push_back(lastTagData
);
535 APT_IGNORE_DEPRECATED(++TagCount
;)
536 lastTagData
= pkgTagSectionPrivate::TagData(Stop
- Section
);
537 // find the colon separating tag and value
538 char const * Colon
= (char const *) memchr(Stop
, ':', End
- Stop
);
541 // find the end of the tag (which might or might not be the colon)
542 char const * EndTag
= Colon
;
544 for (; EndTag
> Stop
&& isspace_ascii(*EndTag
) != 0; --EndTag
)
547 lastTagData
.EndTag
= EndTag
- Section
;
548 lastTagHash
= AlphaHash(Stop
, EndTag
- Stop
);
549 // find the beginning of the value
551 for (; Stop
< End
&& isspace_ascii(*Stop
) != 0; ++Stop
)
552 if (*Stop
== '\n' && Stop
[1] != ' ')
556 lastTagData
.StartValue
= Stop
- Section
;
559 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
564 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++)
568 // Double newline marks the end of the record
569 if (Stop
+1 < End
&& Stop
[1] == '\n')
571 if (lastTagData
.EndTag
!= 0)
573 if (AlphaIndexes
[lastTagHash
] != 0)
574 lastTagData
.NextInBucket
= AlphaIndexes
[lastTagHash
];
575 APT_IGNORE_DEPRECATED(AlphaIndexes
[lastTagHash
] = TagCount
;)
576 d
->Tags
.push_back(lastTagData
);
579 pkgTagSectionPrivate::TagData
const td(Stop
- Section
);
580 d
->Tags
.push_back(td
);
581 TrimRecord(false,End
);
591 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
592 // ---------------------------------------------------------------------
593 /* There should be exactly 2 newline at the end of the record, no more. */
594 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
)
596 if (BeforeRecord
== true)
598 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
601 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
602 // ---------------------------------------------------------------------
603 /* There should be exactly 1 newline at the end of the buffer, no more. */
604 void pkgTagSection::Trim()
606 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
609 // TagSection::Exists - return True if a tag exists /*{{{*/
610 bool pkgTagSection::Exists(const char* const Tag
) const
613 return Find(Tag
, tmp
);
616 // TagSection::Find - Locate a tag /*{{{*/
617 // ---------------------------------------------------------------------
618 /* This searches the section for a tag that matches the given string. */
619 bool pkgTagSection::Find(const char *Tag
,unsigned int &Pos
) const
621 size_t const Length
= strlen(Tag
);
622 unsigned int Bucket
= AlphaIndexes
[AlphaHash(Tag
, Length
)];
626 for (; Bucket
!= 0; Bucket
= d
->Tags
[Bucket
- 1].NextInBucket
)
628 if ((d
->Tags
[Bucket
- 1].EndTag
- d
->Tags
[Bucket
- 1].StartTag
) != Length
)
631 char const * const St
= Section
+ d
->Tags
[Bucket
- 1].StartTag
;
632 if (strncasecmp(Tag
,St
,Length
) != 0)
642 bool pkgTagSection::Find(const char *Tag
,const char *&Start
,
643 const char *&End
) const
646 if (Find(Tag
, Pos
) == false)
649 Start
= Section
+ d
->Tags
[Pos
].StartValue
;
650 // Strip off the gunk from the end
651 End
= Section
+ d
->Tags
[Pos
+ 1].StartTag
;
652 if (unlikely(Start
> End
))
653 return _error
->Error("Internal parsing error");
655 for (; isspace_ascii(End
[-1]) != 0 && End
> Start
; --End
);
660 // TagSection::FindS - Find a string /*{{{*/
661 string
pkgTagSection::FindS(const char *Tag
) const
665 if (Find(Tag
,Start
,End
) == false)
667 return string(Start
,End
);
670 // TagSection::FindRawS - Find a string /*{{{*/
671 string
pkgTagSection::FindRawS(const char *Tag
) const
674 if (Find(Tag
, Pos
) == false)
677 char const *Start
= (char const *) memchr(Section
+ d
->Tags
[Pos
].EndTag
, ':', d
->Tags
[Pos
].StartValue
- d
->Tags
[Pos
].EndTag
);
679 char const *End
= Section
+ d
->Tags
[Pos
+ 1].StartTag
;
680 if (unlikely(Start
> End
))
683 for (; isspace_ascii(End
[-1]) != 0 && End
> Start
; --End
);
685 return std::string(Start
, End
- Start
);
688 // TagSection::FindI - Find an integer /*{{{*/
689 // ---------------------------------------------------------------------
691 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
695 if (Find(Tag
,Start
,Stop
) == false)
698 // Copy it into a temp buffer so we can use strtol
700 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
702 strncpy(S
,Start
,Stop
-Start
);
707 signed long Result
= strtol(S
,&End
,10);
708 if (errno
== ERANGE
||
709 Result
< std::numeric_limits
<int>::min() || Result
> std::numeric_limits
<int>::max()) {
711 _error
->Error(_("Cannot convert %s to integer: out of range"), S
);
718 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
719 // ---------------------------------------------------------------------
721 unsigned long long pkgTagSection::FindULL(const char *Tag
, unsigned long long const &Default
) const
725 if (Find(Tag
,Start
,Stop
) == false)
728 // Copy it into a temp buffer so we can use strtoull
730 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
732 strncpy(S
,Start
,Stop
-Start
);
736 unsigned long long Result
= strtoull(S
,&End
,10);
742 // TagSection::FindB - Find boolean value /*{{{*/
743 // ---------------------------------------------------------------------
745 bool pkgTagSection::FindB(const char *Tag
, bool const &Default
) const
747 const char *Start
, *Stop
;
748 if (Find(Tag
, Start
, Stop
) == false)
750 return StringToBool(string(Start
, Stop
));
753 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
754 // ---------------------------------------------------------------------
755 /* The bits marked in Flag are masked on/off in Flags */
756 bool pkgTagSection::FindFlag(const char * const Tag
, uint8_t &Flags
,
757 uint8_t const Flag
) const
761 if (Find(Tag
,Start
,Stop
) == false)
763 return FindFlag(Flags
, Flag
, Start
, Stop
);
765 bool pkgTagSection::FindFlag(uint8_t &Flags
, uint8_t const Flag
,
766 char const* const Start
, char const* const Stop
)
768 switch (StringToBool(string(Start
, Stop
)))
779 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
784 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
785 unsigned long Flag
) const
789 if (Find(Tag
,Start
,Stop
) == false)
791 return FindFlag(Flags
, Flag
, Start
, Stop
);
793 bool pkgTagSection::FindFlag(unsigned long &Flags
, unsigned long Flag
,
794 char const* Start
, char const* Stop
)
796 switch (StringToBool(string(Start
, Stop
)))
807 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
813 void pkgTagSection::Get(const char *&Start
,const char *&Stop
,unsigned int I
) const/*{{{*/
815 Start
= Section
+ d
->Tags
[I
].StartTag
;
816 Stop
= Section
+ d
->Tags
[I
+1].StartTag
;
819 APT_PURE
unsigned int pkgTagSection::Count() const { /*{{{*/
820 if (d
->Tags
.empty() == true)
822 // the last element is just marking the end and isn't a real one
823 return d
->Tags
.size() - 1;
826 // TagSection::Write - Ordered (re)writing of fields /*{{{*/
827 pkgTagSection::Tag
pkgTagSection::Tag::Remove(std::string
const &Name
)
829 return Tag(REMOVE
, Name
, "");
831 pkgTagSection::Tag
pkgTagSection::Tag::Rename(std::string
const &OldName
, std::string
const &NewName
)
833 return Tag(RENAME
, OldName
, NewName
);
835 pkgTagSection::Tag
pkgTagSection::Tag::Rewrite(std::string
const &Name
, std::string
const &Data
)
837 if (Data
.empty() == true)
838 return Tag(REMOVE
, Name
, "");
840 return Tag(REWRITE
, Name
, Data
);
842 static bool WriteTag(FileFd
&File
, std::string Tag
, std::string
const &Value
)
844 if (Value
.empty() || isspace_ascii(Value
[0]) != 0)
850 return File
.Write(Tag
.c_str(), Tag
.length());
852 static bool RewriteTags(FileFd
&File
, pkgTagSection
const * const This
, char const * const Tag
,
853 std::vector
<pkgTagSection::Tag
>::const_iterator
&R
,
854 std::vector
<pkgTagSection::Tag
>::const_iterator
const &REnd
)
856 size_t const TagLen
= strlen(Tag
);
857 for (; R
!= REnd
; ++R
)
860 if (R
->Name
.length() == TagLen
&& strncasecmp(R
->Name
.c_str(), Tag
, R
->Name
.length()) == 0)
862 if (R
->Action
!= pkgTagSection::Tag::REWRITE
)
866 else if(R
->Action
== pkgTagSection::Tag::RENAME
&& R
->Data
.length() == TagLen
&&
867 strncasecmp(R
->Data
.c_str(), Tag
, R
->Data
.length()) == 0)
868 data
= This
->FindRawS(R
->Name
.c_str());
872 return WriteTag(File
, Tag
, data
);
876 bool pkgTagSection::Write(FileFd
&File
, char const * const * const Order
, std::vector
<Tag
> const &Rewrite
) const
878 // first pass: Write everything we have an order for
881 for (unsigned int I
= 0; Order
[I
] != 0; ++I
)
883 std::vector
<Tag
>::const_iterator R
= Rewrite
.begin();
884 if (RewriteTags(File
, this, Order
[I
], R
, Rewrite
.end()) == false)
886 if (R
!= Rewrite
.end())
889 if (Exists(Order
[I
]) == false)
892 if (WriteTag(File
, Order
[I
], FindRawS(Order
[I
])) == false)
896 // second pass: See if we have tags which aren't ordered
897 if (d
->Tags
.empty() == false)
899 for (std::vector
<pkgTagSectionPrivate::TagData
>::const_iterator T
= d
->Tags
.begin(); T
!= d
->Tags
.end() - 1; ++T
)
901 char const * const fieldname
= Section
+ T
->StartTag
;
902 size_t fieldnamelen
= T
->EndTag
- T
->StartTag
;
906 for (; Order
[I
] != 0; ++I
)
908 if (fieldnamelen
== strlen(Order
[I
]) && strncasecmp(fieldname
, Order
[I
], fieldnamelen
) == 0)
915 std::string
const name(fieldname
, fieldnamelen
);
916 std::vector
<Tag
>::const_iterator R
= Rewrite
.begin();
917 if (RewriteTags(File
, this, name
.c_str(), R
, Rewrite
.end()) == false)
919 if (R
!= Rewrite
.end())
922 if (WriteTag(File
, name
, FindRawS(name
.c_str())) == false)
926 // last pass: see if there are any rewrites remaining we haven't done yet
927 for (std::vector
<Tag
>::const_iterator R
= Rewrite
.begin(); R
!= Rewrite
.end(); ++R
)
929 if (R
->Action
== Tag::REMOVE
)
931 std::string
const name
= ((R
->Action
== Tag::RENAME
) ? R
->Data
: R
->Name
);
932 if (Exists(name
.c_str()))
937 for (; Order
[I
] != 0; ++I
)
939 if (strncasecmp(name
.c_str(), Order
[I
], name
.length()) == 0 && name
.length() == strlen(Order
[I
]))
946 if (WriteTag(File
, name
, ((R
->Action
== Tag::RENAME
) ? FindRawS(R
->Name
.c_str()) : R
->Data
)) == false)
953 void pkgUserTagSection::TrimRecord(bool /*BeforeRecord*/, const char* &End
)/*{{{*/
955 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r' || Stop
[0] == '#'); Stop
++)
957 Stop
= (const char*) memchr(Stop
,'\n',End
-Stop
);
961 #include "tagfile-order.c"
963 // TFRewrite - Rewrite a control record /*{{{*/
964 // ---------------------------------------------------------------------
965 /* This writes the control record to stdout rewriting it as necessary. The
966 override map item specificies the rewriting rules to follow. This also
967 takes the time to sort the feild list. */
968 APT_IGNORE_DEPRECATED_PUSH
969 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
970 TFRewriteData
*Rewrite
)
972 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
973 for (unsigned I
= 0; I
!= 256; I
++)
976 // Set new tag up as necessary.
977 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
979 if (Rewrite
[J
].NewTag
== 0)
980 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
983 // Write all all of the tags, in order.
986 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
988 bool Rewritten
= false;
990 // See if this is a field that needs to be rewritten
991 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
993 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
996 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
998 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
999 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1001 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1008 // See if it is in the fragment
1010 if (Tags
.Find(Order
[I
],Pos
) == false)
1014 if (Rewritten
== true)
1017 /* Write out this element, taking a moment to rewrite the tag
1018 in case of changes of case. */
1021 Tags
.Get(Start
,Stop
,Pos
);
1023 if (fputs(Order
[I
],Output
) < 0)
1024 return _error
->Errno("fputs","IO Error to output");
1025 Start
+= strlen(Order
[I
]);
1026 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
1027 return _error
->Errno("fwrite","IO Error to output");
1028 if (Stop
[-1] != '\n')
1029 fprintf(Output
,"\n");
1033 // Now write all the old tags that were missed.
1034 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
1036 if ((Visited
[I
] & 1) == 1)
1041 Tags
.Get(Start
,Stop
,I
);
1042 const char *End
= Start
;
1043 for (; End
< Stop
&& *End
!= ':'; End
++);
1045 // See if this is a field that needs to be rewritten
1046 bool Rewritten
= false;
1047 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
1049 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
1052 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
1054 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
1055 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1057 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1065 if (Rewritten
== true)
1068 // Write out this element
1069 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
1070 return _error
->Errno("fwrite","IO Error to output");
1071 if (Stop
[-1] != '\n')
1072 fprintf(Output
,"\n");
1075 // Now write all the rewrites that were missed
1076 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
1078 if ((Visited
[J
] & 2) == 2)
1081 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
1083 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
1084 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1086 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
1092 APT_IGNORE_DEPRECATED_POP
1095 pkgTagSection::~pkgTagSection() { delete d
; }