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