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