]> git.saurik.com Git - apt.git/blame - apt-pkg/tagfile.cc
Most interfaces (Maemo) need a high-level name :/.
[apt.git] / apt-pkg / tagfile.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
b3d44315 3// $Id: tagfile.cc,v 1.37.2.2 2003/12/31 16:02:30 mdz Exp $
578bfd0a
AL
4/* ######################################################################
5
6 Fast scanner for RFC-822 type header information
7
ad00ae81 8 This uses a rotating buffer to load the package information into.
578bfd0a
AL
9 The scanner runs over it and isolates and indexes a single section.
10
11 ##################################################################### */
12 /*}}}*/
13// Include Files /*{{{*/
ea542140
DK
14#include<config.h>
15
094a497d 16#include <apt-pkg/tagfile.h>
abfd0770 17#include <apt-pkg/tagfile-keys.h>
094a497d 18#include <apt-pkg/error.h>
cdcc6d34 19#include <apt-pkg/strutl.h>
472ff00e 20#include <apt-pkg/fileutl.h>
eff0c22e 21#include <apt-pkg/string_view.h>
578bfd0a 22
55153bf9
DK
23#include <list>
24
578bfd0a
AL
25#include <string>
26#include <stdio.h>
851a45a8 27#include <ctype.h>
453b82a3
DK
28#include <stdlib.h>
29#include <string.h>
ea542140
DK
30
31#include <apti18n.h>
578bfd0a
AL
32 /*}}}*/
33
851a45a8 34using std::string;
eff0c22e 35using APT::StringView;
851a45a8 36
55153bf9 37class APT_HIDDEN pkgTagFilePrivate /*{{{*/
1abbce9e
MV
38{
39public:
55153bf9 40 void Reset(FileFd * const pFd, unsigned long long const pSize, pkgTagFile::Flags const pFlags)
1abbce9e 41 {
3d8232bf
DK
42 if (Buffer != NULL)
43 free(Buffer);
6c55f07a 44 Buffer = NULL;
3d8232bf 45 Fd = pFd;
55153bf9 46 Flags = pFlags;
6c55f07a
DK
47 Start = NULL;
48 End = NULL;
49 Done = false;
50 iOffset = 0;
51 Size = pSize;
55153bf9
DK
52 isCommentedLine = false;
53 chunks.clear();
1abbce9e 54 }
6c55f07a 55
55153bf9 56 pkgTagFilePrivate(FileFd * const pFd, unsigned long long const Size, pkgTagFile::Flags const pFlags) : Buffer(NULL)
6c55f07a 57 {
55153bf9 58 Reset(pFd, Size, pFlags);
6c55f07a
DK
59 }
60 FileFd * Fd;
55153bf9 61 pkgTagFile::Flags Flags;
1abbce9e
MV
62 char *Buffer;
63 char *Start;
64 char *End;
65 bool Done;
650faab0
DK
66 unsigned long long iOffset;
67 unsigned long long Size;
55153bf9
DK
68 bool isCommentedLine;
69 struct FileChunk
70 {
71 bool const good;
72 size_t length;
73 FileChunk(bool const pgood, size_t const plength) : good(pgood), length(plength) {}
74 };
75 std::list<FileChunk> chunks;
3d8232bf
DK
76
77 ~pkgTagFilePrivate()
78 {
79 if (Buffer != NULL)
80 free(Buffer);
81 }
1abbce9e 82};
55153bf9
DK
83 /*}}}*/
84class APT_HIDDEN pkgTagSectionPrivate /*{{{*/
bc4ccfeb
DK
85{
86public:
87 pkgTagSectionPrivate()
88 {
89 }
90 struct TagData {
91 unsigned int StartTag;
92 unsigned int EndTag;
93 unsigned int StartValue;
94 unsigned int NextInBucket;
95
e8afd168 96 explicit TagData(unsigned int const StartTag) : StartTag(StartTag), EndTag(0), StartValue(0), NextInBucket(0) {}
bc4ccfeb
DK
97 };
98 std::vector<TagData> Tags;
99};
55153bf9 100 /*}}}*/
bc4ccfeb 101
3e633069 102static unsigned long BetaHash(const char *Text, size_t Length) /*{{{*/
8710a36a
DK
103{
104 /* This very simple hash function for the last 8 letters gives
105 very good performance on the debian package files */
106 if (Length > 8)
107 {
108 Text += (Length - 8);
109 Length = 8;
110 }
111 unsigned long Res = 0;
112 for (size_t i = 0; i < Length; ++i)
113 Res = ((unsigned long)(Text[i]) & 0xDF) ^ (Res << 1);
3e633069 114 return Res & 0x7F;
8710a36a
DK
115}
116 /*}}}*/
117
578bfd0a 118// TagFile::pkgTagFile - Constructor /*{{{*/
55153bf9
DK
119pkgTagFile::pkgTagFile(FileFd * const pFd,pkgTagFile::Flags const pFlags, unsigned long long const Size)
120 : d(new pkgTagFilePrivate(pFd, Size + 4, pFlags))
121{
122 Init(pFd, pFlags, Size);
123}
6c55f07a 124pkgTagFile::pkgTagFile(FileFd * const pFd,unsigned long long const Size)
55153bf9 125 : pkgTagFile(pFd, pkgTagFile::STRICT, Size)
feab34c5 126{
feab34c5 127}
55153bf9 128void pkgTagFile::Init(FileFd * const pFd, pkgTagFile::Flags const pFlags, unsigned long long Size)
578bfd0a 129{
0aae6d14
DK
130 /* The size is increased by 4 because if we start with the Size of the
131 filename we need to try to read 1 char more to see an EOF faster, 1
132 char the end-pointer can be on and maybe 2 newlines need to be added
133 to the end of the file -> 4 extra chars */
134 Size += 4;
55153bf9 135 d->Reset(pFd, Size, pFlags);
1abbce9e 136
6c55f07a 137 if (d->Fd->IsOpen() == false)
1abbce9e 138 d->Start = d->End = d->Buffer = 0;
4b2803b8
DK
139 else
140 d->Buffer = (char*)malloc(sizeof(char) * Size);
141
142 if (d->Buffer == NULL)
1abbce9e 143 d->Done = true;
4b2803b8
DK
144 else
145 d->Done = false;
146
1abbce9e 147 d->Start = d->End = d->Buffer;
1abbce9e 148 d->iOffset = 0;
4b2803b8
DK
149 if (d->Done == false)
150 Fill();
55153bf9
DK
151}
152void pkgTagFile::Init(FileFd * const pFd,unsigned long long Size)
153{
154 Init(pFd, pkgTagFile::STRICT, Size);
578bfd0a
AL
155}
156 /*}}}*/
b2e465d6 157// TagFile::~pkgTagFile - Destructor /*{{{*/
29f7b36c
AL
158pkgTagFile::~pkgTagFile()
159{
1abbce9e
MV
160 delete d;
161}
162 /*}}}*/
4b2803b8 163// TagFile::Offset - Return the current offset in the buffer /*{{{*/
a02db58f 164APT_PURE unsigned long pkgTagFile::Offset()
1abbce9e
MV
165{
166 return d->iOffset;
29f7b36c
AL
167}
168 /*}}}*/
75c541fd 169// TagFile::Resize - Resize the internal buffer /*{{{*/
578bfd0a 170// ---------------------------------------------------------------------
75c541fd
MV
171/* Resize the internal buffer (double it in size). Fail if a maximum size
172 * size is reached.
173 */
174bool pkgTagFile::Resize()
578bfd0a 175{
75c541fd 176 // fail is the buffer grows too big
1abbce9e 177 if(d->Size > 1024*1024+1)
432b168c
MV
178 return false;
179
0aae6d14
DK
180 return Resize(d->Size * 2);
181}
182bool pkgTagFile::Resize(unsigned long long const newSize)
183{
184 unsigned long long const EndSize = d->End - d->Start;
0aae6d14 185
75c541fd 186 // get new buffer and use it
55153bf9 187 char* const newBuffer = static_cast<char*>(realloc(d->Buffer, sizeof(char) * newSize));
4b2803b8
DK
188 if (newBuffer == NULL)
189 return false;
190 d->Buffer = newBuffer;
0aae6d14 191 d->Size = newSize;
99c2e5ac 192
75c541fd 193 // update the start/end pointers to the new buffer
1abbce9e
MV
194 d->Start = d->Buffer;
195 d->End = d->Start + EndSize;
75c541fd
MV
196 return true;
197}
81e9789b 198 /*}}}*/
578bfd0a
AL
199// TagFile::Step - Advance to the next section /*{{{*/
200// ---------------------------------------------------------------------
75c541fd
MV
201/* If the Section Scanner fails we refill the buffer and try again.
202 * If that fails too, double the buffer size and try again until a
203 * maximum buffer is reached.
204 */
578bfd0a
AL
205bool pkgTagFile::Step(pkgTagSection &Tag)
206{
8710a36a 207 if(Tag.Scan(d->Start,d->End - d->Start) == false)
0852eaef 208 {
8710a36a
DK
209 do
210 {
211 if (Fill() == false)
212 return false;
213
214 if(Tag.Scan(d->Start,d->End - d->Start, false))
215 break;
75c541fd 216
8710a36a 217 if (Resize() == false)
2f6a2fbb 218 return _error->Error(_("Unable to parse package file %s (%d)"),
6c55f07a 219 d->Fd->Name().c_str(), 1);
8710a36a
DK
220
221 } while (Tag.Scan(d->Start,d->End - d->Start, false) == false);
613f9499 222 }
8710a36a 223
55153bf9
DK
224 size_t tagSize = Tag.size();
225 d->Start += tagSize;
226
227 if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) == 0)
228 d->iOffset += tagSize;
229 else
230 {
231 auto first = d->chunks.begin();
232 for (; first != d->chunks.end(); ++first)
233 {
234 if (first->good == false)
235 d->iOffset += first->length;
236 else
237 {
238 if (tagSize < first->length)
239 {
240 first->length -= tagSize;
241 d->iOffset += tagSize;
242 break;
243 }
244 else
245 {
246 tagSize -= first->length;
247 d->iOffset += first->length;
248 }
249 }
250 }
251 d->chunks.erase(d->chunks.begin(), first);
252 }
b2e465d6
AL
253
254 Tag.Trim();
99c2e5ac
MV
255 return true;
256}
257 /*}}}*/
258// TagFile::Fill - Top up the buffer /*{{{*/
259// ---------------------------------------------------------------------
260/* This takes the bit at the end of the buffer and puts it at the start
261 then fills the rest from the file */
55153bf9
DK
262static bool FillBuffer(pkgTagFilePrivate * const d)
263{
264 unsigned long long Actual = 0;
265 // See if only a bit of the file is left
266 unsigned long long const dataSize = d->Size - ((d->End - d->Buffer) + 1);
267 if (d->Fd->Read(d->End, dataSize, &Actual) == false)
268 return false;
269 if (Actual != dataSize)
270 d->Done = true;
271 d->End += Actual;
272 return true;
273}
274static void RemoveCommentsFromBuffer(pkgTagFilePrivate * const d)
275{
276 // look for valid comments in the buffer
277 char * good_start = nullptr, * bad_start = nullptr;
278 char * current = d->Start;
279 if (d->isCommentedLine == false)
280 {
281 if (d->Start == d->Buffer)
282 {
283 // the start of the buffer is a newline as a record can't start
284 // in the middle of a line by definition.
285 if (*d->Start == '#')
286 {
287 d->isCommentedLine = true;
288 ++current;
289 if (current > d->End)
290 d->chunks.emplace_back(false, 1);
291 }
292 }
293 if (d->isCommentedLine == false)
294 good_start = d->Start;
295 else
296 bad_start = d->Start;
297 }
298 else
299 bad_start = d->Start;
300
301 std::vector<std::pair<char*, size_t>> good_parts;
302 while (current <= d->End)
303 {
923c592c 304 size_t const restLength = (d->End - current);
55153bf9
DK
305 if (d->isCommentedLine == false)
306 {
307 current = static_cast<char*>(memchr(current, '#', restLength));
308 if (current == nullptr)
309 {
310 size_t const goodLength = d->End - good_start;
311 d->chunks.emplace_back(true, goodLength);
312 if (good_start != d->Start)
313 good_parts.push_back(std::make_pair(good_start, goodLength));
314 break;
315 }
316 bad_start = current;
317 --current;
318 // ensure that this is really a comment and not a '#' in the middle of a line
319 if (*current == '\n')
320 {
321 size_t const goodLength = (current - good_start) + 1;
322 d->chunks.emplace_back(true, goodLength);
323 good_parts.push_back(std::make_pair(good_start, goodLength));
324 good_start = nullptr;
325 d->isCommentedLine = true;
326 }
327 current += 2;
328 }
329 else // the current line is a comment
330 {
331 current = static_cast<char*>(memchr(current, '\n', restLength));
332 if (current == nullptr)
333 {
334 d->chunks.emplace_back(false, (d->End - bad_start));
335 break;
336 }
337 ++current;
338 // is the next line a comment, too?
923c592c 339 if (current >= d->End || *current != '#')
55153bf9
DK
340 {
341 d->chunks.emplace_back(false, (current - bad_start));
342 good_start = current;
343 bad_start = nullptr;
344 d->isCommentedLine = false;
345 }
346 ++current;
347 }
348 }
349
350 if (good_parts.empty() == false)
351 {
352 // we found comments, so move later parts over them
353 current = d->Start;
354 for (auto const &good: good_parts)
355 {
356 memmove(current, good.first, good.second);
357 current += good.second;
358 }
359 d->End = current;
360 }
361
362 if (d->isCommentedLine == true)
363 {
364 // deal with a buffer containing only comments
365 // or an (unfinished) comment at the end
366 if (good_parts.empty() == true)
367 d->End = d->Start;
368 else
369 d->Start = d->End;
370 }
371 else
372 {
373 // the buffer was all comment, but ended with the buffer
374 if (good_parts.empty() == true && good_start >= d->End)
375 d->End = d->Start;
376 else
377 d->Start = d->End;
378 }
379}
99c2e5ac
MV
380bool pkgTagFile::Fill()
381{
55153bf9
DK
382 unsigned long long const EndSize = d->End - d->Start;
383 if (EndSize != 0)
384 {
385 memmove(d->Buffer,d->Start,EndSize);
386 d->Start = d->End = d->Buffer + EndSize;
387 }
388 else
389 d->Start = d->End = d->Buffer;
390
650faab0 391 unsigned long long Actual = 0;
55153bf9 392 while (d->Done == false && d->Size > (Actual + 1))
99c2e5ac 393 {
55153bf9 394 if (FillBuffer(d) == false)
99c2e5ac 395 return false;
55153bf9
DK
396 if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) != 0)
397 RemoveCommentsFromBuffer(d);
398 Actual = d->End - d->Buffer;
99c2e5ac 399 }
55153bf9
DK
400 d->Start = d->Buffer;
401
1abbce9e 402 if (d->Done == true)
99c2e5ac
MV
403 {
404 if (EndSize <= 3 && Actual == 0)
405 return false;
1abbce9e 406 if (d->Size - (d->End - d->Buffer) < 4)
99c2e5ac 407 return true;
55153bf9 408
99c2e5ac
MV
409 // Append a double new line if one does not exist
410 unsigned int LineCount = 0;
1abbce9e 411 for (const char *E = d->End - 1; E - d->End < 6 && (*E == '\n' || *E == '\r'); E--)
99c2e5ac 412 if (*E == '\n')
55153bf9 413 ++LineCount;
0aae6d14
DK
414 if (LineCount < 2)
415 {
55153bf9 416 if (static_cast<unsigned long long>(d->End - d->Buffer) >= d->Size)
0aae6d14 417 Resize(d->Size + 3);
55153bf9 418 for (; LineCount < 2; ++LineCount)
0aae6d14
DK
419 *d->End++ = '\n';
420 }
99c2e5ac 421 }
578bfd0a
AL
422 return true;
423}
424 /*}}}*/
ad00ae81
AL
425// TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
426// ---------------------------------------------------------------------
03e39e59
AL
427/* This jumps to a pre-recorded file location and reads the record
428 that is there */
650faab0 429bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
ad00ae81 430{
55153bf9 431 if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) == 0 &&
b2e465d6 432 // We are within a buffer space of the next hit..
55153bf9 433 Offset >= d->iOffset && d->iOffset + (d->End - d->Start) > Offset)
b2e465d6 434 {
650faab0 435 unsigned long long Dist = Offset - d->iOffset;
1abbce9e
MV
436 d->Start += Dist;
437 d->iOffset += Dist;
e62aa1dd
DK
438 // if we have seen the end, don't ask for more
439 if (d->Done == true)
440 return Tag.Scan(d->Start, d->End - d->Start);
441 else
442 return Step(Tag);
b2e465d6
AL
443 }
444
2ca99a0d 445 // Reposition and reload..
1abbce9e
MV
446 d->iOffset = Offset;
447 d->Done = false;
6c55f07a 448 if (d->Fd->Seek(Offset) == false)
2ca99a0d 449 return false;
1abbce9e 450 d->End = d->Start = d->Buffer;
55153bf9
DK
451 d->isCommentedLine = false;
452 d->chunks.clear();
453
2ca99a0d
MV
454 if (Fill() == false)
455 return false;
99c2e5ac 456
1abbce9e 457 if (Tag.Scan(d->Start, d->End - d->Start) == true)
2ca99a0d
MV
458 return true;
459
460 // This appends a double new line (for the real eof handling)
461 if (Fill() == false)
462 return false;
0852eaef 463
8710a36a 464 if (Tag.Scan(d->Start, d->End - d->Start, false) == false)
6c55f07a 465 return _error->Error(_("Unable to parse package file %s (%d)"),d->Fd->Name().c_str(), 2);
06bba740 466
ad00ae81
AL
467 return true;
468}
469 /*}}}*/
b40394c0
MV
470// pkgTagSection::pkgTagSection - Constructor /*{{{*/
471// ---------------------------------------------------------------------
472/* */
bc4ccfeb 473APT_IGNORE_DEPRECATED_PUSH
b40394c0 474pkgTagSection::pkgTagSection()
6c55f07a 475 : Section(0), d(new pkgTagSectionPrivate()), Stop(0)
b40394c0 476{
bc4ccfeb 477 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
3e633069 478 memset(&BetaIndexes, 0, sizeof(BetaIndexes));
b40394c0 479}
bc4ccfeb 480APT_IGNORE_DEPRECATED_POP
b40394c0 481 /*}}}*/
578bfd0a 482// TagSection::Scan - Scan for the end of the header information /*{{{*/
8710a36a 483bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const Restart)
578bfd0a 484{
8710a36a 485 Section = Start;
578bfd0a 486 const char *End = Start + MaxLength;
8710a36a 487
bc4ccfeb 488 if (Restart == false && d->Tags.empty() == false)
8710a36a 489 {
bc4ccfeb 490 Stop = Section + d->Tags.back().StartTag;
8710a36a
DK
491 if (End <= Stop)
492 return false;
493 Stop = (const char *)memchr(Stop,'\n',End - Stop);
494 if (Stop == NULL)
495 return false;
496 ++Stop;
497 }
498 else
499 {
500 Stop = Section;
bc4ccfeb 501 if (d->Tags.empty() == false)
8710a36a 502 {
bc4ccfeb 503 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
3e633069 504 memset(&BetaIndexes, 0, sizeof(BetaIndexes));
bc4ccfeb 505 d->Tags.clear();
8710a36a 506 }
bc4ccfeb 507 d->Tags.reserve(0x100);
8710a36a 508 }
bc4ccfeb 509 unsigned int TagCount = d->Tags.size();
c7b5ce1c 510
2ca99a0d 511 if (Stop == 0)
0852eaef 512 return false;
81e9789b 513
bc4ccfeb 514 pkgTagSectionPrivate::TagData lastTagData(0);
8710a36a 515 lastTagData.EndTag = 0;
abfd0770
JAK
516 Key lastTagKey = Key::Unknown;
517 unsigned int lastTagHash = 0;
8710a36a 518 while (Stop < End)
578bfd0a 519 {
75ab11ae
MV
520 TrimRecord(true,End);
521
522 // this can happen when TrimRecord trims away the entire Record
523 // (e.g. because it just contains comments)
524 if(Stop == End)
525 return true;
81e9789b 526
90d64280 527 // Start a new index and add it to the hash
74dedb4a 528 if (isspace_ascii(Stop[0]) == 0)
c1a22377 529 {
8710a36a
DK
530 // store the last found tag
531 if (lastTagData.EndTag != 0)
532 {
abfd0770
JAK
533 if (lastTagKey != Key::Unknown) {
534 AlphaIndexes[static_cast<size_t>(lastTagKey)] = TagCount;
535 } else {
536 if (BetaIndexes[lastTagHash] != 0)
537 lastTagData.NextInBucket = BetaIndexes[lastTagHash];
538 APT_IGNORE_DEPRECATED_PUSH
539 BetaIndexes[lastTagHash] = TagCount;
540 APT_IGNORE_DEPRECATED_POP
541 }
bc4ccfeb 542 d->Tags.push_back(lastTagData);
8710a36a
DK
543 }
544
bc4ccfeb
DK
545 APT_IGNORE_DEPRECATED(++TagCount;)
546 lastTagData = pkgTagSectionPrivate::TagData(Stop - Section);
8710a36a
DK
547 // find the colon separating tag and value
548 char const * Colon = (char const *) memchr(Stop, ':', End - Stop);
549 if (Colon == NULL)
550 return false;
551 // find the end of the tag (which might or might not be the colon)
552 char const * EndTag = Colon;
553 --EndTag;
74dedb4a 554 for (; EndTag > Stop && isspace_ascii(*EndTag) != 0; --EndTag)
8710a36a
DK
555 ;
556 ++EndTag;
557 lastTagData.EndTag = EndTag - Section;
abfd0770
JAK
558 lastTagKey = pkgTagHash(Stop, EndTag - Stop);
559 if (lastTagKey == Key::Unknown)
560 lastTagHash = BetaHash(Stop, EndTag - Stop);
8710a36a
DK
561 // find the beginning of the value
562 Stop = Colon + 1;
67c90775 563 for (; Stop < End && isspace_ascii(*Stop) != 0; ++Stop)
c72f5c4f
DK
564 if (*Stop == '\n' && Stop[1] != ' ')
565 break;
8710a36a
DK
566 if (Stop >= End)
567 return false;
568 lastTagData.StartValue = Stop - Section;
c1a22377 569 }
0a8e3465 570
c1a22377 571 Stop = (const char *)memchr(Stop,'\n',End - Stop);
8710a36a 572
c1a22377 573 if (Stop == 0)
0852eaef 574 return false;
81e9789b 575
75ab11ae
MV
576 for (; Stop+1 < End && Stop[1] == '\r'; Stop++)
577 /* nothing */
578 ;
c1a22377 579
f3bcc383
AL
580 // Double newline marks the end of the record
581 if (Stop+1 < End && Stop[1] == '\n')
578bfd0a 582 {
8710a36a
DK
583 if (lastTagData.EndTag != 0)
584 {
abfd0770
JAK
585 if (lastTagKey != Key::Unknown) {
586 AlphaIndexes[static_cast<size_t>(lastTagKey)] = TagCount;
587 } else {
588 if (BetaIndexes[lastTagHash] != 0)
589 lastTagData.NextInBucket = BetaIndexes[lastTagHash];
590 APT_IGNORE_DEPRECATED(BetaIndexes[lastTagHash] = TagCount;)
591 }
bc4ccfeb 592 d->Tags.push_back(lastTagData);
8710a36a
DK
593 }
594
bc4ccfeb 595 pkgTagSectionPrivate::TagData const td(Stop - Section);
bc4ccfeb 596 d->Tags.push_back(td);
81e9789b 597 TrimRecord(false,End);
0852eaef 598 return true;
578bfd0a
AL
599 }
600
c1a22377
AL
601 Stop++;
602 }
138d4b3d 603
0852eaef 604 return false;
578bfd0a
AL
605}
606 /*}}}*/
81e9789b
MV
607// TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
608// ---------------------------------------------------------------------
609/* There should be exactly 2 newline at the end of the record, no more. */
610void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
611{
612 if (BeforeRecord == true)
613 return;
614 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
615}
616 /*}}}*/
b2e465d6
AL
617// TagSection::Trim - Trim off any trailing garbage /*{{{*/
618// ---------------------------------------------------------------------
619/* There should be exactly 1 newline at the end of the buffer, no more. */
620void pkgTagSection::Trim()
621{
622 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
623}
624 /*}}}*/
8710a36a 625// TagSection::Exists - return True if a tag exists /*{{{*/
eff0c22e 626bool pkgTagSection::Exists(StringView Tag) const
c8b860fb
MV
627{
628 unsigned int tmp;
629 return Find(Tag, tmp);
630}
631 /*}}}*/
578bfd0a
AL
632// TagSection::Find - Locate a tag /*{{{*/
633// ---------------------------------------------------------------------
634/* This searches the section for a tag that matches the given string. */
abfd0770
JAK
635bool pkgTagSection::Find(Key key,unsigned int &Pos) const
636{
637 auto Bucket = AlphaIndexes[static_cast<size_t>(key)];
638 Pos = Bucket - 1;
639 return Bucket != 0;
640}
eff0c22e 641bool pkgTagSection::Find(StringView TagView,unsigned int &Pos) const
578bfd0a 642{
eff0c22e
JAK
643 const char * const Tag = TagView.data();
644 size_t const Length = TagView.length();
abfd0770
JAK
645 auto key = pkgTagHash(Tag, Length);
646 if (key != Key::Unknown)
647 return Find(key, Pos);
648
3e633069 649 unsigned int Bucket = BetaIndexes[BetaHash(Tag, Length)];
8710a36a 650 if (Bucket == 0)
c1a22377 651 return false;
8710a36a 652
bc4ccfeb 653 for (; Bucket != 0; Bucket = d->Tags[Bucket - 1].NextInBucket)
578bfd0a 654 {
bc4ccfeb 655 if ((d->Tags[Bucket - 1].EndTag - d->Tags[Bucket - 1].StartTag) != Length)
578bfd0a
AL
656 continue;
657
bc4ccfeb 658 char const * const St = Section + d->Tags[Bucket - 1].StartTag;
8710a36a 659 if (strncasecmp(Tag,St,Length) != 0)
b2e465d6 660 continue;
8710a36a
DK
661
662 Pos = Bucket - 1;
b2e465d6
AL
663 return true;
664 }
665
666 Pos = 0;
667 return false;
668}
eff0c22e 669
45ecab44 670bool pkgTagSection::FindInternal(unsigned int Pos, const char *&Start,
b2e465d6
AL
671 const char *&End) const
672{
bc4ccfeb 673 Start = Section + d->Tags[Pos].StartValue;
8710a36a 674 // Strip off the gunk from the end
bc4ccfeb 675 End = Section + d->Tags[Pos + 1].StartTag;
8710a36a
DK
676 if (unlikely(Start > End))
677 return _error->Error("Internal parsing error");
678
74dedb4a 679 for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
8710a36a
DK
680
681 return true;
45ecab44
JAK
682}
683bool pkgTagSection::Find(StringView Tag,const char *&Start,
684 const char *&End) const
685{
686 unsigned int Pos;
687 return Find(Tag, Pos) && FindInternal(Pos, Start, End);
abfd0770
JAK
688}
689bool pkgTagSection::Find(Key key,const char *&Start,
690 const char *&End) const
691{
692 unsigned int Pos;
693 return Find(key, Pos) && FindInternal(Pos, Start, End);
578bfd0a
AL
694}
695 /*}}}*/
0e66b144 696// TagSection::FindS - Find a string /*{{{*/
eff0c22e 697StringView pkgTagSection::Find(StringView Tag) const
a05599f1
AL
698{
699 const char *Start;
700 const char *End;
701 if (Find(Tag,Start,End) == false)
eff0c22e
JAK
702 return StringView();
703 return StringView(Start, End - Start);
abfd0770
JAK
704}
705StringView pkgTagSection::Find(Key key) const
706{
707 const char *Start;
708 const char *End;
709 if (Find(key,Start,End) == false)
710 return StringView();
711 return StringView(Start, End - Start);
a05599f1
AL
712}
713 /*}}}*/
8d058ea5 714// TagSection::FindRawS - Find a string /*{{{*/
45ecab44 715StringView pkgTagSection::FindRawInternal(unsigned int Pos) const
8d058ea5 716{
8d058ea5
DK
717 char const *Start = (char const *) memchr(Section + d->Tags[Pos].EndTag, ':', d->Tags[Pos].StartValue - d->Tags[Pos].EndTag);
718 ++Start;
719 char const *End = Section + d->Tags[Pos + 1].StartTag;
720 if (unlikely(Start > End))
721 return "";
722
74dedb4a 723 for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
8d058ea5 724
eff0c22e 725 return StringView(Start, End - Start);
45ecab44
JAK
726}
727StringView pkgTagSection::FindRaw(StringView Tag) const
728{
729 unsigned int Pos;
730 return Find(Tag, Pos) ? FindRawInternal(Pos) : "";
abfd0770
JAK
731}
732StringView pkgTagSection::FindRaw(Key key) const
733{
734 unsigned int Pos;
735 return Find(key, Pos) ? FindRawInternal(Pos) : "";
8d058ea5
DK
736}
737 /*}}}*/
a05599f1
AL
738// TagSection::FindI - Find an integer /*{{{*/
739// ---------------------------------------------------------------------
740/* */
45ecab44 741signed int pkgTagSection::FindIInternal(unsigned int Pos,signed long Default) const
a05599f1
AL
742{
743 const char *Start;
b0b4efb9 744 const char *Stop;
45ecab44 745 if (FindInternal(Pos,Start,Stop) == false)
b0b4efb9
AL
746 return Default;
747
748 // Copy it into a temp buffer so we can use strtol
749 char S[300];
750 if ((unsigned)(Stop - Start) >= sizeof(S))
751 return Default;
752 strncpy(S,Start,Stop-Start);
753 S[Stop - Start] = 0;
809aa216
JAK
754
755 errno = 0;
b0b4efb9
AL
756 char *End;
757 signed long Result = strtol(S,&End,10);
137e8ad4
JAK
758 if (errno == ERANGE ||
759 Result < std::numeric_limits<int>::min() || Result > std::numeric_limits<int>::max()) {
809aa216 760 errno = ERANGE;
137e8ad4 761 _error->Error(_("Cannot convert %s to integer: out of range"), S);
809aa216 762 }
b0b4efb9
AL
763 if (S == End)
764 return Default;
765 return Result;
45ecab44 766}
abfd0770
JAK
767signed int pkgTagSection::FindI(Key key,signed long Default) const
768{
769 unsigned int Pos;
770
771 return Find(key, Pos) ? FindIInternal(Pos) : Default;
772}
45ecab44
JAK
773signed int pkgTagSection::FindI(StringView Tag,signed long Default) const
774{
775 unsigned int Pos;
776
777 return Find(Tag, Pos) ? FindIInternal(Pos, Default) : Default;
b0b4efb9
AL
778}
779 /*}}}*/
e2c66de5
DK
780// TagSection::FindULL - Find an unsigned long long integer /*{{{*/
781// ---------------------------------------------------------------------
782/* */
45ecab44 783unsigned long long pkgTagSection::FindULLInternal(unsigned int Pos, unsigned long long const &Default) const
e2c66de5
DK
784{
785 const char *Start;
786 const char *Stop;
45ecab44 787 if (FindInternal(Pos,Start,Stop) == false)
e2c66de5
DK
788 return Default;
789
790 // Copy it into a temp buffer so we can use strtoull
791 char S[100];
792 if ((unsigned)(Stop - Start) >= sizeof(S))
793 return Default;
794 strncpy(S,Start,Stop-Start);
795 S[Stop - Start] = 0;
796
797 char *End;
798 unsigned long long Result = strtoull(S,&End,10);
799 if (S == End)
800 return Default;
801 return Result;
45ecab44 802}
abfd0770
JAK
803unsigned long long pkgTagSection::FindULL(Key key, unsigned long long const &Default) const
804{
805 unsigned int Pos;
806
807 return Find(key, Pos) ? FindULLInternal(Pos, Default) : Default;
808}
45ecab44
JAK
809unsigned long long pkgTagSection::FindULL(StringView Tag, unsigned long long const &Default) const
810{
811 unsigned int Pos;
812
813 return Find(Tag, Pos) ? FindULLInternal(Pos, Default) : Default;
e2c66de5
DK
814}
815 /*}}}*/
a2fdb57f
MV
816// TagSection::FindB - Find boolean value /*{{{*/
817// ---------------------------------------------------------------------
818/* */
45ecab44 819bool pkgTagSection::FindBInternal(unsigned int Pos, bool Default) const
a2fdb57f
MV
820{
821 const char *Start, *Stop;
45ecab44 822 if (FindInternal(Pos, Start, Stop) == false)
a2fdb57f
MV
823 return Default;
824 return StringToBool(string(Start, Stop));
45ecab44 825}
abfd0770
JAK
826bool pkgTagSection::FindB(Key key, bool Default) const
827{
828 unsigned int Pos;
829 return Find(key, Pos) ? FindBInternal(Pos, Default): Default;
830}
45ecab44
JAK
831bool pkgTagSection::FindB(StringView Tag, bool Default) const
832{
833 unsigned int Pos;
834 return Find(Tag, Pos) ? FindBInternal(Pos, Default) : Default;
a2fdb57f
MV
835}
836 /*}}}*/
b0b4efb9
AL
837// TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
838// ---------------------------------------------------------------------
839/* The bits marked in Flag are masked on/off in Flags */
45ecab44 840bool pkgTagSection::FindFlagInternal(unsigned int Pos, uint8_t &Flags,
dfe66c72
DK
841 uint8_t const Flag) const
842{
843 const char *Start;
844 const char *Stop;
45ecab44 845 if (FindInternal(Pos,Start,Stop) == false)
dfe66c72
DK
846 return true;
847 return FindFlag(Flags, Flag, Start, Stop);
848}
abfd0770
JAK
849bool pkgTagSection::FindFlag(Key key, uint8_t &Flags,
850 uint8_t const Flag) const
851{
852 unsigned int Pos;
853 if (Find(key,Pos) == false)
854 return true;
855 return FindFlagInternal(Pos, Flags, Flag);
856}
45ecab44
JAK
857bool pkgTagSection::FindFlag(StringView Tag, uint8_t &Flags,
858 uint8_t const Flag) const
859{
860 unsigned int Pos;
861 if (Find(Tag,Pos) == false)
862 return true;
863 return FindFlagInternal(Pos, Flags, Flag);
864}
dfe66c72
DK
865bool pkgTagSection::FindFlag(uint8_t &Flags, uint8_t const Flag,
866 char const* const Start, char const* const Stop)
867{
868 switch (StringToBool(string(Start, Stop)))
869 {
870 case 0:
871 Flags &= ~Flag;
872 return true;
873
874 case 1:
875 Flags |= Flag;
876 return true;
877
878 default:
879 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
880 return true;
881 }
882 return true;
883}
45ecab44 884bool pkgTagSection::FindFlagInternal(unsigned int Pos,unsigned long &Flags,
b2e465d6 885 unsigned long Flag) const
b0b4efb9
AL
886{
887 const char *Start;
888 const char *Stop;
45ecab44 889 if (FindInternal(Pos,Start,Stop) == false)
b0b4efb9 890 return true;
fe0f7911
DK
891 return FindFlag(Flags, Flag, Start, Stop);
892}
abfd0770
JAK
893bool pkgTagSection::FindFlag(Key key,unsigned long &Flags,
894 unsigned long Flag) const
895{
896 unsigned int Pos;
897 return Find(key, Pos) ? FindFlagInternal(Pos, Flags, Flag) : true;
898}
45ecab44
JAK
899bool pkgTagSection::FindFlag(StringView Tag,unsigned long &Flags,
900 unsigned long Flag) const
901{
902 unsigned int Pos;
903 return Find(Tag, Pos) ? FindFlagInternal(Pos, Flags, Flag) : true;
904}
d64e130a 905bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
fe0f7911
DK
906 char const* Start, char const* Stop)
907{
908 switch (StringToBool(string(Start, Stop)))
909 {
b0b4efb9
AL
910 case 0:
911 Flags &= ~Flag;
912 return true;
913
914 case 1:
915 Flags |= Flag;
916 return true;
917
918 default:
b2e465d6 919 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
b0b4efb9
AL
920 return true;
921 }
922 return true;
a05599f1
AL
923}
924 /*}}}*/
55153bf9 925void pkgTagSection::Get(const char *&Start,const char *&Stop,unsigned int I) const/*{{{*/
bc4ccfeb
DK
926{
927 Start = Section + d->Tags[I].StartTag;
928 Stop = Section + d->Tags[I+1].StartTag;
929}
55153bf9 930 /*}}}*/
8710a36a 931APT_PURE unsigned int pkgTagSection::Count() const { /*{{{*/
bc4ccfeb 932 if (d->Tags.empty() == true)
8710a36a
DK
933 return 0;
934 // the last element is just marking the end and isn't a real one
bc4ccfeb 935 return d->Tags.size() - 1;
8710a36a
DK
936}
937 /*}}}*/
8d058ea5
DK
938// TagSection::Write - Ordered (re)writing of fields /*{{{*/
939pkgTagSection::Tag pkgTagSection::Tag::Remove(std::string const &Name)
940{
941 return Tag(REMOVE, Name, "");
942}
943pkgTagSection::Tag pkgTagSection::Tag::Rename(std::string const &OldName, std::string const &NewName)
944{
945 return Tag(RENAME, OldName, NewName);
946}
947pkgTagSection::Tag pkgTagSection::Tag::Rewrite(std::string const &Name, std::string const &Data)
948{
949 if (Data.empty() == true)
950 return Tag(REMOVE, Name, "");
951 else
952 return Tag(REWRITE, Name, Data);
953}
eff0c22e 954static bool WriteTag(FileFd &File, std::string Tag, StringView Value)
8d058ea5 955{
74dedb4a 956 if (Value.empty() || isspace_ascii(Value[0]) != 0)
8d058ea5
DK
957 Tag.append(":");
958 else
959 Tag.append(": ");
eff0c22e 960 Tag.append(Value.data(), Value.length());
8d058ea5
DK
961 Tag.append("\n");
962 return File.Write(Tag.c_str(), Tag.length());
963}
964static bool RewriteTags(FileFd &File, pkgTagSection const * const This, char const * const Tag,
965 std::vector<pkgTagSection::Tag>::const_iterator &R,
966 std::vector<pkgTagSection::Tag>::const_iterator const &REnd)
967{
968 size_t const TagLen = strlen(Tag);
969 for (; R != REnd; ++R)
970 {
971 std::string data;
972 if (R->Name.length() == TagLen && strncasecmp(R->Name.c_str(), Tag, R->Name.length()) == 0)
973 {
974 if (R->Action != pkgTagSection::Tag::REWRITE)
975 break;
976 data = R->Data;
977 }
978 else if(R->Action == pkgTagSection::Tag::RENAME && R->Data.length() == TagLen &&
979 strncasecmp(R->Data.c_str(), Tag, R->Data.length()) == 0)
eff0c22e 980 data = This->FindRaw(R->Name.c_str()).to_string();
8d058ea5
DK
981 else
982 continue;
983
984 return WriteTag(File, Tag, data);
985 }
986 return true;
987}
988bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::vector<Tag> const &Rewrite) const
989{
990 // first pass: Write everything we have an order for
991 if (Order != NULL)
992 {
993 for (unsigned int I = 0; Order[I] != 0; ++I)
994 {
995 std::vector<Tag>::const_iterator R = Rewrite.begin();
996 if (RewriteTags(File, this, Order[I], R, Rewrite.end()) == false)
997 return false;
998 if (R != Rewrite.end())
999 continue;
1000
1001 if (Exists(Order[I]) == false)
1002 continue;
1003
eff0c22e 1004 if (WriteTag(File, Order[I], FindRaw(Order[I])) == false)
8d058ea5
DK
1005 return false;
1006 }
1007 }
1008 // second pass: See if we have tags which aren't ordered
1009 if (d->Tags.empty() == false)
1010 {
1011 for (std::vector<pkgTagSectionPrivate::TagData>::const_iterator T = d->Tags.begin(); T != d->Tags.end() - 1; ++T)
1012 {
1013 char const * const fieldname = Section + T->StartTag;
1014 size_t fieldnamelen = T->EndTag - T->StartTag;
1015 if (Order != NULL)
1016 {
1017 unsigned int I = 0;
1018 for (; Order[I] != 0; ++I)
1019 {
1020 if (fieldnamelen == strlen(Order[I]) && strncasecmp(fieldname, Order[I], fieldnamelen) == 0)
1021 break;
1022 }
1023 if (Order[I] != 0)
1024 continue;
1025 }
1026
1027 std::string const name(fieldname, fieldnamelen);
1028 std::vector<Tag>::const_iterator R = Rewrite.begin();
1029 if (RewriteTags(File, this, name.c_str(), R, Rewrite.end()) == false)
1030 return false;
1031 if (R != Rewrite.end())
1032 continue;
1033
eff0c22e 1034 if (WriteTag(File, name, FindRaw(name)) == false)
8d058ea5
DK
1035 return false;
1036 }
1037 }
1038 // last pass: see if there are any rewrites remaining we haven't done yet
1039 for (std::vector<Tag>::const_iterator R = Rewrite.begin(); R != Rewrite.end(); ++R)
1040 {
1041 if (R->Action == Tag::REMOVE)
1042 continue;
1043 std::string const name = ((R->Action == Tag::RENAME) ? R->Data : R->Name);
1044 if (Exists(name.c_str()))
1045 continue;
1046 if (Order != NULL)
1047 {
1048 unsigned int I = 0;
1049 for (; Order[I] != 0; ++I)
1050 {
1051 if (strncasecmp(name.c_str(), Order[I], name.length()) == 0 && name.length() == strlen(Order[I]))
1052 break;
1053 }
1054 if (Order[I] != 0)
1055 continue;
1056 }
1057
eff0c22e 1058 if (WriteTag(File, name, ((R->Action == Tag::RENAME) ? FindRaw(R->Name) : R->Data)) == false)
8d058ea5
DK
1059 return false;
1060 }
1061 return true;
1062}
1063 /*}}}*/
e8fb1cdf 1064
81460e32
DK
1065void pkgUserTagSection::TrimRecord(bool /*BeforeRecord*/, const char* &End)/*{{{*/
1066{
1067 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r' || Stop[0] == '#'); Stop++)
1068 if (Stop[0] == '#')
1069 Stop = (const char*) memchr(Stop,'\n',End-Stop);
1070}
1071 /*}}}*/
1072
e8fb1cdf
DK
1073#include "tagfile-order.c"
1074
b2e465d6
AL
1075// TFRewrite - Rewrite a control record /*{{{*/
1076// ---------------------------------------------------------------------
1077/* This writes the control record to stdout rewriting it as necessary. The
1078 override map item specificies the rewriting rules to follow. This also
1079 takes the time to sort the feild list. */
8d058ea5 1080APT_IGNORE_DEPRECATED_PUSH
b2e465d6
AL
1081bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
1082 TFRewriteData *Rewrite)
1083{
1084 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
1085 for (unsigned I = 0; I != 256; I++)
1086 Visited[I] = 0;
1087
1088 // Set new tag up as necessary.
1089 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1090 {
1091 if (Rewrite[J].NewTag == 0)
1092 Rewrite[J].NewTag = Rewrite[J].Tag;
1093 }
1094
1095 // Write all all of the tags, in order.
9e51c0b6 1096 if (Order != NULL)
b2e465d6 1097 {
9e51c0b6 1098 for (unsigned int I = 0; Order[I] != 0; I++)
b2e465d6 1099 {
9e51c0b6
MV
1100 bool Rewritten = false;
1101
1102 // See if this is a field that needs to be rewritten
1103 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1104 {
1105 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
1106 {
1107 Visited[J] |= 2;
1108 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
1109 {
74dedb4a 1110 if (isspace_ascii(Rewrite[J].Rewrite[0]))
9e51c0b6
MV
1111 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1112 else
1113 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1114 }
1115 Rewritten = true;
1116 break;
1117 }
1118 }
b2e465d6 1119
9e51c0b6
MV
1120 // See if it is in the fragment
1121 unsigned Pos;
eff0c22e 1122 if (Tags.Find(StringView(Order[I]),Pos) == false)
9e51c0b6
MV
1123 continue;
1124 Visited[Pos] |= 1;
1125
1126 if (Rewritten == true)
1127 continue;
b2e465d6 1128
9e51c0b6
MV
1129 /* Write out this element, taking a moment to rewrite the tag
1130 in case of changes of case. */
1131 const char *Start;
1132 const char *Stop;
1133 Tags.Get(Start,Stop,Pos);
b2e465d6 1134
9e51c0b6
MV
1135 if (fputs(Order[I],Output) < 0)
1136 return _error->Errno("fputs","IO Error to output");
1137 Start += strlen(Order[I]);
1138 if (fwrite(Start,Stop - Start,1,Output) != 1)
1139 return _error->Errno("fwrite","IO Error to output");
1140 if (Stop[-1] != '\n')
1141 fprintf(Output,"\n");
1142 }
1143 }
b2e465d6
AL
1144
1145 // Now write all the old tags that were missed.
1146 for (unsigned int I = 0; I != Tags.Count(); I++)
1147 {
1148 if ((Visited[I] & 1) == 1)
1149 continue;
1150
1151 const char *Start;
1152 const char *Stop;
1153 Tags.Get(Start,Stop,I);
1154 const char *End = Start;
1155 for (; End < Stop && *End != ':'; End++);
1156
1157 // See if this is a field that needs to be rewritten
1158 bool Rewritten = false;
1159 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1160 {
1161 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
1162 {
1163 Visited[J] |= 2;
1164 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
1165 {
74dedb4a 1166 if (isspace_ascii(Rewrite[J].Rewrite[0]))
b2e465d6
AL
1167 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1168 else
1169 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1170 }
1171
1172 Rewritten = true;
1173 break;
1174 }
1175 }
1176
1177 if (Rewritten == true)
1178 continue;
1179
1180 // Write out this element
1181 if (fwrite(Start,Stop - Start,1,Output) != 1)
1182 return _error->Errno("fwrite","IO Error to output");
1183 if (Stop[-1] != '\n')
1184 fprintf(Output,"\n");
1185 }
1186
1187 // Now write all the rewrites that were missed
1188 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1189 {
1190 if ((Visited[J] & 2) == 2)
1191 continue;
1192
1193 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
1194 {
74dedb4a 1195 if (isspace_ascii(Rewrite[J].Rewrite[0]))
b2e465d6
AL
1196 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1197 else
1198 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1199 }
1200 }
1201
1202 return true;
1203}
8d058ea5 1204APT_IGNORE_DEPRECATED_POP
b2e465d6 1205 /*}}}*/
862bafea 1206
bc4ccfeb 1207pkgTagSection::~pkgTagSection() { delete d; }