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