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