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/mmap.h>
17 #include <apt-pkg/tagfile.h>
18 #include <apt-pkg/tagfile-keys.h>
19 #include <apt-pkg/error.h>
20 #include <apt-pkg/strutl.h>
21 #include <apt-pkg/fileutl.h>
22 #include <apt-pkg/string_view.h>
36 using APT::StringView
;
38 class APT_HIDDEN pkgTagFilePrivate
/*{{{*/
41 void Reset(FileFd
* const pFd
, pkgTagFile::Flags
const pFlags
)
55 pkgTagFilePrivate(FileFd
* const pFd
, pkgTagFile::Flags
const pFlags
) : Map(NULL
)
60 pkgTagFile::Flags Flags
;
65 unsigned long long iOffset
;
75 class APT_HIDDEN pkgTagSectionPrivate
/*{{{*/
78 pkgTagSectionPrivate()
82 unsigned int StartTag
;
84 unsigned int StartValue
;
85 unsigned int NextInBucket
;
87 explicit TagData(unsigned int const StartTag
) : StartTag(StartTag
), EndTag(0), StartValue(0), NextInBucket(0) {}
89 std::vector
<TagData
> Tags
;
93 static unsigned long BetaHash(const char *Text
, size_t Length
) /*{{{*/
95 /* This very simple hash function for the last 8 letters gives
96 very good performance on the debian package files */
102 unsigned long Res
= 0;
103 for (size_t i
= 0; i
< Length
; ++i
)
104 Res
= ((unsigned long)(Text
[i
]) & 0xDF) ^ (Res
<< 1);
109 // TagFile::pkgTagFile - Constructor /*{{{*/
110 pkgTagFile::pkgTagFile(FileFd
* const pFd
,pkgTagFile::Flags
const pFlags
)
111 : d(new pkgTagFilePrivate(pFd
, pFlags
))
115 pkgTagFile::pkgTagFile(FileFd
* const pFd
)
116 : pkgTagFile(pFd
, pkgTagFile::STRICT
)
119 void pkgTagFile::Init(FileFd
* const pFd
, pkgTagFile::Flags
const pFlags
)
121 d
->Reset(pFd
, pFlags
);
123 if (d
->Fd
->IsOpen() == false || d
->Fd
->Size() == 0)
126 d
->Map
= new MMap(*d
->Fd
, MMap::ReadOnly
);
127 d
->Buffer
= static_cast<char *>(d
->Map
->Data());
130 if (d
->Buffer
== NULL
)
134 d
->End
= d
->Buffer
+ d
->Map
->Size();
137 d
->Start
= d
->Buffer
;
139 void pkgTagFile::Init(FileFd
* const pFd
)
141 Init(pFd
, pkgTagFile::STRICT
);
144 // TagFile::~pkgTagFile - Destructor /*{{{*/
145 pkgTagFile::~pkgTagFile()
150 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
151 APT_PURE
unsigned long pkgTagFile::Offset()
156 // TagFile::Step - Advance to the next section /*{{{*/
157 // ---------------------------------------------------------------------
158 /* If the Section Scanner fails we refill the buffer and try again.
159 * If that fails too, double the buffer size and try again until a
160 * maximum buffer is reached.
162 bool pkgTagFile::Step(pkgTagSection
&Tag
)
164 if(Tag
.Scan(d
->Start
,d
->End
- d
->Start
,(d
->Flags
& SUPPORT_COMMENTS
) != 0) == false)
166 if (d
->Start
== d
->End
)
169 return _error
->Warning(_("Unable to parse package file %s (%d)"),
170 d
->Fd
->Name().c_str(), 1);
173 size_t tagSize
= Tag
.size();
175 d
->iOffset
+= tagSize
;
179 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
180 // ---------------------------------------------------------------------
181 /* This jumps to a pre-recorded file location and reads the record
183 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long long Offset
)
185 unsigned int Size(d
->Map
->Size());
189 // Reposition and reload..
192 d
->Start
= d
->Buffer
+ d
->iOffset
;
197 // pkgTagSection::pkgTagSection - Constructor /*{{{*/
198 // ---------------------------------------------------------------------
200 APT_IGNORE_DEPRECATED_PUSH
201 pkgTagSection::pkgTagSection()
202 : Section(0), d(new pkgTagSectionPrivate()), Stop(0)
204 memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
));
205 memset(&BetaIndexes
, 0, sizeof(BetaIndexes
));
207 APT_IGNORE_DEPRECATED_POP
209 // TagSection::Scan - Scan for the end of the header information /*{{{*/
210 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
,bool const SupportComments
)
213 const char *End
= Start
+ MaxLength
;
216 if (d
->Tags
.empty() == false)
218 memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
));
219 memset(&BetaIndexes
, 0, sizeof(BetaIndexes
));
222 d
->Tags
.reserve(0x100);
224 unsigned int TagCount
= d
->Tags
.size();
229 pkgTagSectionPrivate::TagData
lastTagData(0);
230 lastTagData
.EndTag
= 0;
231 Key lastTagKey
= Key::Unknown
;
232 unsigned int lastTagHash
= 0;
235 TrimRecord(true,End
,SupportComments
);
237 // this can happen when TrimRecord trims away the entire Record
238 // (e.g. because it just contains comments)
242 // Start a new index and add it to the hash
243 if (isspace_ascii(Stop
[0]) == 0)
245 // store the last found tag
246 if (lastTagData
.EndTag
!= 0)
248 if (lastTagKey
!= Key::Unknown
) {
249 AlphaIndexes
[static_cast<size_t>(lastTagKey
)] = TagCount
;
251 if (BetaIndexes
[lastTagHash
] != 0)
252 lastTagData
.NextInBucket
= BetaIndexes
[lastTagHash
];
253 APT_IGNORE_DEPRECATED_PUSH
254 BetaIndexes
[lastTagHash
] = TagCount
;
255 APT_IGNORE_DEPRECATED_POP
257 d
->Tags
.push_back(lastTagData
);
260 APT_IGNORE_DEPRECATED(++TagCount
;)
261 lastTagData
= pkgTagSectionPrivate::TagData(Stop
- Section
);
262 // find the colon separating tag and value
263 char const * Colon
= (char const *) memchr(Stop
, ':', End
- Stop
);
266 // find the end of the tag (which might or might not be the colon)
267 char const * EndTag
= Colon
;
269 for (; EndTag
> Stop
&& isspace_ascii(*EndTag
) != 0; --EndTag
)
272 lastTagData
.EndTag
= EndTag
- Section
;
273 lastTagKey
= pkgTagHash(Stop
, EndTag
- Stop
);
274 if (lastTagKey
== Key::Unknown
)
275 lastTagHash
= BetaHash(Stop
, EndTag
- Stop
);
276 // find the beginning of the value
278 for (; Stop
< End
&& isspace_ascii(*Stop
) != 0; ++Stop
)
279 if (*Stop
== '\n' && Stop
[1] != ' ')
283 lastTagData
.StartValue
= Stop
- Section
;
286 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
293 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++)
297 // Double newline marks the end of the record
298 if (Stop
+1 < End
&& Stop
[1] == '\n')
300 if (lastTagData
.EndTag
!= 0)
302 if (lastTagKey
!= Key::Unknown
) {
303 AlphaIndexes
[static_cast<size_t>(lastTagKey
)] = TagCount
;
305 if (BetaIndexes
[lastTagHash
] != 0)
306 lastTagData
.NextInBucket
= BetaIndexes
[lastTagHash
];
307 APT_IGNORE_DEPRECATED(BetaIndexes
[lastTagHash
] = TagCount
;)
309 d
->Tags
.push_back(lastTagData
);
315 pkgTagSectionPrivate::TagData
const td(Stop
- Section
);
316 d
->Tags
.push_back(td
);
317 TrimRecord(false,End
,SupportComments
);
327 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
328 // ---------------------------------------------------------------------
329 /* There should be exactly 2 newline at the end of the record, no more. */
330 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
, bool SupportComments
)
332 if (BeforeRecord
== false)
333 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
334 if (SupportComments
&& Stop
< End
&& Stop
[0] == '#') {
335 Stop
= (const char*) memchr(Stop
,'\n',End
-Stop
) ?: End
;
340 // TagSection::Exists - return True if a tag exists /*{{{*/
341 bool pkgTagSection::Exists(StringView Tag
) const
344 return Find(Tag
, tmp
);
347 // TagSection::Find - Locate a tag /*{{{*/
348 // ---------------------------------------------------------------------
349 /* This searches the section for a tag that matches the given string. */
350 bool pkgTagSection::Find(Key key
,unsigned int &Pos
) const
352 auto Bucket
= AlphaIndexes
[static_cast<size_t>(key
)];
356 bool pkgTagSection::Find(StringView TagView
,unsigned int &Pos
) const
358 const char * const Tag
= TagView
.data();
359 size_t const Length
= TagView
.length();
360 auto key
= pkgTagHash(Tag
, Length
);
361 if (key
!= Key::Unknown
)
362 return Find(key
, Pos
);
364 unsigned int Bucket
= BetaIndexes
[BetaHash(Tag
, Length
)];
368 for (; Bucket
!= 0; Bucket
= d
->Tags
[Bucket
- 1].NextInBucket
)
370 if ((d
->Tags
[Bucket
- 1].EndTag
- d
->Tags
[Bucket
- 1].StartTag
) != Length
)
373 char const * const St
= Section
+ d
->Tags
[Bucket
- 1].StartTag
;
374 if (strncasecmp(Tag
,St
,Length
) != 0)
385 bool pkgTagSection::FindInternal(unsigned int Pos
, const char *&Start
,
386 const char *&End
) const
388 Start
= Section
+ d
->Tags
[Pos
].StartValue
;
389 // Strip off the gunk from the end
390 End
= Section
+ d
->Tags
[Pos
+ 1].StartTag
;
391 if (unlikely(Start
> End
))
392 return _error
->Error("Internal parsing error");
394 for (; isspace_ascii(End
[-1]) != 0 && End
> Start
; --End
);
398 bool pkgTagSection::Find(StringView Tag
,const char *&Start
,
399 const char *&End
) const
402 return Find(Tag
, Pos
) && FindInternal(Pos
, Start
, End
);
404 bool pkgTagSection::Find(Key key
,const char *&Start
,
405 const char *&End
) const
408 return Find(key
, Pos
) && FindInternal(Pos
, Start
, End
);
411 // TagSection::FindS - Find a string /*{{{*/
412 StringView
pkgTagSection::Find(StringView Tag
) const
416 if (Find(Tag
,Start
,End
) == false)
418 return StringView(Start
, End
- Start
);
420 StringView
pkgTagSection::Find(Key key
) const
424 if (Find(key
,Start
,End
) == false)
426 return StringView(Start
, End
- Start
);
429 // TagSection::FindRawS - Find a string /*{{{*/
430 StringView
pkgTagSection::FindRawInternal(unsigned int Pos
) const
432 char const *Start
= (char const *) memchr(Section
+ d
->Tags
[Pos
].EndTag
, ':', d
->Tags
[Pos
].StartValue
- d
->Tags
[Pos
].EndTag
);
434 char const *End
= Section
+ d
->Tags
[Pos
+ 1].StartTag
;
435 if (unlikely(Start
> End
))
438 for (; isspace_ascii(End
[-1]) != 0 && End
> Start
; --End
);
440 return StringView(Start
, End
- Start
);
442 StringView
pkgTagSection::FindRaw(StringView Tag
) const
445 return Find(Tag
, Pos
) ? FindRawInternal(Pos
) : "";
447 StringView
pkgTagSection::FindRaw(Key key
) const
450 return Find(key
, Pos
) ? FindRawInternal(Pos
) : "";
453 // TagSection::FindI - Find an integer /*{{{*/
454 // ---------------------------------------------------------------------
456 signed int pkgTagSection::FindIInternal(unsigned int Pos
,signed long Default
) const
460 if (FindInternal(Pos
,Start
,Stop
) == false)
463 // Copy it into a temp buffer so we can use strtol
465 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
467 strncpy(S
,Start
,Stop
-Start
);
472 signed long Result
= strtol(S
,&End
,10);
473 if (errno
== ERANGE
||
474 Result
< std::numeric_limits
<int>::min() || Result
> std::numeric_limits
<int>::max()) {
476 _error
->Error(_("Cannot convert %s to integer: out of range"), S
);
482 signed int pkgTagSection::FindI(Key key
,signed long Default
) const
486 return Find(key
, Pos
) ? FindIInternal(Pos
) : Default
;
488 signed int pkgTagSection::FindI(StringView Tag
,signed long Default
) const
492 return Find(Tag
, Pos
) ? FindIInternal(Pos
, Default
) : Default
;
495 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
496 // ---------------------------------------------------------------------
498 unsigned long long pkgTagSection::FindULLInternal(unsigned int Pos
, unsigned long long const &Default
) const
502 if (FindInternal(Pos
,Start
,Stop
) == false)
505 // Copy it into a temp buffer so we can use strtoull
507 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
509 strncpy(S
,Start
,Stop
-Start
);
513 unsigned long long Result
= strtoull(S
,&End
,10);
518 unsigned long long pkgTagSection::FindULL(Key key
, unsigned long long const &Default
) const
522 return Find(key
, Pos
) ? FindULLInternal(Pos
, Default
) : Default
;
524 unsigned long long pkgTagSection::FindULL(StringView Tag
, unsigned long long const &Default
) const
528 return Find(Tag
, Pos
) ? FindULLInternal(Pos
, Default
) : Default
;
531 // TagSection::FindB - Find boolean value /*{{{*/
532 // ---------------------------------------------------------------------
534 bool pkgTagSection::FindBInternal(unsigned int Pos
, bool Default
) const
536 const char *Start
, *Stop
;
537 if (FindInternal(Pos
, Start
, Stop
) == false)
539 return StringToBool(string(Start
, Stop
));
541 bool pkgTagSection::FindB(Key key
, bool Default
) const
544 return Find(key
, Pos
) ? FindBInternal(Pos
, Default
): Default
;
546 bool pkgTagSection::FindB(StringView Tag
, bool Default
) const
549 return Find(Tag
, Pos
) ? FindBInternal(Pos
, Default
) : Default
;
552 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
553 // ---------------------------------------------------------------------
554 /* The bits marked in Flag are masked on/off in Flags */
555 bool pkgTagSection::FindFlagInternal(unsigned int Pos
, uint8_t &Flags
,
556 uint8_t const Flag
) const
560 if (FindInternal(Pos
,Start
,Stop
) == false)
562 return FindFlag(Flags
, Flag
, Start
, Stop
);
564 bool pkgTagSection::FindFlag(Key key
, uint8_t &Flags
,
565 uint8_t const Flag
) const
568 if (Find(key
,Pos
) == false)
570 return FindFlagInternal(Pos
, Flags
, Flag
);
572 bool pkgTagSection::FindFlag(StringView Tag
, uint8_t &Flags
,
573 uint8_t const Flag
) const
576 if (Find(Tag
,Pos
) == false)
578 return FindFlagInternal(Pos
, Flags
, Flag
);
580 bool pkgTagSection::FindFlag(uint8_t &Flags
, uint8_t const Flag
,
581 char const* const Start
, char const* const Stop
)
583 switch (StringToBool(string(Start
, Stop
)))
594 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
599 bool pkgTagSection::FindFlagInternal(unsigned int Pos
,unsigned long &Flags
,
600 unsigned long Flag
) const
604 if (FindInternal(Pos
,Start
,Stop
) == false)
606 return FindFlag(Flags
, Flag
, Start
, Stop
);
608 bool pkgTagSection::FindFlag(Key key
,unsigned long &Flags
,
609 unsigned long Flag
) const
612 return Find(key
, Pos
) ? FindFlagInternal(Pos
, Flags
, Flag
) : true;
614 bool pkgTagSection::FindFlag(StringView Tag
,unsigned long &Flags
,
615 unsigned long Flag
) const
618 return Find(Tag
, Pos
) ? FindFlagInternal(Pos
, Flags
, Flag
) : true;
620 bool pkgTagSection::FindFlag(unsigned long &Flags
, unsigned long Flag
,
621 char const* Start
, char const* Stop
)
623 switch (StringToBool(string(Start
, Stop
)))
634 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
640 void pkgTagSection::Get(const char *&Start
,const char *&Stop
,unsigned int I
) const/*{{{*/
642 Start
= Section
+ d
->Tags
[I
].StartTag
;
643 Stop
= Section
+ d
->Tags
[I
+1].StartTag
;
646 APT_PURE
unsigned int pkgTagSection::Count() const { /*{{{*/
647 if (d
->Tags
.empty() == true)
649 // the last element is just marking the end and isn't a real one
650 return d
->Tags
.size() - 1;
653 // TagSection::Write - Ordered (re)writing of fields /*{{{*/
654 pkgTagSection::Tag
pkgTagSection::Tag::Remove(std::string
const &Name
)
656 return Tag(REMOVE
, Name
, "");
658 pkgTagSection::Tag
pkgTagSection::Tag::Rename(std::string
const &OldName
, std::string
const &NewName
)
660 return Tag(RENAME
, OldName
, NewName
);
662 pkgTagSection::Tag
pkgTagSection::Tag::Rewrite(std::string
const &Name
, std::string
const &Data
)
664 if (Data
.empty() == true)
665 return Tag(REMOVE
, Name
, "");
667 return Tag(REWRITE
, Name
, Data
);
669 static bool WriteTag(FileFd
&File
, std::string Tag
, StringView Value
)
671 if (Value
.empty() || isspace_ascii(Value
[0]) != 0)
675 Tag
.append(Value
.data(), Value
.length());
677 return File
.Write(Tag
.c_str(), Tag
.length());
679 static bool RewriteTags(FileFd
&File
, pkgTagSection
const * const This
, char const * const Tag
,
680 std::vector
<pkgTagSection::Tag
>::const_iterator
&R
,
681 std::vector
<pkgTagSection::Tag
>::const_iterator
const &REnd
)
683 size_t const TagLen
= strlen(Tag
);
684 for (; R
!= REnd
; ++R
)
687 if (R
->Name
.length() == TagLen
&& strncasecmp(R
->Name
.c_str(), Tag
, R
->Name
.length()) == 0)
689 if (R
->Action
!= pkgTagSection::Tag::REWRITE
)
693 else if(R
->Action
== pkgTagSection::Tag::RENAME
&& R
->Data
.length() == TagLen
&&
694 strncasecmp(R
->Data
.c_str(), Tag
, R
->Data
.length()) == 0)
695 data
= This
->FindRaw(R
->Name
.c_str()).to_string();
699 return WriteTag(File
, Tag
, data
);
703 bool pkgTagSection::Write(FileFd
&File
, char const * const * const Order
, std::vector
<Tag
> const &Rewrite
) const
705 // first pass: Write everything we have an order for
708 for (unsigned int I
= 0; Order
[I
] != 0; ++I
)
710 std::vector
<Tag
>::const_iterator R
= Rewrite
.begin();
711 if (RewriteTags(File
, this, Order
[I
], R
, Rewrite
.end()) == false)
713 if (R
!= Rewrite
.end())
716 if (Exists(Order
[I
]) == false)
719 if (WriteTag(File
, Order
[I
], FindRaw(Order
[I
])) == false)
723 // second pass: See if we have tags which aren't ordered
724 if (d
->Tags
.empty() == false)
726 for (std::vector
<pkgTagSectionPrivate::TagData
>::const_iterator T
= d
->Tags
.begin(); T
!= d
->Tags
.end() - 1; ++T
)
728 char const * const fieldname
= Section
+ T
->StartTag
;
729 size_t fieldnamelen
= T
->EndTag
- T
->StartTag
;
733 for (; Order
[I
] != 0; ++I
)
735 if (fieldnamelen
== strlen(Order
[I
]) && strncasecmp(fieldname
, Order
[I
], fieldnamelen
) == 0)
742 std::string
const name(fieldname
, fieldnamelen
);
743 std::vector
<Tag
>::const_iterator R
= Rewrite
.begin();
744 if (RewriteTags(File
, this, name
.c_str(), R
, Rewrite
.end()) == false)
746 if (R
!= Rewrite
.end())
749 if (WriteTag(File
, name
, FindRaw(name
)) == false)
753 // last pass: see if there are any rewrites remaining we haven't done yet
754 for (std::vector
<Tag
>::const_iterator R
= Rewrite
.begin(); R
!= Rewrite
.end(); ++R
)
756 if (R
->Action
== Tag::REMOVE
)
758 std::string
const name
= ((R
->Action
== Tag::RENAME
) ? R
->Data
: R
->Name
);
759 if (Exists(name
.c_str()))
764 for (; Order
[I
] != 0; ++I
)
766 if (strncasecmp(name
.c_str(), Order
[I
], name
.length()) == 0 && name
.length() == strlen(Order
[I
]))
773 if (WriteTag(File
, name
, ((R
->Action
== Tag::RENAME
) ? FindRaw(R
->Name
) : R
->Data
)) == false)
780 #include "tagfile-order.c"
782 // TFRewrite - Rewrite a control record /*{{{*/
783 // ---------------------------------------------------------------------
784 /* This writes the control record to stdout rewriting it as necessary. The
785 override map item specificies the rewriting rules to follow. This also
786 takes the time to sort the feild list. */
787 APT_IGNORE_DEPRECATED_PUSH
788 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
789 TFRewriteData
*Rewrite
)
791 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
792 for (unsigned I
= 0; I
!= 256; I
++)
795 // Set new tag up as necessary.
796 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
798 if (Rewrite
[J
].NewTag
== 0)
799 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
802 // Write all all of the tags, in order.
805 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
807 bool Rewritten
= false;
809 // See if this is a field that needs to be rewritten
810 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
812 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
815 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
817 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
818 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
820 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
827 // See if it is in the fragment
829 if (Tags
.Find(StringView(Order
[I
]),Pos
) == false)
833 if (Rewritten
== true)
836 /* Write out this element, taking a moment to rewrite the tag
837 in case of changes of case. */
840 Tags
.Get(Start
,Stop
,Pos
);
842 if (fputs(Order
[I
],Output
) < 0)
843 return _error
->Errno("fputs","IO Error to output");
844 Start
+= strlen(Order
[I
]);
845 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
846 return _error
->Errno("fwrite","IO Error to output");
847 if (Stop
[-1] != '\n')
848 fprintf(Output
,"\n");
852 // Now write all the old tags that were missed.
853 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
855 if ((Visited
[I
] & 1) == 1)
860 Tags
.Get(Start
,Stop
,I
);
861 const char *End
= Start
;
862 for (; End
< Stop
&& *End
!= ':'; End
++);
864 // See if this is a field that needs to be rewritten
865 bool Rewritten
= false;
866 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
868 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
871 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
873 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
874 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
876 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
884 if (Rewritten
== true)
887 // Write out this element
888 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
889 return _error
->Errno("fwrite","IO Error to output");
890 if (Stop
[-1] != '\n')
891 fprintf(Output
,"\n");
894 // Now write all the rewrites that were missed
895 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
897 if ((Visited
[J
] & 2) == 2)
900 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
902 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
903 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
905 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
911 APT_IGNORE_DEPRECATED_POP
914 pkgTagSection::~pkgTagSection() { delete d
; }