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