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