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