]> git.saurik.com Git - apt.git/blob - apt-pkg/tagfile.cc
support lang= and target= sources.list options
[apt.git] / apt-pkg / tagfile.cc
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/tagfile.h>
17 #include <apt-pkg/error.h>
18 #include <apt-pkg/strutl.h>
19 #include <apt-pkg/fileutl.h>
20
21 #include <string>
22 #include <stdio.h>
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <apti18n.h>
28 /*}}}*/
29
30 using std::string;
31
32 class pkgTagFilePrivate
33 {
34 public:
35 void Reset(FileFd * const pFd, unsigned long long const pSize)
36 {
37 if (Buffer != NULL)
38 free(Buffer);
39 Buffer = NULL;
40 Fd = pFd;
41 Start = NULL;
42 End = NULL;
43 Done = false;
44 iOffset = 0;
45 Size = pSize;
46 }
47
48 pkgTagFilePrivate(FileFd * const pFd, unsigned long long const Size) : Buffer(NULL)
49 {
50 Reset(pFd, Size);
51 }
52 FileFd * Fd;
53 char *Buffer;
54 char *Start;
55 char *End;
56 bool Done;
57 unsigned long long iOffset;
58 unsigned long long Size;
59
60 ~pkgTagFilePrivate()
61 {
62 if (Buffer != NULL)
63 free(Buffer);
64 }
65 };
66
67 class pkgTagSectionPrivate
68 {
69 public:
70 pkgTagSectionPrivate()
71 {
72 }
73 struct TagData {
74 unsigned int StartTag;
75 unsigned int EndTag;
76 unsigned int StartValue;
77 unsigned int NextInBucket;
78
79 explicit TagData(unsigned int const StartTag) : StartTag(StartTag), EndTag(0), StartValue(0), NextInBucket(0) {}
80 };
81 std::vector<TagData> Tags;
82 };
83
84 static unsigned long AlphaHash(const char *Text, size_t Length) /*{{{*/
85 {
86 /* This very simple hash function for the last 8 letters gives
87 very good performance on the debian package files */
88 if (Length > 8)
89 {
90 Text += (Length - 8);
91 Length = 8;
92 }
93 unsigned long Res = 0;
94 for (size_t i = 0; i < Length; ++i)
95 Res = ((unsigned long)(Text[i]) & 0xDF) ^ (Res << 1);
96 return Res & 0xFF;
97 }
98 /*}}}*/
99
100 // TagFile::pkgTagFile - Constructor /*{{{*/
101 // ---------------------------------------------------------------------
102 /* */
103 pkgTagFile::pkgTagFile(FileFd * const pFd,unsigned long long const Size)
104 : d(new pkgTagFilePrivate(pFd, Size + 4))
105 {
106 Init(pFd, Size);
107 }
108 void pkgTagFile::Init(FileFd * const pFd,unsigned long long Size)
109 {
110 /* The size is increased by 4 because if we start with the Size of the
111 filename we need to try to read 1 char more to see an EOF faster, 1
112 char the end-pointer can be on and maybe 2 newlines need to be added
113 to the end of the file -> 4 extra chars */
114 Size += 4;
115 d->Reset(pFd, Size);
116
117 if (d->Fd->IsOpen() == false)
118 d->Start = d->End = d->Buffer = 0;
119 else
120 d->Buffer = (char*)malloc(sizeof(char) * Size);
121
122 if (d->Buffer == NULL)
123 d->Done = true;
124 else
125 d->Done = false;
126
127 d->Start = d->End = d->Buffer;
128 d->iOffset = 0;
129 if (d->Done == false)
130 Fill();
131 }
132 /*}}}*/
133 // TagFile::~pkgTagFile - Destructor /*{{{*/
134 // ---------------------------------------------------------------------
135 /* */
136 pkgTagFile::~pkgTagFile()
137 {
138 delete d;
139 }
140 /*}}}*/
141 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
142 APT_PURE unsigned long pkgTagFile::Offset()
143 {
144 return d->iOffset;
145 }
146 /*}}}*/
147 // TagFile::Resize - Resize the internal buffer /*{{{*/
148 // ---------------------------------------------------------------------
149 /* Resize the internal buffer (double it in size). Fail if a maximum size
150 * size is reached.
151 */
152 bool pkgTagFile::Resize()
153 {
154 // fail is the buffer grows too big
155 if(d->Size > 1024*1024+1)
156 return false;
157
158 return Resize(d->Size * 2);
159 }
160 bool pkgTagFile::Resize(unsigned long long const newSize)
161 {
162 unsigned long long const EndSize = d->End - d->Start;
163
164 // get new buffer and use it
165 char* newBuffer = (char*)realloc(d->Buffer, sizeof(char) * newSize);
166 if (newBuffer == NULL)
167 return false;
168 d->Buffer = newBuffer;
169 d->Size = newSize;
170
171 // update the start/end pointers to the new buffer
172 d->Start = d->Buffer;
173 d->End = d->Start + EndSize;
174 return true;
175 }
176 /*}}}*/
177 // TagFile::Step - Advance to the next section /*{{{*/
178 // ---------------------------------------------------------------------
179 /* If the Section Scanner fails we refill the buffer and try again.
180 * If that fails too, double the buffer size and try again until a
181 * maximum buffer is reached.
182 */
183 bool pkgTagFile::Step(pkgTagSection &Tag)
184 {
185 if(Tag.Scan(d->Start,d->End - d->Start) == false)
186 {
187 do
188 {
189 if (Fill() == false)
190 return false;
191
192 if(Tag.Scan(d->Start,d->End - d->Start, false))
193 break;
194
195 if (Resize() == false)
196 return _error->Error(_("Unable to parse package file %s (%d)"),
197 d->Fd->Name().c_str(), 1);
198
199 } while (Tag.Scan(d->Start,d->End - d->Start, false) == false);
200 }
201
202 d->Start += Tag.size();
203 d->iOffset += Tag.size();
204
205 Tag.Trim();
206 return true;
207 }
208 /*}}}*/
209 // TagFile::Fill - Top up the buffer /*{{{*/
210 // ---------------------------------------------------------------------
211 /* This takes the bit at the end of the buffer and puts it at the start
212 then fills the rest from the file */
213 bool pkgTagFile::Fill()
214 {
215 unsigned long long EndSize = d->End - d->Start;
216 unsigned long long Actual = 0;
217
218 memmove(d->Buffer,d->Start,EndSize);
219 d->Start = d->Buffer;
220 d->End = d->Buffer + EndSize;
221
222 if (d->Done == false)
223 {
224 // See if only a bit of the file is left
225 unsigned long long const dataSize = d->Size - ((d->End - d->Buffer) + 1);
226 if (d->Fd->Read(d->End, dataSize, &Actual) == false)
227 return false;
228 if (Actual != dataSize)
229 d->Done = true;
230 d->End += Actual;
231 }
232
233 if (d->Done == true)
234 {
235 if (EndSize <= 3 && Actual == 0)
236 return false;
237 if (d->Size - (d->End - d->Buffer) < 4)
238 return true;
239
240 // Append a double new line if one does not exist
241 unsigned int LineCount = 0;
242 for (const char *E = d->End - 1; E - d->End < 6 && (*E == '\n' || *E == '\r'); E--)
243 if (*E == '\n')
244 LineCount++;
245 if (LineCount < 2)
246 {
247 if ((unsigned)(d->End - d->Buffer) >= d->Size)
248 Resize(d->Size + 3);
249 for (; LineCount < 2; LineCount++)
250 *d->End++ = '\n';
251 }
252
253 return true;
254 }
255
256 return true;
257 }
258 /*}}}*/
259 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
260 // ---------------------------------------------------------------------
261 /* This jumps to a pre-recorded file location and reads the record
262 that is there */
263 bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
264 {
265 // We are within a buffer space of the next hit..
266 if (Offset >= d->iOffset && d->iOffset + (d->End - d->Start) > Offset)
267 {
268 unsigned long long Dist = Offset - d->iOffset;
269 d->Start += Dist;
270 d->iOffset += Dist;
271 // if we have seen the end, don't ask for more
272 if (d->Done == true)
273 return Tag.Scan(d->Start, d->End - d->Start);
274 else
275 return Step(Tag);
276 }
277
278 // Reposition and reload..
279 d->iOffset = Offset;
280 d->Done = false;
281 if (d->Fd->Seek(Offset) == false)
282 return false;
283 d->End = d->Start = d->Buffer;
284
285 if (Fill() == false)
286 return false;
287
288 if (Tag.Scan(d->Start, d->End - d->Start) == true)
289 return true;
290
291 // This appends a double new line (for the real eof handling)
292 if (Fill() == false)
293 return false;
294
295 if (Tag.Scan(d->Start, d->End - d->Start, false) == false)
296 return _error->Error(_("Unable to parse package file %s (%d)"),d->Fd->Name().c_str(), 2);
297
298 return true;
299 }
300 /*}}}*/
301 // pkgTagSection::pkgTagSection - Constructor /*{{{*/
302 // ---------------------------------------------------------------------
303 /* */
304 APT_IGNORE_DEPRECATED_PUSH
305 pkgTagSection::pkgTagSection()
306 : Section(0), d(new pkgTagSectionPrivate()), Stop(0)
307 {
308 #if APT_PKG_ABI < 413
309 TagCount = 0;
310 memset(&Indexes, 0, sizeof(Indexes));
311 #endif
312 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
313 }
314 APT_IGNORE_DEPRECATED_POP
315 /*}}}*/
316 // TagSection::Scan - Scan for the end of the header information /*{{{*/
317 #if APT_PKG_ABI < 413
318 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
319 {
320 return Scan(Start, MaxLength, true);
321 }
322 #endif
323 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const Restart)
324 {
325 Section = Start;
326 const char *End = Start + MaxLength;
327
328 if (Restart == false && d->Tags.empty() == false)
329 {
330 Stop = Section + d->Tags.back().StartTag;
331 if (End <= Stop)
332 return false;
333 Stop = (const char *)memchr(Stop,'\n',End - Stop);
334 if (Stop == NULL)
335 return false;
336 ++Stop;
337 }
338 else
339 {
340 Stop = Section;
341 if (d->Tags.empty() == false)
342 {
343 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
344 d->Tags.clear();
345 }
346 d->Tags.reserve(0x100);
347 }
348 #if APT_PKG_ABI >= 413
349 unsigned int TagCount = d->Tags.size();
350 #else
351 APT_IGNORE_DEPRECATED(TagCount = d->Tags.size();)
352 #endif
353
354 if (Stop == 0)
355 return false;
356
357 pkgTagSectionPrivate::TagData lastTagData(0);
358 lastTagData.EndTag = 0;
359 unsigned long lastTagHash = 0;
360 while (Stop < End)
361 {
362 TrimRecord(true,End);
363
364 // this can happen when TrimRecord trims away the entire Record
365 // (e.g. because it just contains comments)
366 if(Stop == End)
367 return true;
368
369 // Start a new index and add it to the hash
370 if (isspace(Stop[0]) == 0)
371 {
372 // store the last found tag
373 if (lastTagData.EndTag != 0)
374 {
375 if (AlphaIndexes[lastTagHash] != 0)
376 lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
377 APT_IGNORE_DEPRECATED_PUSH
378 AlphaIndexes[lastTagHash] = TagCount;
379 #if APT_PKG_ABI < 413
380 if (d->Tags.size() < sizeof(Indexes)/sizeof(Indexes[0]))
381 Indexes[d->Tags.size()] = lastTagData.StartTag;
382 #endif
383 APT_IGNORE_DEPRECATED_POP
384 d->Tags.push_back(lastTagData);
385 }
386
387 APT_IGNORE_DEPRECATED(++TagCount;)
388 lastTagData = pkgTagSectionPrivate::TagData(Stop - Section);
389 // find the colon separating tag and value
390 char const * Colon = (char const *) memchr(Stop, ':', End - Stop);
391 if (Colon == NULL)
392 return false;
393 // find the end of the tag (which might or might not be the colon)
394 char const * EndTag = Colon;
395 --EndTag;
396 for (; EndTag > Stop && isspace(*EndTag) != 0; --EndTag)
397 ;
398 ++EndTag;
399 lastTagData.EndTag = EndTag - Section;
400 lastTagHash = AlphaHash(Stop, EndTag - Stop);
401 // find the beginning of the value
402 Stop = Colon + 1;
403 for (; isspace(*Stop) != 0; ++Stop);
404 if (Stop >= End)
405 return false;
406 lastTagData.StartValue = Stop - Section;
407 }
408
409 Stop = (const char *)memchr(Stop,'\n',End - Stop);
410
411 if (Stop == 0)
412 return false;
413
414 for (; Stop+1 < End && Stop[1] == '\r'; Stop++)
415 /* nothing */
416 ;
417
418 // Double newline marks the end of the record
419 if (Stop+1 < End && Stop[1] == '\n')
420 {
421 if (lastTagData.EndTag != 0)
422 {
423 if (AlphaIndexes[lastTagHash] != 0)
424 lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
425 APT_IGNORE_DEPRECATED(AlphaIndexes[lastTagHash] = TagCount;)
426 #if APT_PKG_ABI < 413
427 APT_IGNORE_DEPRECATED(Indexes[d->Tags.size()] = lastTagData.StartTag;)
428 #endif
429 d->Tags.push_back(lastTagData);
430 }
431
432 pkgTagSectionPrivate::TagData const td(Stop - Section);
433 #if APT_PKG_ABI < 413
434 APT_IGNORE_DEPRECATED(Indexes[d->Tags.size()] = td.StartTag;)
435 #endif
436 d->Tags.push_back(td);
437 TrimRecord(false,End);
438 return true;
439 }
440
441 Stop++;
442 }
443
444 return false;
445 }
446 /*}}}*/
447 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
448 // ---------------------------------------------------------------------
449 /* There should be exactly 2 newline at the end of the record, no more. */
450 void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
451 {
452 if (BeforeRecord == true)
453 return;
454 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
455 }
456 /*}}}*/
457 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
458 // ---------------------------------------------------------------------
459 /* There should be exactly 1 newline at the end of the buffer, no more. */
460 void pkgTagSection::Trim()
461 {
462 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
463 }
464 /*}}}*/
465 // TagSection::Exists - return True if a tag exists /*{{{*/
466 #if APT_PKG_ABI >= 413
467 bool pkgTagSection::Exists(const char* const Tag) const
468 #else
469 bool pkgTagSection::Exists(const char* const Tag)
470 #endif
471 {
472 unsigned int tmp;
473 return Find(Tag, tmp);
474 }
475 /*}}}*/
476 // TagSection::Find - Locate a tag /*{{{*/
477 // ---------------------------------------------------------------------
478 /* This searches the section for a tag that matches the given string. */
479 bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
480 {
481 size_t const Length = strlen(Tag);
482 unsigned int Bucket = AlphaIndexes[AlphaHash(Tag, Length)];
483 if (Bucket == 0)
484 return false;
485
486 for (; Bucket != 0; Bucket = d->Tags[Bucket - 1].NextInBucket)
487 {
488 if ((d->Tags[Bucket - 1].EndTag - d->Tags[Bucket - 1].StartTag) != Length)
489 continue;
490
491 char const * const St = Section + d->Tags[Bucket - 1].StartTag;
492 if (strncasecmp(Tag,St,Length) != 0)
493 continue;
494
495 Pos = Bucket - 1;
496 return true;
497 }
498
499 Pos = 0;
500 return false;
501 }
502 bool pkgTagSection::Find(const char *Tag,const char *&Start,
503 const char *&End) const
504 {
505 unsigned int Pos;
506 if (Find(Tag, Pos) == false)
507 return false;
508
509 Start = Section + d->Tags[Pos].StartValue;
510 // Strip off the gunk from the end
511 End = Section + d->Tags[Pos + 1].StartTag;
512 if (unlikely(Start > End))
513 return _error->Error("Internal parsing error");
514
515 for (; isspace(End[-1]) != 0 && End > Start; --End);
516
517 return true;
518 }
519 /*}}}*/
520 // TagSection::FindS - Find a string /*{{{*/
521 string pkgTagSection::FindS(const char *Tag) const
522 {
523 const char *Start;
524 const char *End;
525 if (Find(Tag,Start,End) == false)
526 return string();
527 return string(Start,End);
528 }
529 /*}}}*/
530 // TagSection::FindRawS - Find a string /*{{{*/
531 string pkgTagSection::FindRawS(const char *Tag) const
532 {
533 unsigned int Pos;
534 if (Find(Tag, Pos) == false)
535 return "";
536
537 char const *Start = (char const *) memchr(Section + d->Tags[Pos].EndTag, ':', d->Tags[Pos].StartValue - d->Tags[Pos].EndTag);
538 ++Start;
539 char const *End = Section + d->Tags[Pos + 1].StartTag;
540 if (unlikely(Start > End))
541 return "";
542
543 for (; isspace(End[-1]) != 0 && End > Start; --End);
544
545 return std::string(Start, End - Start);
546 }
547 /*}}}*/
548 // TagSection::FindI - Find an integer /*{{{*/
549 // ---------------------------------------------------------------------
550 /* */
551 signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
552 {
553 const char *Start;
554 const char *Stop;
555 if (Find(Tag,Start,Stop) == false)
556 return Default;
557
558 // Copy it into a temp buffer so we can use strtol
559 char S[300];
560 if ((unsigned)(Stop - Start) >= sizeof(S))
561 return Default;
562 strncpy(S,Start,Stop-Start);
563 S[Stop - Start] = 0;
564
565 char *End;
566 signed long Result = strtol(S,&End,10);
567 if (S == End)
568 return Default;
569 return Result;
570 }
571 /*}}}*/
572 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
573 // ---------------------------------------------------------------------
574 /* */
575 unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long const &Default) const
576 {
577 const char *Start;
578 const char *Stop;
579 if (Find(Tag,Start,Stop) == false)
580 return Default;
581
582 // Copy it into a temp buffer so we can use strtoull
583 char S[100];
584 if ((unsigned)(Stop - Start) >= sizeof(S))
585 return Default;
586 strncpy(S,Start,Stop-Start);
587 S[Stop - Start] = 0;
588
589 char *End;
590 unsigned long long Result = strtoull(S,&End,10);
591 if (S == End)
592 return Default;
593 return Result;
594 }
595 /*}}}*/
596 // TagSection::FindB - Find boolean value /*{{{*/
597 // ---------------------------------------------------------------------
598 /* */
599 bool pkgTagSection::FindB(const char *Tag, bool const &Default) const
600 {
601 const char *Start, *Stop;
602 if (Find(Tag, Start, Stop) == false)
603 return Default;
604 return StringToBool(string(Start, Stop));
605 }
606 /*}}}*/
607 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
608 // ---------------------------------------------------------------------
609 /* The bits marked in Flag are masked on/off in Flags */
610 bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
611 unsigned long Flag) const
612 {
613 const char *Start;
614 const char *Stop;
615 if (Find(Tag,Start,Stop) == false)
616 return true;
617 return FindFlag(Flags, Flag, Start, Stop);
618 }
619 bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
620 char const* Start, char const* Stop)
621 {
622 switch (StringToBool(string(Start, Stop)))
623 {
624 case 0:
625 Flags &= ~Flag;
626 return true;
627
628 case 1:
629 Flags |= Flag;
630 return true;
631
632 default:
633 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
634 return true;
635 }
636 return true;
637 }
638 /*}}}*/
639 void pkgTagSection::Get(const char *&Start,const char *&Stop,unsigned int I) const
640 {
641 Start = Section + d->Tags[I].StartTag;
642 Stop = Section + d->Tags[I+1].StartTag;
643 }
644 APT_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 /*{{{*/
652 pkgTagSection::Tag pkgTagSection::Tag::Remove(std::string const &Name)
653 {
654 return Tag(REMOVE, Name, "");
655 }
656 pkgTagSection::Tag pkgTagSection::Tag::Rename(std::string const &OldName, std::string const &NewName)
657 {
658 return Tag(RENAME, OldName, NewName);
659 }
660 pkgTagSection::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 }
667 static bool WriteTag(FileFd &File, std::string Tag, std::string const &Value)
668 {
669 if (Value.empty() || isspace(Value[0]) != 0)
670 Tag.append(":");
671 else
672 Tag.append(": ");
673 Tag.append(Value);
674 Tag.append("\n");
675 return File.Write(Tag.c_str(), Tag.length());
676 }
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)
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->FindRawS(R->Name.c_str());
694 else
695 continue;
696
697 return WriteTag(File, Tag, data);
698 }
699 return true;
700 }
701 bool 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], FindRawS(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, FindRawS(name.c_str())) == 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) ? FindRawS(R->Name.c_str()) : 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. */
785 APT_IGNORE_DEPRECATED_PUSH
786 bool 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(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(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(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(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 }
909 APT_IGNORE_DEPRECATED_POP
910 /*}}}*/
911
912 pkgTagSection::~pkgTagSection() { delete d; }