]> git.saurik.com Git - apt.git/blob - apt-pkg/tagfile.cc
implement a more c++-style TFRewrite alternative
[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 pkgTagFilePrivate(FileFd *pFd, unsigned long long Size) : Fd(*pFd), Buffer(NULL),
36 Start(NULL), End(NULL),
37 Done(false), iOffset(0),
38 Size(Size)
39 {
40 }
41 FileFd &Fd;
42 char *Buffer;
43 char *Start;
44 char *End;
45 bool Done;
46 unsigned long long iOffset;
47 unsigned long long Size;
48 };
49
50 class pkgTagSectionPrivate
51 {
52 public:
53 pkgTagSectionPrivate()
54 {
55 }
56 struct TagData {
57 unsigned int StartTag;
58 unsigned int EndTag;
59 unsigned int StartValue;
60 unsigned int NextInBucket;
61
62 TagData(unsigned int const StartTag) : StartTag(StartTag), EndTag(0), StartValue(0), NextInBucket(0) {}
63 };
64 std::vector<TagData> Tags;
65 };
66
67 static unsigned long AlphaHash(const char *Text, size_t Length) /*{{{*/
68 {
69 /* This very simple hash function for the last 8 letters gives
70 very good performance on the debian package files */
71 if (Length > 8)
72 {
73 Text += (Length - 8);
74 Length = 8;
75 }
76 unsigned long Res = 0;
77 for (size_t i = 0; i < Length; ++i)
78 Res = ((unsigned long)(Text[i]) & 0xDF) ^ (Res << 1);
79 return Res & 0xFF;
80 }
81 /*}}}*/
82
83 // TagFile::pkgTagFile - Constructor /*{{{*/
84 // ---------------------------------------------------------------------
85 /* */
86 pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long long Size)
87 : d(NULL)
88 {
89 Init(pFd, Size);
90 }
91
92 void pkgTagFile::Init(FileFd *pFd,unsigned long long Size)
93 {
94 /* The size is increased by 4 because if we start with the Size of the
95 filename we need to try to read 1 char more to see an EOF faster, 1
96 char the end-pointer can be on and maybe 2 newlines need to be added
97 to the end of the file -> 4 extra chars */
98 Size += 4;
99 if(d != NULL)
100 {
101 free(d->Buffer);
102 delete d;
103 }
104 d = new pkgTagFilePrivate(pFd, Size);
105
106 if (d->Fd.IsOpen() == false)
107 d->Start = d->End = d->Buffer = 0;
108 else
109 d->Buffer = (char*)malloc(sizeof(char) * Size);
110
111 if (d->Buffer == NULL)
112 d->Done = true;
113 else
114 d->Done = false;
115
116 d->Start = d->End = d->Buffer;
117 d->iOffset = 0;
118 if (d->Done == false)
119 Fill();
120 }
121 /*}}}*/
122 // TagFile::~pkgTagFile - Destructor /*{{{*/
123 // ---------------------------------------------------------------------
124 /* */
125 pkgTagFile::~pkgTagFile()
126 {
127 free(d->Buffer);
128 delete d;
129 }
130 /*}}}*/
131 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
132 APT_PURE unsigned long pkgTagFile::Offset()
133 {
134 return d->iOffset;
135 }
136 /*}}}*/
137 // TagFile::Resize - Resize the internal buffer /*{{{*/
138 // ---------------------------------------------------------------------
139 /* Resize the internal buffer (double it in size). Fail if a maximum size
140 * size is reached.
141 */
142 bool pkgTagFile::Resize()
143 {
144 // fail is the buffer grows too big
145 if(d->Size > 1024*1024+1)
146 return false;
147
148 return Resize(d->Size * 2);
149 }
150 bool pkgTagFile::Resize(unsigned long long const newSize)
151 {
152 unsigned long long const EndSize = d->End - d->Start;
153
154 // get new buffer and use it
155 char* newBuffer = (char*)realloc(d->Buffer, sizeof(char) * newSize);
156 if (newBuffer == NULL)
157 return false;
158 d->Buffer = newBuffer;
159 d->Size = newSize;
160
161 // update the start/end pointers to the new buffer
162 d->Start = d->Buffer;
163 d->End = d->Start + EndSize;
164 return true;
165 }
166 /*}}}*/
167 // TagFile::Step - Advance to the next section /*{{{*/
168 // ---------------------------------------------------------------------
169 /* If the Section Scanner fails we refill the buffer and try again.
170 * If that fails too, double the buffer size and try again until a
171 * maximum buffer is reached.
172 */
173 bool pkgTagFile::Step(pkgTagSection &Tag)
174 {
175 if(Tag.Scan(d->Start,d->End - d->Start) == false)
176 {
177 do
178 {
179 if (Fill() == false)
180 return false;
181
182 if(Tag.Scan(d->Start,d->End - d->Start, false))
183 break;
184
185 if (Resize() == false)
186 return _error->Error(_("Unable to parse package file %s (%d)"),
187 d->Fd.Name().c_str(), 1);
188
189 } while (Tag.Scan(d->Start,d->End - d->Start, false) == false);
190 }
191
192 d->Start += Tag.size();
193 d->iOffset += Tag.size();
194
195 Tag.Trim();
196 return true;
197 }
198 /*}}}*/
199 // TagFile::Fill - Top up the buffer /*{{{*/
200 // ---------------------------------------------------------------------
201 /* This takes the bit at the end of the buffer and puts it at the start
202 then fills the rest from the file */
203 bool pkgTagFile::Fill()
204 {
205 unsigned long long EndSize = d->End - d->Start;
206 unsigned long long Actual = 0;
207
208 memmove(d->Buffer,d->Start,EndSize);
209 d->Start = d->Buffer;
210 d->End = d->Buffer + EndSize;
211
212 if (d->Done == false)
213 {
214 // See if only a bit of the file is left
215 unsigned long long const dataSize = d->Size - ((d->End - d->Buffer) + 1);
216 if (d->Fd.Read(d->End, dataSize, &Actual) == false)
217 return false;
218 if (Actual != dataSize)
219 d->Done = true;
220 d->End += Actual;
221 }
222
223 if (d->Done == true)
224 {
225 if (EndSize <= 3 && Actual == 0)
226 return false;
227 if (d->Size - (d->End - d->Buffer) < 4)
228 return true;
229
230 // Append a double new line if one does not exist
231 unsigned int LineCount = 0;
232 for (const char *E = d->End - 1; E - d->End < 6 && (*E == '\n' || *E == '\r'); E--)
233 if (*E == '\n')
234 LineCount++;
235 if (LineCount < 2)
236 {
237 if ((unsigned)(d->End - d->Buffer) >= d->Size)
238 Resize(d->Size + 3);
239 for (; LineCount < 2; LineCount++)
240 *d->End++ = '\n';
241 }
242
243 return true;
244 }
245
246 return true;
247 }
248 /*}}}*/
249 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
250 // ---------------------------------------------------------------------
251 /* This jumps to a pre-recorded file location and reads the record
252 that is there */
253 bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
254 {
255 // We are within a buffer space of the next hit..
256 if (Offset >= d->iOffset && d->iOffset + (d->End - d->Start) > Offset)
257 {
258 unsigned long long Dist = Offset - d->iOffset;
259 d->Start += Dist;
260 d->iOffset += Dist;
261 // if we have seen the end, don't ask for more
262 if (d->Done == true)
263 return Tag.Scan(d->Start, d->End - d->Start);
264 else
265 return Step(Tag);
266 }
267
268 // Reposition and reload..
269 d->iOffset = Offset;
270 d->Done = false;
271 if (d->Fd.Seek(Offset) == false)
272 return false;
273 d->End = d->Start = d->Buffer;
274
275 if (Fill() == false)
276 return false;
277
278 if (Tag.Scan(d->Start, d->End - d->Start) == true)
279 return true;
280
281 // This appends a double new line (for the real eof handling)
282 if (Fill() == false)
283 return false;
284
285 if (Tag.Scan(d->Start, d->End - d->Start, false) == false)
286 return _error->Error(_("Unable to parse package file %s (%d)"),d->Fd.Name().c_str(), 2);
287
288 return true;
289 }
290 /*}}}*/
291 // pkgTagSection::pkgTagSection - Constructor /*{{{*/
292 // ---------------------------------------------------------------------
293 /* */
294 APT_IGNORE_DEPRECATED_PUSH
295 pkgTagSection::pkgTagSection()
296 : Section(0), d(NULL), Stop(0)
297 {
298 d = new pkgTagSectionPrivate();
299 #if APT_PKG_ABI < 413
300 TagCount = 0;
301 memset(&Indexes, 0, sizeof(Indexes));
302 #endif
303 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
304 }
305 APT_IGNORE_DEPRECATED_POP
306 /*}}}*/
307 // TagSection::Scan - Scan for the end of the header information /*{{{*/
308 #if APT_PKG_ABI < 413
309 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
310 {
311 return Scan(Start, MaxLength, true);
312 }
313 #endif
314 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const Restart)
315 {
316 Section = Start;
317 const char *End = Start + MaxLength;
318
319 if (Restart == false && d->Tags.empty() == false)
320 {
321 Stop = Section + d->Tags.back().StartTag;
322 if (End <= Stop)
323 return false;
324 Stop = (const char *)memchr(Stop,'\n',End - Stop);
325 if (Stop == NULL)
326 return false;
327 ++Stop;
328 }
329 else
330 {
331 Stop = Section;
332 if (d->Tags.empty() == false)
333 {
334 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
335 d->Tags.clear();
336 }
337 d->Tags.reserve(0x100);
338 }
339 #if APT_PKG_ABI >= 413
340 unsigned int TagCount = d->Tags.size();
341 #else
342 APT_IGNORE_DEPRECATED(TagCount = d->Tags.size();)
343 #endif
344
345 if (Stop == 0)
346 return false;
347
348 pkgTagSectionPrivate::TagData lastTagData(0);
349 lastTagData.EndTag = 0;
350 unsigned long lastTagHash = 0;
351 while (Stop < End)
352 {
353 TrimRecord(true,End);
354
355 // this can happen when TrimRecord trims away the entire Record
356 // (e.g. because it just contains comments)
357 if(Stop == End)
358 return true;
359
360 // Start a new index and add it to the hash
361 if (isspace(Stop[0]) == 0)
362 {
363 // store the last found tag
364 if (lastTagData.EndTag != 0)
365 {
366 if (AlphaIndexes[lastTagHash] != 0)
367 lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
368 APT_IGNORE_DEPRECATED_PUSH
369 AlphaIndexes[lastTagHash] = TagCount;
370 #if APT_PKG_ABI < 413
371 if (d->Tags.size() < sizeof(Indexes)/sizeof(Indexes[0]))
372 Indexes[d->Tags.size()] = lastTagData.StartTag;
373 #endif
374 APT_IGNORE_DEPRECATED_POP
375 d->Tags.push_back(lastTagData);
376 }
377
378 APT_IGNORE_DEPRECATED(++TagCount;)
379 lastTagData = pkgTagSectionPrivate::TagData(Stop - Section);
380 // find the colon separating tag and value
381 char const * Colon = (char const *) memchr(Stop, ':', End - Stop);
382 if (Colon == NULL)
383 return false;
384 // find the end of the tag (which might or might not be the colon)
385 char const * EndTag = Colon;
386 --EndTag;
387 for (; EndTag > Stop && isspace(*EndTag) != 0; --EndTag)
388 ;
389 ++EndTag;
390 lastTagData.EndTag = EndTag - Section;
391 lastTagHash = AlphaHash(Stop, EndTag - Stop);
392 // find the beginning of the value
393 Stop = Colon + 1;
394 for (; isspace(*Stop) != 0; ++Stop);
395 if (Stop >= End)
396 return false;
397 lastTagData.StartValue = Stop - Section;
398 }
399
400 Stop = (const char *)memchr(Stop,'\n',End - Stop);
401
402 if (Stop == 0)
403 return false;
404
405 for (; Stop+1 < End && Stop[1] == '\r'; Stop++)
406 /* nothing */
407 ;
408
409 // Double newline marks the end of the record
410 if (Stop+1 < End && Stop[1] == '\n')
411 {
412 if (lastTagData.EndTag != 0)
413 {
414 if (AlphaIndexes[lastTagHash] != 0)
415 lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
416 APT_IGNORE_DEPRECATED(AlphaIndexes[lastTagHash] = TagCount;)
417 #if APT_PKG_ABI < 413
418 APT_IGNORE_DEPRECATED(Indexes[d->Tags.size()] = lastTagData.StartTag;)
419 #endif
420 d->Tags.push_back(lastTagData);
421 }
422
423 pkgTagSectionPrivate::TagData const td(Stop - Section);
424 #if APT_PKG_ABI < 413
425 APT_IGNORE_DEPRECATED(Indexes[d->Tags.size()] = td.StartTag;)
426 #endif
427 d->Tags.push_back(td);
428 TrimRecord(false,End);
429 return true;
430 }
431
432 Stop++;
433 }
434
435 return false;
436 }
437 /*}}}*/
438 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
439 // ---------------------------------------------------------------------
440 /* There should be exactly 2 newline at the end of the record, no more. */
441 void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
442 {
443 if (BeforeRecord == true)
444 return;
445 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
446 }
447 /*}}}*/
448 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
449 // ---------------------------------------------------------------------
450 /* There should be exactly 1 newline at the end of the buffer, no more. */
451 void pkgTagSection::Trim()
452 {
453 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
454 }
455 /*}}}*/
456 // TagSection::Exists - return True if a tag exists /*{{{*/
457 #if APT_PKG_ABI >= 413
458 bool pkgTagSection::Exists(const char* const Tag) const
459 #else
460 bool pkgTagSection::Exists(const char* const Tag)
461 #endif
462 {
463 unsigned int tmp;
464 return Find(Tag, tmp);
465 }
466 /*}}}*/
467 // TagSection::Find - Locate a tag /*{{{*/
468 // ---------------------------------------------------------------------
469 /* This searches the section for a tag that matches the given string. */
470 bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
471 {
472 size_t const Length = strlen(Tag);
473 unsigned int Bucket = AlphaIndexes[AlphaHash(Tag, Length)];
474 if (Bucket == 0)
475 return false;
476
477 for (; Bucket != 0; Bucket = d->Tags[Bucket - 1].NextInBucket)
478 {
479 if ((d->Tags[Bucket - 1].EndTag - d->Tags[Bucket - 1].StartTag) != Length)
480 continue;
481
482 char const * const St = Section + d->Tags[Bucket - 1].StartTag;
483 if (strncasecmp(Tag,St,Length) != 0)
484 continue;
485
486 Pos = Bucket - 1;
487 return true;
488 }
489
490 Pos = 0;
491 return false;
492 }
493 bool pkgTagSection::Find(const char *Tag,const char *&Start,
494 const char *&End) const
495 {
496 unsigned int Pos;
497 if (Find(Tag, Pos) == false)
498 return false;
499
500 Start = Section + d->Tags[Pos].StartValue;
501 // Strip off the gunk from the end
502 End = Section + d->Tags[Pos + 1].StartTag;
503 if (unlikely(Start > End))
504 return _error->Error("Internal parsing error");
505
506 for (; isspace(End[-1]) != 0 && End > Start; --End);
507
508 return true;
509 }
510 /*}}}*/
511 // TagSection::FindS - Find a string /*{{{*/
512 string pkgTagSection::FindS(const char *Tag) const
513 {
514 const char *Start;
515 const char *End;
516 if (Find(Tag,Start,End) == false)
517 return string();
518 return string(Start,End);
519 }
520 /*}}}*/
521 // TagSection::FindRawS - Find a string /*{{{*/
522 string pkgTagSection::FindRawS(const char *Tag) const
523 {
524 unsigned int Pos;
525 if (Find(Tag, Pos) == false)
526 return "";
527
528 char const *Start = (char const *) memchr(Section + d->Tags[Pos].EndTag, ':', d->Tags[Pos].StartValue - d->Tags[Pos].EndTag);
529 ++Start;
530 char const *End = Section + d->Tags[Pos + 1].StartTag;
531 if (unlikely(Start > End))
532 return "";
533
534 for (; isspace(End[-1]) != 0 && End > Start; --End);
535
536 return std::string(Start, End - Start);
537 }
538 /*}}}*/
539 // TagSection::FindI - Find an integer /*{{{*/
540 // ---------------------------------------------------------------------
541 /* */
542 signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
543 {
544 const char *Start;
545 const char *Stop;
546 if (Find(Tag,Start,Stop) == false)
547 return Default;
548
549 // Copy it into a temp buffer so we can use strtol
550 char S[300];
551 if ((unsigned)(Stop - Start) >= sizeof(S))
552 return Default;
553 strncpy(S,Start,Stop-Start);
554 S[Stop - Start] = 0;
555
556 char *End;
557 signed long Result = strtol(S,&End,10);
558 if (S == End)
559 return Default;
560 return Result;
561 }
562 /*}}}*/
563 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
564 // ---------------------------------------------------------------------
565 /* */
566 unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long const &Default) const
567 {
568 const char *Start;
569 const char *Stop;
570 if (Find(Tag,Start,Stop) == false)
571 return Default;
572
573 // Copy it into a temp buffer so we can use strtoull
574 char S[100];
575 if ((unsigned)(Stop - Start) >= sizeof(S))
576 return Default;
577 strncpy(S,Start,Stop-Start);
578 S[Stop - Start] = 0;
579
580 char *End;
581 unsigned long long Result = strtoull(S,&End,10);
582 if (S == End)
583 return Default;
584 return Result;
585 }
586 /*}}}*/
587 // TagSection::FindB - Find boolean value /*{{{*/
588 // ---------------------------------------------------------------------
589 /* */
590 bool pkgTagSection::FindB(const char *Tag, bool const &Default) const
591 {
592 const char *Start, *Stop;
593 if (Find(Tag, Start, Stop) == false)
594 return Default;
595 return StringToBool(string(Start, Stop));
596 }
597 /*}}}*/
598 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
599 // ---------------------------------------------------------------------
600 /* The bits marked in Flag are masked on/off in Flags */
601 bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
602 unsigned long Flag) const
603 {
604 const char *Start;
605 const char *Stop;
606 if (Find(Tag,Start,Stop) == false)
607 return true;
608 return FindFlag(Flags, Flag, Start, Stop);
609 }
610 bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
611 char const* Start, char const* Stop)
612 {
613 switch (StringToBool(string(Start, Stop)))
614 {
615 case 0:
616 Flags &= ~Flag;
617 return true;
618
619 case 1:
620 Flags |= Flag;
621 return true;
622
623 default:
624 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
625 return true;
626 }
627 return true;
628 }
629 /*}}}*/
630 void pkgTagSection::Get(const char *&Start,const char *&Stop,unsigned int I) const
631 {
632 Start = Section + d->Tags[I].StartTag;
633 Stop = Section + d->Tags[I+1].StartTag;
634 }
635 APT_PURE unsigned int pkgTagSection::Count() const { /*{{{*/
636 if (d->Tags.empty() == true)
637 return 0;
638 // the last element is just marking the end and isn't a real one
639 return d->Tags.size() - 1;
640 }
641 /*}}}*/
642 // TagSection::Write - Ordered (re)writing of fields /*{{{*/
643 pkgTagSection::Tag pkgTagSection::Tag::Remove(std::string const &Name)
644 {
645 return Tag(REMOVE, Name, "");
646 }
647 pkgTagSection::Tag pkgTagSection::Tag::Rename(std::string const &OldName, std::string const &NewName)
648 {
649 return Tag(RENAME, OldName, NewName);
650 }
651 pkgTagSection::Tag pkgTagSection::Tag::Rewrite(std::string const &Name, std::string const &Data)
652 {
653 if (Data.empty() == true)
654 return Tag(REMOVE, Name, "");
655 else
656 return Tag(REWRITE, Name, Data);
657 }
658 static bool WriteTag(FileFd &File, std::string Tag, std::string const &Value)
659 {
660 if (Value.empty() || isspace(Value[0]) != 0)
661 Tag.append(":");
662 else
663 Tag.append(": ");
664 Tag.append(Value);
665 Tag.append("\n");
666 return File.Write(Tag.c_str(), Tag.length());
667 }
668 static bool RewriteTags(FileFd &File, pkgTagSection const * const This, char const * const Tag,
669 std::vector<pkgTagSection::Tag>::const_iterator &R,
670 std::vector<pkgTagSection::Tag>::const_iterator const &REnd)
671 {
672 size_t const TagLen = strlen(Tag);
673 for (; R != REnd; ++R)
674 {
675 std::string data;
676 if (R->Name.length() == TagLen && strncasecmp(R->Name.c_str(), Tag, R->Name.length()) == 0)
677 {
678 if (R->Action != pkgTagSection::Tag::REWRITE)
679 break;
680 data = R->Data;
681 }
682 else if(R->Action == pkgTagSection::Tag::RENAME && R->Data.length() == TagLen &&
683 strncasecmp(R->Data.c_str(), Tag, R->Data.length()) == 0)
684 data = This->FindRawS(R->Name.c_str());
685 else
686 continue;
687
688 return WriteTag(File, Tag, data);
689 }
690 return true;
691 }
692 bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::vector<Tag> const &Rewrite) const
693 {
694 // first pass: Write everything we have an order for
695 if (Order != NULL)
696 {
697 for (unsigned int I = 0; Order[I] != 0; ++I)
698 {
699 std::vector<Tag>::const_iterator R = Rewrite.begin();
700 if (RewriteTags(File, this, Order[I], R, Rewrite.end()) == false)
701 return false;
702 if (R != Rewrite.end())
703 continue;
704
705 if (Exists(Order[I]) == false)
706 continue;
707
708 if (WriteTag(File, Order[I], FindRawS(Order[I])) == false)
709 return false;
710 }
711 }
712 // second pass: See if we have tags which aren't ordered
713 if (d->Tags.empty() == false)
714 {
715 for (std::vector<pkgTagSectionPrivate::TagData>::const_iterator T = d->Tags.begin(); T != d->Tags.end() - 1; ++T)
716 {
717 char const * const fieldname = Section + T->StartTag;
718 size_t fieldnamelen = T->EndTag - T->StartTag;
719 if (Order != NULL)
720 {
721 unsigned int I = 0;
722 for (; Order[I] != 0; ++I)
723 {
724 if (fieldnamelen == strlen(Order[I]) && strncasecmp(fieldname, Order[I], fieldnamelen) == 0)
725 break;
726 }
727 if (Order[I] != 0)
728 continue;
729 }
730
731 std::string const name(fieldname, fieldnamelen);
732 std::vector<Tag>::const_iterator R = Rewrite.begin();
733 if (RewriteTags(File, this, name.c_str(), R, Rewrite.end()) == false)
734 return false;
735 if (R != Rewrite.end())
736 continue;
737
738 if (WriteTag(File, name, FindRawS(name.c_str())) == false)
739 return false;
740 }
741 }
742 // last pass: see if there are any rewrites remaining we haven't done yet
743 for (std::vector<Tag>::const_iterator R = Rewrite.begin(); R != Rewrite.end(); ++R)
744 {
745 if (R->Action == Tag::REMOVE)
746 continue;
747 std::string const name = ((R->Action == Tag::RENAME) ? R->Data : R->Name);
748 if (Exists(name.c_str()))
749 continue;
750 if (Order != NULL)
751 {
752 unsigned int I = 0;
753 for (; Order[I] != 0; ++I)
754 {
755 if (strncasecmp(name.c_str(), Order[I], name.length()) == 0 && name.length() == strlen(Order[I]))
756 break;
757 }
758 if (Order[I] != 0)
759 continue;
760 }
761
762 if (WriteTag(File, name, ((R->Action == Tag::RENAME) ? FindRawS(R->Name.c_str()) : R->Data)) == false)
763 return false;
764 }
765 return true;
766 }
767 /*}}}*/
768
769 #include "tagfile-order.c"
770
771 // TFRewrite - Rewrite a control record /*{{{*/
772 // ---------------------------------------------------------------------
773 /* This writes the control record to stdout rewriting it as necessary. The
774 override map item specificies the rewriting rules to follow. This also
775 takes the time to sort the feild list. */
776 APT_IGNORE_DEPRECATED_PUSH
777 bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
778 TFRewriteData *Rewrite)
779 {
780 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
781 for (unsigned I = 0; I != 256; I++)
782 Visited[I] = 0;
783
784 // Set new tag up as necessary.
785 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
786 {
787 if (Rewrite[J].NewTag == 0)
788 Rewrite[J].NewTag = Rewrite[J].Tag;
789 }
790
791 // Write all all of the tags, in order.
792 if (Order != NULL)
793 {
794 for (unsigned int I = 0; Order[I] != 0; I++)
795 {
796 bool Rewritten = false;
797
798 // See if this is a field that needs to be rewritten
799 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
800 {
801 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
802 {
803 Visited[J] |= 2;
804 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
805 {
806 if (isspace(Rewrite[J].Rewrite[0]))
807 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
808 else
809 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
810 }
811 Rewritten = true;
812 break;
813 }
814 }
815
816 // See if it is in the fragment
817 unsigned Pos;
818 if (Tags.Find(Order[I],Pos) == false)
819 continue;
820 Visited[Pos] |= 1;
821
822 if (Rewritten == true)
823 continue;
824
825 /* Write out this element, taking a moment to rewrite the tag
826 in case of changes of case. */
827 const char *Start;
828 const char *Stop;
829 Tags.Get(Start,Stop,Pos);
830
831 if (fputs(Order[I],Output) < 0)
832 return _error->Errno("fputs","IO Error to output");
833 Start += strlen(Order[I]);
834 if (fwrite(Start,Stop - Start,1,Output) != 1)
835 return _error->Errno("fwrite","IO Error to output");
836 if (Stop[-1] != '\n')
837 fprintf(Output,"\n");
838 }
839 }
840
841 // Now write all the old tags that were missed.
842 for (unsigned int I = 0; I != Tags.Count(); I++)
843 {
844 if ((Visited[I] & 1) == 1)
845 continue;
846
847 const char *Start;
848 const char *Stop;
849 Tags.Get(Start,Stop,I);
850 const char *End = Start;
851 for (; End < Stop && *End != ':'; End++);
852
853 // See if this is a field that needs to be rewritten
854 bool Rewritten = false;
855 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
856 {
857 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
858 {
859 Visited[J] |= 2;
860 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
861 {
862 if (isspace(Rewrite[J].Rewrite[0]))
863 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
864 else
865 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
866 }
867
868 Rewritten = true;
869 break;
870 }
871 }
872
873 if (Rewritten == true)
874 continue;
875
876 // Write out this element
877 if (fwrite(Start,Stop - Start,1,Output) != 1)
878 return _error->Errno("fwrite","IO Error to output");
879 if (Stop[-1] != '\n')
880 fprintf(Output,"\n");
881 }
882
883 // Now write all the rewrites that were missed
884 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
885 {
886 if ((Visited[J] & 2) == 2)
887 continue;
888
889 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
890 {
891 if (isspace(Rewrite[J].Rewrite[0]))
892 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
893 else
894 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
895 }
896 }
897
898 return true;
899 }
900 APT_IGNORE_DEPRECATED_POP
901 /*}}}*/
902
903 pkgTagSection::~pkgTagSection() { delete d; }