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 == End
|| Stop
[1] != ' '))
281 lastTagData
.StartValue
= Stop
- Section
;
284 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
291 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++)
295 // Double newline marks the end of the record
296 if (Stop
+1 < End
&& Stop
[1] == '\n')
298 if (lastTagData
.EndTag
!= 0)
300 if (lastTagKey
!= Key::Unknown
) {
301 AlphaIndexes
[static_cast<size_t>(lastTagKey
)] = TagCount
;
303 if (BetaIndexes
[lastTagHash
] != 0)
304 lastTagData
.NextInBucket
= BetaIndexes
[lastTagHash
];
305 APT_IGNORE_DEPRECATED(BetaIndexes
[lastTagHash
] = TagCount
;)
307 d
->Tags
.push_back(lastTagData
);
313 pkgTagSectionPrivate::TagData
const td(Stop
- Section
);
314 d
->Tags
.push_back(td
);
315 TrimRecord(false,End
,SupportComments
);
325 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
326 // ---------------------------------------------------------------------
327 /* There should be exactly 2 newline at the end of the record, no more. */
328 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
, bool SupportComments
)
330 if (BeforeRecord
== false)
331 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
332 if (SupportComments
&& Stop
< End
&& Stop
[0] == '#') {
333 Stop
= (const char*) memchr(Stop
,'\n',End
-Stop
) ?: End
;
338 // TagSection::Exists - return True if a tag exists /*{{{*/
339 bool pkgTagSection::Exists(StringView Tag
) const
342 return Find(Tag
, tmp
);
345 // TagSection::Find - Locate a tag /*{{{*/
346 // ---------------------------------------------------------------------
347 /* This searches the section for a tag that matches the given string. */
348 bool pkgTagSection::Find(Key key
,unsigned int &Pos
) const
350 auto Bucket
= AlphaIndexes
[static_cast<size_t>(key
)];
354 bool pkgTagSection::Find(StringView TagView
,unsigned int &Pos
) const
356 const char * const Tag
= TagView
.data();
357 size_t const Length
= TagView
.length();
358 auto key
= pkgTagHash(Tag
, Length
);
359 if (key
!= Key::Unknown
)
360 return Find(key
, Pos
);
362 unsigned int Bucket
= BetaIndexes
[BetaHash(Tag
, Length
)];
366 for (; Bucket
!= 0; Bucket
= d
->Tags
[Bucket
- 1].NextInBucket
)
368 if ((d
->Tags
[Bucket
- 1].EndTag
- d
->Tags
[Bucket
- 1].StartTag
) != Length
)
371 char const * const St
= Section
+ d
->Tags
[Bucket
- 1].StartTag
;
372 if (strncasecmp(Tag
,St
,Length
) != 0)
383 bool pkgTagSection::FindInternal(unsigned int Pos
, const char *&Start
,
384 const char *&End
) const
386 Start
= Section
+ d
->Tags
[Pos
].StartValue
;
387 // Strip off the gunk from the end
388 End
= Section
+ d
->Tags
[Pos
+ 1].StartTag
;
389 if (unlikely(Start
> End
))
390 return _error
->Error("Internal parsing error");
392 for (; End
> Start
&& isspace_ascii(End
[-1]) != 0; --End
);
396 bool pkgTagSection::Find(StringView Tag
,const char *&Start
,
397 const char *&End
) const
400 return Find(Tag
, Pos
) && FindInternal(Pos
, Start
, End
);
402 bool pkgTagSection::Find(Key key
,const char *&Start
,
403 const char *&End
) const
406 return Find(key
, Pos
) && FindInternal(Pos
, Start
, End
);
409 // TagSection::FindS - Find a string /*{{{*/
410 StringView
pkgTagSection::Find(StringView Tag
) const
414 if (Find(Tag
,Start
,End
) == false)
416 return StringView(Start
, End
- Start
);
418 StringView
pkgTagSection::Find(Key key
) const
422 if (Find(key
,Start
,End
) == false)
424 return StringView(Start
, End
- Start
);
427 // TagSection::FindRawS - Find a string /*{{{*/
428 StringView
pkgTagSection::FindRawInternal(unsigned int Pos
) const
430 char const *Start
= (char const *) memchr(Section
+ d
->Tags
[Pos
].EndTag
, ':', d
->Tags
[Pos
].StartValue
- d
->Tags
[Pos
].EndTag
);
432 char const *End
= Section
+ d
->Tags
[Pos
+ 1].StartTag
;
433 if (unlikely(Start
> End
))
436 for (; isspace_ascii(End
[-1]) != 0 && End
> Start
; --End
);
438 return StringView(Start
, End
- Start
);
440 StringView
pkgTagSection::FindRaw(StringView Tag
) const
443 return Find(Tag
, Pos
) ? FindRawInternal(Pos
) : "";
445 StringView
pkgTagSection::FindRaw(Key key
) const
448 return Find(key
, Pos
) ? FindRawInternal(Pos
) : "";
451 // TagSection::FindI - Find an integer /*{{{*/
452 // ---------------------------------------------------------------------
454 signed int pkgTagSection::FindIInternal(unsigned int Pos
,signed long Default
) const
458 if (FindInternal(Pos
,Start
,Stop
) == false)
461 // Copy it into a temp buffer so we can use strtol
463 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
465 strncpy(S
,Start
,Stop
-Start
);
470 signed long Result
= strtol(S
,&End
,10);
471 if (errno
== ERANGE
||
472 Result
< std::numeric_limits
<int>::min() || Result
> std::numeric_limits
<int>::max()) {
474 _error
->Error(_("Cannot convert %s to integer: out of range"), S
);
480 signed int pkgTagSection::FindI(Key key
,signed long Default
) const
484 return Find(key
, Pos
) ? FindIInternal(Pos
) : Default
;
486 signed int pkgTagSection::FindI(StringView Tag
,signed long Default
) const
490 return Find(Tag
, Pos
) ? FindIInternal(Pos
, Default
) : Default
;
493 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
494 // ---------------------------------------------------------------------
496 unsigned long long pkgTagSection::FindULLInternal(unsigned int Pos
, unsigned long long const &Default
) const
500 if (FindInternal(Pos
,Start
,Stop
) == false)
503 // Copy it into a temp buffer so we can use strtoull
505 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
507 strncpy(S
,Start
,Stop
-Start
);
511 unsigned long long Result
= strtoull(S
,&End
,10);
516 unsigned long long pkgTagSection::FindULL(Key key
, unsigned long long const &Default
) const
520 return Find(key
, Pos
) ? FindULLInternal(Pos
, Default
) : Default
;
522 unsigned long long pkgTagSection::FindULL(StringView Tag
, unsigned long long const &Default
) const
526 return Find(Tag
, Pos
) ? FindULLInternal(Pos
, Default
) : Default
;
529 // TagSection::FindB - Find boolean value /*{{{*/
530 // ---------------------------------------------------------------------
532 bool pkgTagSection::FindBInternal(unsigned int Pos
, bool Default
) const
534 const char *Start
, *Stop
;
535 if (FindInternal(Pos
, Start
, Stop
) == false)
537 return StringToBool(string(Start
, Stop
));
539 bool pkgTagSection::FindB(Key key
, bool Default
) const
542 return Find(key
, Pos
) ? FindBInternal(Pos
, Default
): Default
;
544 bool pkgTagSection::FindB(StringView Tag
, bool Default
) const
547 return Find(Tag
, Pos
) ? FindBInternal(Pos
, Default
) : Default
;
550 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
551 // ---------------------------------------------------------------------
552 /* The bits marked in Flag are masked on/off in Flags */
553 bool pkgTagSection::FindFlagInternal(unsigned int Pos
, uint8_t &Flags
,
554 uint8_t const Flag
) const
558 if (FindInternal(Pos
,Start
,Stop
) == false)
560 return FindFlag(Flags
, Flag
, Start
, Stop
);
562 bool pkgTagSection::FindFlag(Key key
, uint8_t &Flags
,
563 uint8_t const Flag
) const
566 if (Find(key
,Pos
) == false)
568 return FindFlagInternal(Pos
, Flags
, Flag
);
570 bool pkgTagSection::FindFlag(StringView Tag
, uint8_t &Flags
,
571 uint8_t const Flag
) const
574 if (Find(Tag
,Pos
) == false)
576 return FindFlagInternal(Pos
, Flags
, Flag
);
578 bool pkgTagSection::FindFlag(uint8_t &Flags
, uint8_t const Flag
,
579 char const* const Start
, char const* const Stop
)
581 switch (StringToBool(string(Start
, Stop
)))
592 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
597 bool pkgTagSection::FindFlagInternal(unsigned int Pos
,unsigned long &Flags
,
598 unsigned long Flag
) const
602 if (FindInternal(Pos
,Start
,Stop
) == false)
604 return FindFlag(Flags
, Flag
, Start
, Stop
);
606 bool pkgTagSection::FindFlag(Key key
,unsigned long &Flags
,
607 unsigned long Flag
) const
610 return Find(key
, Pos
) ? FindFlagInternal(Pos
, Flags
, Flag
) : true;
612 bool pkgTagSection::FindFlag(StringView Tag
,unsigned long &Flags
,
613 unsigned long Flag
) const
616 return Find(Tag
, Pos
) ? FindFlagInternal(Pos
, Flags
, Flag
) : true;
618 bool pkgTagSection::FindFlag(unsigned long &Flags
, unsigned long Flag
,
619 char const* Start
, char const* Stop
)
621 switch (StringToBool(string(Start
, Stop
)))
632 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
638 void pkgTagSection::Get(const char *&Start
,const char *&Stop
,unsigned int I
) const/*{{{*/
640 Start
= Section
+ d
->Tags
[I
].StartTag
;
641 Stop
= Section
+ d
->Tags
[I
+1].StartTag
;
644 APT_PURE
unsigned int pkgTagSection::Count() const { /*{{{*/
645 if (d
->Tags
.empty() == true)
647 // the last element is just marking the end and isn't a real one
648 return d
->Tags
.size() - 1;
651 // TagSection::Write - Ordered (re)writing of fields /*{{{*/
652 pkgTagSection::Tag
pkgTagSection::Tag::Remove(std::string
const &Name
)
654 return Tag(REMOVE
, Name
, "");
656 pkgTagSection::Tag
pkgTagSection::Tag::Rename(std::string
const &OldName
, std::string
const &NewName
)
658 return Tag(RENAME
, OldName
, NewName
);
660 pkgTagSection::Tag
pkgTagSection::Tag::Rewrite(std::string
const &Name
, std::string
const &Data
)
662 if (Data
.empty() == true)
663 return Tag(REMOVE
, Name
, "");
665 return Tag(REWRITE
, Name
, Data
);
667 static bool WriteTag(FileFd
&File
, std::string Tag
, StringView Value
)
669 if (Value
.empty() || isspace_ascii(Value
[0]) != 0)
673 Tag
.append(Value
.data(), Value
.length());
675 return File
.Write(Tag
.c_str(), Tag
.length());
677 static bool RewriteTags(FileFd
&File
, pkgTagSection
const * const This
, char const * const Tag
,
678 std::vector
<pkgTagSection::Tag
>::const_iterator
&R
,
679 std::vector
<pkgTagSection::Tag
>::const_iterator
const &REnd
)
681 size_t const TagLen
= strlen(Tag
);
682 for (; R
!= REnd
; ++R
)
685 if (R
->Name
.length() == TagLen
&& strncasecmp(R
->Name
.c_str(), Tag
, R
->Name
.length()) == 0)
687 if (R
->Action
!= pkgTagSection::Tag::REWRITE
)
691 else if(R
->Action
== pkgTagSection::Tag::RENAME
&& R
->Data
.length() == TagLen
&&
692 strncasecmp(R
->Data
.c_str(), Tag
, R
->Data
.length()) == 0)
693 data
= This
->FindRaw(R
->Name
.c_str()).to_string();
697 return WriteTag(File
, Tag
, data
);
701 bool pkgTagSection::Write(FileFd
&File
, char const * const * const Order
, std::vector
<Tag
> const &Rewrite
) const
703 // first pass: Write everything we have an order for
706 for (unsigned int I
= 0; Order
[I
] != 0; ++I
)
708 std::vector
<Tag
>::const_iterator R
= Rewrite
.begin();
709 if (RewriteTags(File
, this, Order
[I
], R
, Rewrite
.end()) == false)
711 if (R
!= Rewrite
.end())
714 if (Exists(Order
[I
]) == false)
717 if (WriteTag(File
, Order
[I
], FindRaw(Order
[I
])) == false)
721 // second pass: See if we have tags which aren't ordered
722 if (d
->Tags
.empty() == false)
724 for (std::vector
<pkgTagSectionPrivate::TagData
>::const_iterator T
= d
->Tags
.begin(); T
!= d
->Tags
.end() - 1; ++T
)
726 char const * const fieldname
= Section
+ T
->StartTag
;
727 size_t fieldnamelen
= T
->EndTag
- T
->StartTag
;
731 for (; Order
[I
] != 0; ++I
)
733 if (fieldnamelen
== strlen(Order
[I
]) && strncasecmp(fieldname
, Order
[I
], fieldnamelen
) == 0)
740 std::string
const name(fieldname
, fieldnamelen
);
741 std::vector
<Tag
>::const_iterator R
= Rewrite
.begin();
742 if (RewriteTags(File
, this, name
.c_str(), R
, Rewrite
.end()) == false)
744 if (R
!= Rewrite
.end())
747 if (WriteTag(File
, name
, FindRaw(name
)) == false)
751 // last pass: see if there are any rewrites remaining we haven't done yet
752 for (std::vector
<Tag
>::const_iterator R
= Rewrite
.begin(); R
!= Rewrite
.end(); ++R
)
754 if (R
->Action
== Tag::REMOVE
)
756 std::string
const name
= ((R
->Action
== Tag::RENAME
) ? R
->Data
: R
->Name
);
757 if (Exists(name
.c_str()))
762 for (; Order
[I
] != 0; ++I
)
764 if (strncasecmp(name
.c_str(), Order
[I
], name
.length()) == 0 && name
.length() == strlen(Order
[I
]))
771 if (WriteTag(File
, name
, ((R
->Action
== Tag::RENAME
) ? FindRaw(R
->Name
) : R
->Data
)) == false)
778 #include "tagfile-order.c"
780 // TFRewrite - Rewrite a control record /*{{{*/
781 // ---------------------------------------------------------------------
782 /* This writes the control record to stdout rewriting it as necessary. The
783 override map item specificies the rewriting rules to follow. This also
784 takes the time to sort the feild list. */
785 APT_IGNORE_DEPRECATED_PUSH
786 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
787 TFRewriteData
*Rewrite
)
789 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
790 for (unsigned I
= 0; I
!= 256; I
++)
793 // Set new tag up as necessary.
794 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
796 if (Rewrite
[J
].NewTag
== 0)
797 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
800 // Write all all of the tags, in order.
803 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
805 bool Rewritten
= false;
807 // See if this is a field that needs to be rewritten
808 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
810 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
813 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
815 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
816 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
818 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
825 // See if it is in the fragment
827 if (Tags
.Find(StringView(Order
[I
]),Pos
) == false)
831 if (Rewritten
== true)
834 /* Write out this element, taking a moment to rewrite the tag
835 in case of changes of case. */
838 Tags
.Get(Start
,Stop
,Pos
);
840 if (fputs(Order
[I
],Output
) < 0)
841 return _error
->Errno("fputs","IO Error to output");
842 Start
+= strlen(Order
[I
]);
843 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
844 return _error
->Errno("fwrite","IO Error to output");
845 if (Stop
[-1] != '\n')
846 fprintf(Output
,"\n");
850 // Now write all the old tags that were missed.
851 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
853 if ((Visited
[I
] & 1) == 1)
858 Tags
.Get(Start
,Stop
,I
);
859 const char *End
= Start
;
860 for (; End
< Stop
&& *End
!= ':'; End
++);
862 // See if this is a field that needs to be rewritten
863 bool Rewritten
= false;
864 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
866 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
869 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
871 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
872 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
874 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
882 if (Rewritten
== true)
885 // Write out this element
886 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
887 return _error
->Errno("fwrite","IO Error to output");
888 if (Stop
[-1] != '\n')
889 fprintf(Output
,"\n");
892 // Now write all the rewrites that were missed
893 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
895 if ((Visited
[J
] & 2) == 2)
898 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
900 if (isspace_ascii(Rewrite
[J
].Rewrite
[0]))
901 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
903 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
909 APT_IGNORE_DEPRECATED_POP
912 pkgTagSection::~pkgTagSection() { delete d
; }