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