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