]> git.saurik.com Git - apt.git/blob - apt-pkg/tagfile.cc
Switch to DJB hashing and use prime number as table size
[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 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
309 }
310 APT_IGNORE_DEPRECATED_POP
311 /*}}}*/
312 // TagSection::Scan - Scan for the end of the header information /*{{{*/
313 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const Restart)
314 {
315 Section = Start;
316 const char *End = Start + MaxLength;
317
318 if (Restart == false && d->Tags.empty() == false)
319 {
320 Stop = Section + d->Tags.back().StartTag;
321 if (End <= Stop)
322 return false;
323 Stop = (const char *)memchr(Stop,'\n',End - Stop);
324 if (Stop == NULL)
325 return false;
326 ++Stop;
327 }
328 else
329 {
330 Stop = Section;
331 if (d->Tags.empty() == false)
332 {
333 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
334 d->Tags.clear();
335 }
336 d->Tags.reserve(0x100);
337 }
338 unsigned int TagCount = d->Tags.size();
339
340 if (Stop == 0)
341 return false;
342
343 pkgTagSectionPrivate::TagData lastTagData(0);
344 lastTagData.EndTag = 0;
345 unsigned long lastTagHash = 0;
346 while (Stop < End)
347 {
348 TrimRecord(true,End);
349
350 // this can happen when TrimRecord trims away the entire Record
351 // (e.g. because it just contains comments)
352 if(Stop == End)
353 return true;
354
355 // Start a new index and add it to the hash
356 if (isspace_ascii(Stop[0]) == 0)
357 {
358 // store the last found tag
359 if (lastTagData.EndTag != 0)
360 {
361 if (AlphaIndexes[lastTagHash] != 0)
362 lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
363 APT_IGNORE_DEPRECATED_PUSH
364 AlphaIndexes[lastTagHash] = TagCount;
365 APT_IGNORE_DEPRECATED_POP
366 d->Tags.push_back(lastTagData);
367 }
368
369 APT_IGNORE_DEPRECATED(++TagCount;)
370 lastTagData = pkgTagSectionPrivate::TagData(Stop - Section);
371 // find the colon separating tag and value
372 char const * Colon = (char const *) memchr(Stop, ':', End - Stop);
373 if (Colon == NULL)
374 return false;
375 // find the end of the tag (which might or might not be the colon)
376 char const * EndTag = Colon;
377 --EndTag;
378 for (; EndTag > Stop && isspace_ascii(*EndTag) != 0; --EndTag)
379 ;
380 ++EndTag;
381 lastTagData.EndTag = EndTag - Section;
382 lastTagHash = AlphaHash(Stop, EndTag - Stop);
383 // find the beginning of the value
384 Stop = Colon + 1;
385 for (; isspace_ascii(*Stop) != 0; ++Stop)
386 if (*Stop == '\n' && Stop[1] != ' ')
387 break;
388 if (Stop >= End)
389 return false;
390 lastTagData.StartValue = Stop - Section;
391 }
392
393 Stop = (const char *)memchr(Stop,'\n',End - Stop);
394
395 if (Stop == 0)
396 return false;
397
398 for (; Stop+1 < End && Stop[1] == '\r'; Stop++)
399 /* nothing */
400 ;
401
402 // Double newline marks the end of the record
403 if (Stop+1 < End && Stop[1] == '\n')
404 {
405 if (lastTagData.EndTag != 0)
406 {
407 if (AlphaIndexes[lastTagHash] != 0)
408 lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
409 APT_IGNORE_DEPRECATED(AlphaIndexes[lastTagHash] = TagCount;)
410 d->Tags.push_back(lastTagData);
411 }
412
413 pkgTagSectionPrivate::TagData const td(Stop - Section);
414 d->Tags.push_back(td);
415 TrimRecord(false,End);
416 return true;
417 }
418
419 Stop++;
420 }
421
422 return false;
423 }
424 /*}}}*/
425 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
426 // ---------------------------------------------------------------------
427 /* There should be exactly 2 newline at the end of the record, no more. */
428 void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
429 {
430 if (BeforeRecord == true)
431 return;
432 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
433 }
434 /*}}}*/
435 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
436 // ---------------------------------------------------------------------
437 /* There should be exactly 1 newline at the end of the buffer, no more. */
438 void pkgTagSection::Trim()
439 {
440 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
441 }
442 /*}}}*/
443 // TagSection::Exists - return True if a tag exists /*{{{*/
444 bool pkgTagSection::Exists(const char* const Tag) const
445 {
446 unsigned int tmp;
447 return Find(Tag, tmp);
448 }
449 /*}}}*/
450 // TagSection::Find - Locate a tag /*{{{*/
451 // ---------------------------------------------------------------------
452 /* This searches the section for a tag that matches the given string. */
453 bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
454 {
455 size_t const Length = strlen(Tag);
456 unsigned int Bucket = AlphaIndexes[AlphaHash(Tag, Length)];
457 if (Bucket == 0)
458 return false;
459
460 for (; Bucket != 0; Bucket = d->Tags[Bucket - 1].NextInBucket)
461 {
462 if ((d->Tags[Bucket - 1].EndTag - d->Tags[Bucket - 1].StartTag) != Length)
463 continue;
464
465 char const * const St = Section + d->Tags[Bucket - 1].StartTag;
466 if (strncasecmp(Tag,St,Length) != 0)
467 continue;
468
469 Pos = Bucket - 1;
470 return true;
471 }
472
473 Pos = 0;
474 return false;
475 }
476 bool pkgTagSection::Find(const char *Tag,const char *&Start,
477 const char *&End) const
478 {
479 unsigned int Pos;
480 if (Find(Tag, Pos) == false)
481 return false;
482
483 Start = Section + d->Tags[Pos].StartValue;
484 // Strip off the gunk from the end
485 End = Section + d->Tags[Pos + 1].StartTag;
486 if (unlikely(Start > End))
487 return _error->Error("Internal parsing error");
488
489 for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
490
491 return true;
492 }
493 /*}}}*/
494 // TagSection::FindS - Find a string /*{{{*/
495 string pkgTagSection::FindS(const char *Tag) const
496 {
497 const char *Start;
498 const char *End;
499 if (Find(Tag,Start,End) == false)
500 return string();
501 return string(Start,End);
502 }
503 /*}}}*/
504 // TagSection::FindRawS - Find a string /*{{{*/
505 string pkgTagSection::FindRawS(const char *Tag) const
506 {
507 unsigned int Pos;
508 if (Find(Tag, Pos) == false)
509 return "";
510
511 char const *Start = (char const *) memchr(Section + d->Tags[Pos].EndTag, ':', d->Tags[Pos].StartValue - d->Tags[Pos].EndTag);
512 ++Start;
513 char const *End = Section + d->Tags[Pos + 1].StartTag;
514 if (unlikely(Start > End))
515 return "";
516
517 for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
518
519 return std::string(Start, End - Start);
520 }
521 /*}}}*/
522 // TagSection::FindI - Find an integer /*{{{*/
523 // ---------------------------------------------------------------------
524 /* */
525 signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
526 {
527 const char *Start;
528 const char *Stop;
529 if (Find(Tag,Start,Stop) == false)
530 return Default;
531
532 // Copy it into a temp buffer so we can use strtol
533 char S[300];
534 if ((unsigned)(Stop - Start) >= sizeof(S))
535 return Default;
536 strncpy(S,Start,Stop-Start);
537 S[Stop - Start] = 0;
538
539 errno = 0;
540 char *End;
541 signed long Result = strtol(S,&End,10);
542 if (errno == ERANGE ||
543 Result < std::numeric_limits<int>::min() || Result > std::numeric_limits<int>::max()) {
544 errno = ERANGE;
545 _error->Error(_("Cannot convert %s to integer: out of range"), S);
546 }
547 if (S == End)
548 return Default;
549 return Result;
550 }
551 /*}}}*/
552 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
553 // ---------------------------------------------------------------------
554 /* */
555 unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long const &Default) const
556 {
557 const char *Start;
558 const char *Stop;
559 if (Find(Tag,Start,Stop) == false)
560 return Default;
561
562 // Copy it into a temp buffer so we can use strtoull
563 char S[100];
564 if ((unsigned)(Stop - Start) >= sizeof(S))
565 return Default;
566 strncpy(S,Start,Stop-Start);
567 S[Stop - Start] = 0;
568
569 char *End;
570 unsigned long long Result = strtoull(S,&End,10);
571 if (S == End)
572 return Default;
573 return Result;
574 }
575 /*}}}*/
576 // TagSection::FindB - Find boolean value /*{{{*/
577 // ---------------------------------------------------------------------
578 /* */
579 bool pkgTagSection::FindB(const char *Tag, bool const &Default) const
580 {
581 const char *Start, *Stop;
582 if (Find(Tag, Start, Stop) == false)
583 return Default;
584 return StringToBool(string(Start, Stop));
585 }
586 /*}}}*/
587 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
588 // ---------------------------------------------------------------------
589 /* The bits marked in Flag are masked on/off in Flags */
590 bool pkgTagSection::FindFlag(const char * const Tag, uint8_t &Flags,
591 uint8_t const Flag) const
592 {
593 const char *Start;
594 const char *Stop;
595 if (Find(Tag,Start,Stop) == false)
596 return true;
597 return FindFlag(Flags, Flag, Start, Stop);
598 }
599 bool pkgTagSection::FindFlag(uint8_t &Flags, uint8_t const Flag,
600 char const* const Start, char const* const Stop)
601 {
602 switch (StringToBool(string(Start, Stop)))
603 {
604 case 0:
605 Flags &= ~Flag;
606 return true;
607
608 case 1:
609 Flags |= Flag;
610 return true;
611
612 default:
613 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
614 return true;
615 }
616 return true;
617 }
618 bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
619 unsigned long Flag) const
620 {
621 const char *Start;
622 const char *Stop;
623 if (Find(Tag,Start,Stop) == false)
624 return true;
625 return FindFlag(Flags, Flag, Start, Stop);
626 }
627 bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
628 char const* Start, char const* Stop)
629 {
630 switch (StringToBool(string(Start, Stop)))
631 {
632 case 0:
633 Flags &= ~Flag;
634 return true;
635
636 case 1:
637 Flags |= Flag;
638 return true;
639
640 default:
641 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
642 return true;
643 }
644 return true;
645 }
646 /*}}}*/
647 void pkgTagSection::Get(const char *&Start,const char *&Stop,unsigned int I) const
648 {
649 Start = Section + d->Tags[I].StartTag;
650 Stop = Section + d->Tags[I+1].StartTag;
651 }
652 APT_PURE unsigned int pkgTagSection::Count() const { /*{{{*/
653 if (d->Tags.empty() == true)
654 return 0;
655 // the last element is just marking the end and isn't a real one
656 return d->Tags.size() - 1;
657 }
658 /*}}}*/
659 // TagSection::Write - Ordered (re)writing of fields /*{{{*/
660 pkgTagSection::Tag pkgTagSection::Tag::Remove(std::string const &Name)
661 {
662 return Tag(REMOVE, Name, "");
663 }
664 pkgTagSection::Tag pkgTagSection::Tag::Rename(std::string const &OldName, std::string const &NewName)
665 {
666 return Tag(RENAME, OldName, NewName);
667 }
668 pkgTagSection::Tag pkgTagSection::Tag::Rewrite(std::string const &Name, std::string const &Data)
669 {
670 if (Data.empty() == true)
671 return Tag(REMOVE, Name, "");
672 else
673 return Tag(REWRITE, Name, Data);
674 }
675 static bool WriteTag(FileFd &File, std::string Tag, std::string const &Value)
676 {
677 if (Value.empty() || isspace_ascii(Value[0]) != 0)
678 Tag.append(":");
679 else
680 Tag.append(": ");
681 Tag.append(Value);
682 Tag.append("\n");
683 return File.Write(Tag.c_str(), Tag.length());
684 }
685 static bool RewriteTags(FileFd &File, pkgTagSection const * const This, char const * const Tag,
686 std::vector<pkgTagSection::Tag>::const_iterator &R,
687 std::vector<pkgTagSection::Tag>::const_iterator const &REnd)
688 {
689 size_t const TagLen = strlen(Tag);
690 for (; R != REnd; ++R)
691 {
692 std::string data;
693 if (R->Name.length() == TagLen && strncasecmp(R->Name.c_str(), Tag, R->Name.length()) == 0)
694 {
695 if (R->Action != pkgTagSection::Tag::REWRITE)
696 break;
697 data = R->Data;
698 }
699 else if(R->Action == pkgTagSection::Tag::RENAME && R->Data.length() == TagLen &&
700 strncasecmp(R->Data.c_str(), Tag, R->Data.length()) == 0)
701 data = This->FindRawS(R->Name.c_str());
702 else
703 continue;
704
705 return WriteTag(File, Tag, data);
706 }
707 return true;
708 }
709 bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::vector<Tag> const &Rewrite) const
710 {
711 // first pass: Write everything we have an order for
712 if (Order != NULL)
713 {
714 for (unsigned int I = 0; Order[I] != 0; ++I)
715 {
716 std::vector<Tag>::const_iterator R = Rewrite.begin();
717 if (RewriteTags(File, this, Order[I], R, Rewrite.end()) == false)
718 return false;
719 if (R != Rewrite.end())
720 continue;
721
722 if (Exists(Order[I]) == false)
723 continue;
724
725 if (WriteTag(File, Order[I], FindRawS(Order[I])) == false)
726 return false;
727 }
728 }
729 // second pass: See if we have tags which aren't ordered
730 if (d->Tags.empty() == false)
731 {
732 for (std::vector<pkgTagSectionPrivate::TagData>::const_iterator T = d->Tags.begin(); T != d->Tags.end() - 1; ++T)
733 {
734 char const * const fieldname = Section + T->StartTag;
735 size_t fieldnamelen = T->EndTag - T->StartTag;
736 if (Order != NULL)
737 {
738 unsigned int I = 0;
739 for (; Order[I] != 0; ++I)
740 {
741 if (fieldnamelen == strlen(Order[I]) && strncasecmp(fieldname, Order[I], fieldnamelen) == 0)
742 break;
743 }
744 if (Order[I] != 0)
745 continue;
746 }
747
748 std::string const name(fieldname, fieldnamelen);
749 std::vector<Tag>::const_iterator R = Rewrite.begin();
750 if (RewriteTags(File, this, name.c_str(), R, Rewrite.end()) == false)
751 return false;
752 if (R != Rewrite.end())
753 continue;
754
755 if (WriteTag(File, name, FindRawS(name.c_str())) == false)
756 return false;
757 }
758 }
759 // last pass: see if there are any rewrites remaining we haven't done yet
760 for (std::vector<Tag>::const_iterator R = Rewrite.begin(); R != Rewrite.end(); ++R)
761 {
762 if (R->Action == Tag::REMOVE)
763 continue;
764 std::string const name = ((R->Action == Tag::RENAME) ? R->Data : R->Name);
765 if (Exists(name.c_str()))
766 continue;
767 if (Order != NULL)
768 {
769 unsigned int I = 0;
770 for (; Order[I] != 0; ++I)
771 {
772 if (strncasecmp(name.c_str(), Order[I], name.length()) == 0 && name.length() == strlen(Order[I]))
773 break;
774 }
775 if (Order[I] != 0)
776 continue;
777 }
778
779 if (WriteTag(File, name, ((R->Action == Tag::RENAME) ? FindRawS(R->Name.c_str()) : R->Data)) == false)
780 return false;
781 }
782 return true;
783 }
784 /*}}}*/
785
786 void pkgUserTagSection::TrimRecord(bool /*BeforeRecord*/, const char* &End)/*{{{*/
787 {
788 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r' || Stop[0] == '#'); Stop++)
789 if (Stop[0] == '#')
790 Stop = (const char*) memchr(Stop,'\n',End-Stop);
791 }
792 /*}}}*/
793
794 #include "tagfile-order.c"
795
796 // TFRewrite - Rewrite a control record /*{{{*/
797 // ---------------------------------------------------------------------
798 /* This writes the control record to stdout rewriting it as necessary. The
799 override map item specificies the rewriting rules to follow. This also
800 takes the time to sort the feild list. */
801 APT_IGNORE_DEPRECATED_PUSH
802 bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
803 TFRewriteData *Rewrite)
804 {
805 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
806 for (unsigned I = 0; I != 256; I++)
807 Visited[I] = 0;
808
809 // Set new tag up as necessary.
810 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
811 {
812 if (Rewrite[J].NewTag == 0)
813 Rewrite[J].NewTag = Rewrite[J].Tag;
814 }
815
816 // Write all all of the tags, in order.
817 if (Order != NULL)
818 {
819 for (unsigned int I = 0; Order[I] != 0; I++)
820 {
821 bool Rewritten = false;
822
823 // See if this is a field that needs to be rewritten
824 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
825 {
826 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
827 {
828 Visited[J] |= 2;
829 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
830 {
831 if (isspace_ascii(Rewrite[J].Rewrite[0]))
832 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
833 else
834 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
835 }
836 Rewritten = true;
837 break;
838 }
839 }
840
841 // See if it is in the fragment
842 unsigned Pos;
843 if (Tags.Find(Order[I],Pos) == false)
844 continue;
845 Visited[Pos] |= 1;
846
847 if (Rewritten == true)
848 continue;
849
850 /* Write out this element, taking a moment to rewrite the tag
851 in case of changes of case. */
852 const char *Start;
853 const char *Stop;
854 Tags.Get(Start,Stop,Pos);
855
856 if (fputs(Order[I],Output) < 0)
857 return _error->Errno("fputs","IO Error to output");
858 Start += strlen(Order[I]);
859 if (fwrite(Start,Stop - Start,1,Output) != 1)
860 return _error->Errno("fwrite","IO Error to output");
861 if (Stop[-1] != '\n')
862 fprintf(Output,"\n");
863 }
864 }
865
866 // Now write all the old tags that were missed.
867 for (unsigned int I = 0; I != Tags.Count(); I++)
868 {
869 if ((Visited[I] & 1) == 1)
870 continue;
871
872 const char *Start;
873 const char *Stop;
874 Tags.Get(Start,Stop,I);
875 const char *End = Start;
876 for (; End < Stop && *End != ':'; End++);
877
878 // See if this is a field that needs to be rewritten
879 bool Rewritten = false;
880 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
881 {
882 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
883 {
884 Visited[J] |= 2;
885 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
886 {
887 if (isspace_ascii(Rewrite[J].Rewrite[0]))
888 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
889 else
890 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
891 }
892
893 Rewritten = true;
894 break;
895 }
896 }
897
898 if (Rewritten == true)
899 continue;
900
901 // Write out this element
902 if (fwrite(Start,Stop - Start,1,Output) != 1)
903 return _error->Errno("fwrite","IO Error to output");
904 if (Stop[-1] != '\n')
905 fprintf(Output,"\n");
906 }
907
908 // Now write all the rewrites that were missed
909 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
910 {
911 if ((Visited[J] & 2) == 2)
912 continue;
913
914 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
915 {
916 if (isspace_ascii(Rewrite[J].Rewrite[0]))
917 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
918 else
919 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
920 }
921 }
922
923 return true;
924 }
925 APT_IGNORE_DEPRECATED_POP
926 /*}}}*/
927
928 pkgTagSection::~pkgTagSection() { delete d; }