]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/tagfile.cc
Don't download "optional" files not in Release :/.
[apt.git] / apt-pkg / tagfile.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: tagfile.cc,v 1.37.2.2 2003/12/31 16:02:30 mdz Exp $
4/* ######################################################################
5
6 Fast scanner for RFC-822 type header information
7
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.
10
11 ##################################################################### */
12 /*}}}*/
13// Include Files /*{{{*/
14#include<config.h>
15
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>
23
24#include <list>
25
26#include <string>
27#include <stdio.h>
28#include <ctype.h>
29#include <stdlib.h>
30#include <string.h>
31
32#include <apti18n.h>
33 /*}}}*/
34
35using std::string;
36using APT::StringView;
37
38class APT_HIDDEN pkgTagFilePrivate /*{{{*/
39{
40public:
41 void Reset(FileFd * const pFd, pkgTagFile::Flags const pFlags)
42 {
43 if (Map != NULL)
44 delete Map;
45 Map = NULL;
46 Buffer = NULL;
47 Fd = pFd;
48 Flags = pFlags;
49 Start = NULL;
50 End = NULL;
51 Done = false;
52 iOffset = 0;
53 }
54
55 pkgTagFilePrivate(FileFd * const pFd, pkgTagFile::Flags const pFlags) : Map(NULL)
56 {
57 Reset(pFd, pFlags);
58 }
59 FileFd * Fd;
60 pkgTagFile::Flags Flags;
61 char *Buffer;
62 char *Start;
63 char *End;
64 bool Done;
65 unsigned long long iOffset;
66 MMap *Map;
67
68 ~pkgTagFilePrivate()
69 {
70 if (Map != NULL)
71 delete Map;
72 }
73};
74 /*}}}*/
75class APT_HIDDEN pkgTagSectionPrivate /*{{{*/
76{
77public:
78 pkgTagSectionPrivate()
79 {
80 }
81 struct TagData {
82 unsigned int StartTag;
83 unsigned int EndTag;
84 unsigned int StartValue;
85 unsigned int NextInBucket;
86
87 explicit TagData(unsigned int const StartTag) : StartTag(StartTag), EndTag(0), StartValue(0), NextInBucket(0) {}
88 };
89 std::vector<TagData> Tags;
90};
91 /*}}}*/
92
93static unsigned long BetaHash(const char *Text, size_t Length) /*{{{*/
94{
95 /* This very simple hash function for the last 8 letters gives
96 very good performance on the debian package files */
97 if (Length > 8)
98 {
99 Text += (Length - 8);
100 Length = 8;
101 }
102 unsigned long Res = 0;
103 for (size_t i = 0; i < Length; ++i)
104 Res = ((unsigned long)(Text[i]) & 0xDF) ^ (Res << 1);
105 return Res & 0x7F;
106}
107 /*}}}*/
108
109// TagFile::pkgTagFile - Constructor /*{{{*/
110pkgTagFile::pkgTagFile(FileFd * const pFd,pkgTagFile::Flags const pFlags)
111 : d(new pkgTagFilePrivate(pFd, pFlags))
112{
113 Init(pFd, pFlags);
114}
115pkgTagFile::pkgTagFile(FileFd * const pFd)
116 : pkgTagFile(pFd, pkgTagFile::STRICT)
117{
118}
119void pkgTagFile::Init(FileFd * const pFd, pkgTagFile::Flags const pFlags)
120{
121 d->Reset(pFd, pFlags);
122
123 if (d->Fd->IsOpen() == false || d->Fd->Size() == 0)
124 _error->Discard();
125 else {
126 d->Map = new MMap(*d->Fd, MMap::ReadOnly);
127 d->Buffer = static_cast<char *>(d->Map->Data());
128 }
129
130 if (d->Buffer == NULL)
131 d->Done = true;
132 else {
133 d->Done = false;
134 d->End = d->Buffer + d->Map->Size();
135 }
136
137 d->Start = d->Buffer;
138}
139void pkgTagFile::Init(FileFd * const pFd)
140{
141 Init(pFd, pkgTagFile::STRICT);
142}
143 /*}}}*/
144// TagFile::~pkgTagFile - Destructor /*{{{*/
145pkgTagFile::~pkgTagFile()
146{
147 delete d;
148}
149 /*}}}*/
150// TagFile::Offset - Return the current offset in the buffer /*{{{*/
151APT_PURE unsigned long pkgTagFile::Offset()
152{
153 return d->iOffset;
154}
155 /*}}}*/
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.
161 */
162bool pkgTagFile::Step(pkgTagSection &Tag)
163{
164 if(Tag.Scan(d->Start,d->End - d->Start,(d->Flags & SUPPORT_COMMENTS) != 0) == false)
165 {
166 if (d->Start == d->End)
167 return false;
168 else
169 return _error->Warning(_("Unable to parse package file %s (%d)"),
170 d->Fd->Name().c_str(), 1);
171 }
172
173 size_t tagSize = Tag.size();
174 d->Start += tagSize;
175 d->iOffset += tagSize;
176 return true;
177}
178 /*}}}*/
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
182 that is there */
183bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
184{
185 unsigned int Size(d->Map->Size());
186 if (Offset >= Size)
187 return false;
188
189 // Reposition and reload..
190 d->iOffset = Offset;
191 d->Done = false;
192 d->Start = d->Buffer + d->iOffset;
193
194 return Step(Tag);
195}
196 /*}}}*/
197// pkgTagSection::pkgTagSection - Constructor /*{{{*/
198// ---------------------------------------------------------------------
199/* */
200APT_IGNORE_DEPRECATED_PUSH
201pkgTagSection::pkgTagSection()
202 : Section(0), d(new pkgTagSectionPrivate()), Stop(0)
203{
204 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
205 memset(&BetaIndexes, 0, sizeof(BetaIndexes));
206}
207APT_IGNORE_DEPRECATED_POP
208 /*}}}*/
209// TagSection::Scan - Scan for the end of the header information /*{{{*/
210bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength,bool const SupportComments)
211{
212 Section = Start;
213 const char *End = Start + MaxLength;
214
215 Stop = Section;
216 if (d->Tags.empty() == false)
217 {
218 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
219 memset(&BetaIndexes, 0, sizeof(BetaIndexes));
220 d->Tags.clear();
221 }
222 d->Tags.reserve(0x100);
223
224 unsigned int TagCount = d->Tags.size();
225
226 if (Stop == 0)
227 return false;
228
229 pkgTagSectionPrivate::TagData lastTagData(0);
230 lastTagData.EndTag = 0;
231 Key lastTagKey = Key::Unknown;
232 unsigned int lastTagHash = 0;
233 while (Stop < End)
234 {
235 TrimRecord(true,End,SupportComments);
236
237 // this can happen when TrimRecord trims away the entire Record
238 // (e.g. because it just contains comments)
239 if(Stop == End)
240 goto end;
241
242 // Start a new index and add it to the hash
243 if (isspace_ascii(Stop[0]) == 0)
244 {
245 // store the last found tag
246 if (lastTagData.EndTag != 0)
247 {
248 if (lastTagKey != Key::Unknown) {
249 AlphaIndexes[static_cast<size_t>(lastTagKey)] = TagCount;
250 } else {
251 if (BetaIndexes[lastTagHash] != 0)
252 lastTagData.NextInBucket = BetaIndexes[lastTagHash];
253 APT_IGNORE_DEPRECATED_PUSH
254 BetaIndexes[lastTagHash] = TagCount;
255 APT_IGNORE_DEPRECATED_POP
256 }
257 d->Tags.push_back(lastTagData);
258 }
259
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);
264 if (Colon == NULL)
265 return false;
266 // find the end of the tag (which might or might not be the colon)
267 char const * EndTag = Colon;
268 --EndTag;
269 for (; EndTag > Stop && isspace_ascii(*EndTag) != 0; --EndTag)
270 ;
271 ++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
277 Stop = Colon + 1;
278 for (; Stop < End && isspace_ascii(*Stop) != 0; ++Stop)
279 if (*Stop == '\n' && (Stop+1 == End || Stop[1] != ' '))
280 break;
281 lastTagData.StartValue = Stop - Section;
282 }
283
284 Stop = (const char *)memchr(Stop,'\n',End - Stop);
285
286 if (Stop == 0) {
287 Stop = End;
288 goto end;
289 }
290
291 for (; Stop+1 < End && Stop[1] == '\r'; Stop++)
292 /* nothing */
293 ;
294
295 // Double newline marks the end of the record
296 if (Stop+1 < End && Stop[1] == '\n')
297 end: {
298 if (lastTagData.EndTag != 0)
299 {
300 if (lastTagKey != Key::Unknown) {
301 AlphaIndexes[static_cast<size_t>(lastTagKey)] = TagCount;
302 } else {
303 if (BetaIndexes[lastTagHash] != 0)
304 lastTagData.NextInBucket = BetaIndexes[lastTagHash];
305 APT_IGNORE_DEPRECATED(BetaIndexes[lastTagHash] = TagCount;)
306 }
307 d->Tags.push_back(lastTagData);
308 }
309
310 if (d->Tags.empty())
311 return false;
312
313 pkgTagSectionPrivate::TagData const td(Stop - Section);
314 d->Tags.push_back(td);
315 TrimRecord(false,End,SupportComments);
316 return true;
317 }
318
319 Stop++;
320 }
321
322 goto end;
323}
324 /*}}}*/
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. */
328void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End, bool SupportComments)
329{ trim:
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;
334 goto trim;
335 }
336}
337 /*}}}*/
338// TagSection::Exists - return True if a tag exists /*{{{*/
339bool pkgTagSection::Exists(StringView Tag) const
340{
341 unsigned int tmp;
342 return Find(Tag, tmp);
343}
344 /*}}}*/
345// TagSection::Find - Locate a tag /*{{{*/
346// ---------------------------------------------------------------------
347/* This searches the section for a tag that matches the given string. */
348bool pkgTagSection::Find(Key key,unsigned int &Pos) const
349{
350 auto Bucket = AlphaIndexes[static_cast<size_t>(key)];
351 Pos = Bucket - 1;
352 return Bucket != 0;
353}
354bool pkgTagSection::Find(StringView TagView,unsigned int &Pos) const
355{
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);
361
362 unsigned int Bucket = BetaIndexes[BetaHash(Tag, Length)];
363 if (Bucket == 0)
364 return false;
365
366 for (; Bucket != 0; Bucket = d->Tags[Bucket - 1].NextInBucket)
367 {
368 if ((d->Tags[Bucket - 1].EndTag - d->Tags[Bucket - 1].StartTag) != Length)
369 continue;
370
371 char const * const St = Section + d->Tags[Bucket - 1].StartTag;
372 if (strncasecmp(Tag,St,Length) != 0)
373 continue;
374
375 Pos = Bucket - 1;
376 return true;
377 }
378
379 Pos = 0;
380 return false;
381}
382
383bool pkgTagSection::FindInternal(unsigned int Pos, const char *&Start,
384 const char *&End) const
385{
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");
391
392 for (; End > Start && isspace_ascii(End[-1]) != 0; --End);
393
394 return true;
395}
396bool pkgTagSection::Find(StringView Tag,const char *&Start,
397 const char *&End) const
398{
399 unsigned int Pos;
400 return Find(Tag, Pos) && FindInternal(Pos, Start, End);
401}
402bool pkgTagSection::Find(Key key,const char *&Start,
403 const char *&End) const
404{
405 unsigned int Pos;
406 return Find(key, Pos) && FindInternal(Pos, Start, End);
407}
408 /*}}}*/
409// TagSection::FindS - Find a string /*{{{*/
410StringView pkgTagSection::Find(StringView Tag) const
411{
412 const char *Start;
413 const char *End;
414 if (Find(Tag,Start,End) == false)
415 return StringView();
416 return StringView(Start, End - Start);
417}
418StringView pkgTagSection::Find(Key key) const
419{
420 const char *Start;
421 const char *End;
422 if (Find(key,Start,End) == false)
423 return StringView();
424 return StringView(Start, End - Start);
425}
426 /*}}}*/
427// TagSection::FindRawS - Find a string /*{{{*/
428StringView pkgTagSection::FindRawInternal(unsigned int Pos) const
429{
430 char const *Start = (char const *) memchr(Section + d->Tags[Pos].EndTag, ':', d->Tags[Pos].StartValue - d->Tags[Pos].EndTag);
431 ++Start;
432 char const *End = Section + d->Tags[Pos + 1].StartTag;
433 if (unlikely(Start > End))
434 return "";
435
436 for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
437
438 return StringView(Start, End - Start);
439}
440StringView pkgTagSection::FindRaw(StringView Tag) const
441{
442 unsigned int Pos;
443 return Find(Tag, Pos) ? FindRawInternal(Pos) : "";
444}
445StringView pkgTagSection::FindRaw(Key key) const
446{
447 unsigned int Pos;
448 return Find(key, Pos) ? FindRawInternal(Pos) : "";
449}
450 /*}}}*/
451// TagSection::FindI - Find an integer /*{{{*/
452// ---------------------------------------------------------------------
453/* */
454signed int pkgTagSection::FindIInternal(unsigned int Pos,signed long Default) const
455{
456 const char *Start;
457 const char *Stop;
458 if (FindInternal(Pos,Start,Stop) == false)
459 return Default;
460
461 // Copy it into a temp buffer so we can use strtol
462 char S[300];
463 if ((unsigned)(Stop - Start) >= sizeof(S))
464 return Default;
465 strncpy(S,Start,Stop-Start);
466 S[Stop - Start] = 0;
467
468 errno = 0;
469 char *End;
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()) {
473 errno = ERANGE;
474 _error->Error(_("Cannot convert %s to integer: out of range"), S);
475 }
476 if (S == End)
477 return Default;
478 return Result;
479}
480signed int pkgTagSection::FindI(Key key,signed long Default) const
481{
482 unsigned int Pos;
483
484 return Find(key, Pos) ? FindIInternal(Pos) : Default;
485}
486signed int pkgTagSection::FindI(StringView Tag,signed long Default) const
487{
488 unsigned int Pos;
489
490 return Find(Tag, Pos) ? FindIInternal(Pos, Default) : Default;
491}
492 /*}}}*/
493// TagSection::FindULL - Find an unsigned long long integer /*{{{*/
494// ---------------------------------------------------------------------
495/* */
496unsigned long long pkgTagSection::FindULLInternal(unsigned int Pos, unsigned long long const &Default) const
497{
498 const char *Start;
499 const char *Stop;
500 if (FindInternal(Pos,Start,Stop) == false)
501 return Default;
502
503 // Copy it into a temp buffer so we can use strtoull
504 char S[100];
505 if ((unsigned)(Stop - Start) >= sizeof(S))
506 return Default;
507 strncpy(S,Start,Stop-Start);
508 S[Stop - Start] = 0;
509
510 char *End;
511 unsigned long long Result = strtoull(S,&End,10);
512 if (S == End)
513 return Default;
514 return Result;
515}
516unsigned long long pkgTagSection::FindULL(Key key, unsigned long long const &Default) const
517{
518 unsigned int Pos;
519
520 return Find(key, Pos) ? FindULLInternal(Pos, Default) : Default;
521}
522unsigned long long pkgTagSection::FindULL(StringView Tag, unsigned long long const &Default) const
523{
524 unsigned int Pos;
525
526 return Find(Tag, Pos) ? FindULLInternal(Pos, Default) : Default;
527}
528 /*}}}*/
529// TagSection::FindB - Find boolean value /*{{{*/
530// ---------------------------------------------------------------------
531/* */
532bool pkgTagSection::FindBInternal(unsigned int Pos, bool Default) const
533{
534 const char *Start, *Stop;
535 if (FindInternal(Pos, Start, Stop) == false)
536 return Default;
537 return StringToBool(string(Start, Stop));
538}
539bool pkgTagSection::FindB(Key key, bool Default) const
540{
541 unsigned int Pos;
542 return Find(key, Pos) ? FindBInternal(Pos, Default): Default;
543}
544bool pkgTagSection::FindB(StringView Tag, bool Default) const
545{
546 unsigned int Pos;
547 return Find(Tag, Pos) ? FindBInternal(Pos, Default) : Default;
548}
549 /*}}}*/
550// TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
551// ---------------------------------------------------------------------
552/* The bits marked in Flag are masked on/off in Flags */
553bool pkgTagSection::FindFlagInternal(unsigned int Pos, uint8_t &Flags,
554 uint8_t const Flag) const
555{
556 const char *Start;
557 const char *Stop;
558 if (FindInternal(Pos,Start,Stop) == false)
559 return true;
560 return FindFlag(Flags, Flag, Start, Stop);
561}
562bool pkgTagSection::FindFlag(Key key, uint8_t &Flags,
563 uint8_t const Flag) const
564{
565 unsigned int Pos;
566 if (Find(key,Pos) == false)
567 return true;
568 return FindFlagInternal(Pos, Flags, Flag);
569}
570bool pkgTagSection::FindFlag(StringView Tag, uint8_t &Flags,
571 uint8_t const Flag) const
572{
573 unsigned int Pos;
574 if (Find(Tag,Pos) == false)
575 return true;
576 return FindFlagInternal(Pos, Flags, Flag);
577}
578bool pkgTagSection::FindFlag(uint8_t &Flags, uint8_t const Flag,
579 char const* const Start, char const* const Stop)
580{
581 switch (StringToBool(string(Start, Stop)))
582 {
583 case 0:
584 Flags &= ~Flag;
585 return true;
586
587 case 1:
588 Flags |= Flag;
589 return true;
590
591 default:
592 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
593 return true;
594 }
595 return true;
596}
597bool pkgTagSection::FindFlagInternal(unsigned int Pos,unsigned long &Flags,
598 unsigned long Flag) const
599{
600 const char *Start;
601 const char *Stop;
602 if (FindInternal(Pos,Start,Stop) == false)
603 return true;
604 return FindFlag(Flags, Flag, Start, Stop);
605}
606bool pkgTagSection::FindFlag(Key key,unsigned long &Flags,
607 unsigned long Flag) const
608{
609 unsigned int Pos;
610 return Find(key, Pos) ? FindFlagInternal(Pos, Flags, Flag) : true;
611}
612bool pkgTagSection::FindFlag(StringView Tag,unsigned long &Flags,
613 unsigned long Flag) const
614{
615 unsigned int Pos;
616 return Find(Tag, Pos) ? FindFlagInternal(Pos, Flags, Flag) : true;
617}
618bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
619 char const* Start, char const* Stop)
620{
621 switch (StringToBool(string(Start, Stop)))
622 {
623 case 0:
624 Flags &= ~Flag;
625 return true;
626
627 case 1:
628 Flags |= Flag;
629 return true;
630
631 default:
632 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
633 return true;
634 }
635 return true;
636}
637 /*}}}*/
638void pkgTagSection::Get(const char *&Start,const char *&Stop,unsigned int I) const/*{{{*/
639{
640 Start = Section + d->Tags[I].StartTag;
641 Stop = Section + d->Tags[I+1].StartTag;
642}
643 /*}}}*/
644APT_PURE unsigned int pkgTagSection::Count() const { /*{{{*/
645 if (d->Tags.empty() == true)
646 return 0;
647 // the last element is just marking the end and isn't a real one
648 return d->Tags.size() - 1;
649}
650 /*}}}*/
651// TagSection::Write - Ordered (re)writing of fields /*{{{*/
652pkgTagSection::Tag pkgTagSection::Tag::Remove(std::string const &Name)
653{
654 return Tag(REMOVE, Name, "");
655}
656pkgTagSection::Tag pkgTagSection::Tag::Rename(std::string const &OldName, std::string const &NewName)
657{
658 return Tag(RENAME, OldName, NewName);
659}
660pkgTagSection::Tag pkgTagSection::Tag::Rewrite(std::string const &Name, std::string const &Data)
661{
662 if (Data.empty() == true)
663 return Tag(REMOVE, Name, "");
664 else
665 return Tag(REWRITE, Name, Data);
666}
667static bool WriteTag(FileFd &File, std::string Tag, StringView Value)
668{
669 if (Value.empty() || isspace_ascii(Value[0]) != 0)
670 Tag.append(":");
671 else
672 Tag.append(": ");
673 Tag.append(Value.data(), Value.length());
674 Tag.append("\n");
675 return File.Write(Tag.c_str(), Tag.length());
676}
677static 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)
680{
681 size_t const TagLen = strlen(Tag);
682 for (; R != REnd; ++R)
683 {
684 std::string data;
685 if (R->Name.length() == TagLen && strncasecmp(R->Name.c_str(), Tag, R->Name.length()) == 0)
686 {
687 if (R->Action != pkgTagSection::Tag::REWRITE)
688 break;
689 data = R->Data;
690 }
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();
694 else
695 continue;
696
697 return WriteTag(File, Tag, data);
698 }
699 return true;
700}
701bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::vector<Tag> const &Rewrite) const
702{
703 // first pass: Write everything we have an order for
704 if (Order != NULL)
705 {
706 for (unsigned int I = 0; Order[I] != 0; ++I)
707 {
708 std::vector<Tag>::const_iterator R = Rewrite.begin();
709 if (RewriteTags(File, this, Order[I], R, Rewrite.end()) == false)
710 return false;
711 if (R != Rewrite.end())
712 continue;
713
714 if (Exists(Order[I]) == false)
715 continue;
716
717 if (WriteTag(File, Order[I], FindRaw(Order[I])) == false)
718 return false;
719 }
720 }
721 // second pass: See if we have tags which aren't ordered
722 if (d->Tags.empty() == false)
723 {
724 for (std::vector<pkgTagSectionPrivate::TagData>::const_iterator T = d->Tags.begin(); T != d->Tags.end() - 1; ++T)
725 {
726 char const * const fieldname = Section + T->StartTag;
727 size_t fieldnamelen = T->EndTag - T->StartTag;
728 if (Order != NULL)
729 {
730 unsigned int I = 0;
731 for (; Order[I] != 0; ++I)
732 {
733 if (fieldnamelen == strlen(Order[I]) && strncasecmp(fieldname, Order[I], fieldnamelen) == 0)
734 break;
735 }
736 if (Order[I] != 0)
737 continue;
738 }
739
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)
743 return false;
744 if (R != Rewrite.end())
745 continue;
746
747 if (WriteTag(File, name, FindRaw(name)) == false)
748 return false;
749 }
750 }
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)
753 {
754 if (R->Action == Tag::REMOVE)
755 continue;
756 std::string const name = ((R->Action == Tag::RENAME) ? R->Data : R->Name);
757 if (Exists(name.c_str()))
758 continue;
759 if (Order != NULL)
760 {
761 unsigned int I = 0;
762 for (; Order[I] != 0; ++I)
763 {
764 if (strncasecmp(name.c_str(), Order[I], name.length()) == 0 && name.length() == strlen(Order[I]))
765 break;
766 }
767 if (Order[I] != 0)
768 continue;
769 }
770
771 if (WriteTag(File, name, ((R->Action == Tag::RENAME) ? FindRaw(R->Name) : R->Data)) == false)
772 return false;
773 }
774 return true;
775}
776 /*}}}*/
777
778#include "tagfile-order.c"
779
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. */
785APT_IGNORE_DEPRECATED_PUSH
786bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
787 TFRewriteData *Rewrite)
788{
789 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
790 for (unsigned I = 0; I != 256; I++)
791 Visited[I] = 0;
792
793 // Set new tag up as necessary.
794 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
795 {
796 if (Rewrite[J].NewTag == 0)
797 Rewrite[J].NewTag = Rewrite[J].Tag;
798 }
799
800 // Write all all of the tags, in order.
801 if (Order != NULL)
802 {
803 for (unsigned int I = 0; Order[I] != 0; I++)
804 {
805 bool Rewritten = false;
806
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++)
809 {
810 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
811 {
812 Visited[J] |= 2;
813 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
814 {
815 if (isspace_ascii(Rewrite[J].Rewrite[0]))
816 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
817 else
818 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
819 }
820 Rewritten = true;
821 break;
822 }
823 }
824
825 // See if it is in the fragment
826 unsigned Pos;
827 if (Tags.Find(StringView(Order[I]),Pos) == false)
828 continue;
829 Visited[Pos] |= 1;
830
831 if (Rewritten == true)
832 continue;
833
834 /* Write out this element, taking a moment to rewrite the tag
835 in case of changes of case. */
836 const char *Start;
837 const char *Stop;
838 Tags.Get(Start,Stop,Pos);
839
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");
847 }
848 }
849
850 // Now write all the old tags that were missed.
851 for (unsigned int I = 0; I != Tags.Count(); I++)
852 {
853 if ((Visited[I] & 1) == 1)
854 continue;
855
856 const char *Start;
857 const char *Stop;
858 Tags.Get(Start,Stop,I);
859 const char *End = Start;
860 for (; End < Stop && *End != ':'; End++);
861
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++)
865 {
866 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
867 {
868 Visited[J] |= 2;
869 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
870 {
871 if (isspace_ascii(Rewrite[J].Rewrite[0]))
872 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
873 else
874 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
875 }
876
877 Rewritten = true;
878 break;
879 }
880 }
881
882 if (Rewritten == true)
883 continue;
884
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");
890 }
891
892 // Now write all the rewrites that were missed
893 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
894 {
895 if ((Visited[J] & 2) == 2)
896 continue;
897
898 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
899 {
900 if (isspace_ascii(Rewrite[J].Rewrite[0]))
901 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
902 else
903 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
904 }
905 }
906
907 return true;
908}
909APT_IGNORE_DEPRECATED_POP
910 /*}}}*/
911
912pkgTagSection::~pkgTagSection() { delete d; }