]> git.saurik.com Git - apt.git/blame - apt-pkg/tagfile.cc
move ByHash into its own function
[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 /*{{{*/
8710a36a 284bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const Restart)
578bfd0a 285{
8710a36a 286 Section = Start;
578bfd0a 287 const char *End = Start + MaxLength;
8710a36a
DK
288
289 if (Restart == false && Tags.empty() == false)
290 {
291 Stop = Section + Tags.back().StartTag;
292 if (End <= Stop)
293 return false;
294 Stop = (const char *)memchr(Stop,'\n',End - Stop);
295 if (Stop == NULL)
296 return false;
297 ++Stop;
298 }
299 else
300 {
301 Stop = Section;
302 if (Tags.empty() == false)
303 {
304 memset(&LookupTable, 0, sizeof(LookupTable));
305 Tags.clear();
306 }
307 Tags.reserve(0x100);
308 }
309 size_t TagCount = Tags.size();
c7b5ce1c 310
2ca99a0d 311 if (Stop == 0)
0852eaef 312 return false;
81e9789b 313
8710a36a
DK
314 TagData lastTagData(0);
315 lastTagData.EndTag = 0;
316 unsigned long lastTagHash = 0;
317 while (Stop < End)
578bfd0a 318 {
75ab11ae
MV
319 TrimRecord(true,End);
320
321 // this can happen when TrimRecord trims away the entire Record
322 // (e.g. because it just contains comments)
323 if(Stop == End)
324 return true;
81e9789b 325
90d64280 326 // Start a new index and add it to the hash
c1a22377
AL
327 if (isspace(Stop[0]) == 0)
328 {
8710a36a
DK
329 // store the last found tag
330 if (lastTagData.EndTag != 0)
331 {
332 if (LookupTable[lastTagHash] != 0)
333 lastTagData.NextInBucket = LookupTable[lastTagHash];
334 LookupTable[lastTagHash] = TagCount;
335 Tags.push_back(lastTagData);
336 }
337
338 ++TagCount;
339 lastTagData = TagData(Stop - Section);
340 // find the colon separating tag and value
341 char const * Colon = (char const *) memchr(Stop, ':', End - Stop);
342 if (Colon == NULL)
343 return false;
344 // find the end of the tag (which might or might not be the colon)
345 char const * EndTag = Colon;
346 --EndTag;
347 for (; EndTag > Stop && isspace(*EndTag) != 0; --EndTag)
348 ;
349 ++EndTag;
350 lastTagData.EndTag = EndTag - Section;
351 lastTagHash = AlphaHash(Stop, EndTag - Stop);
352 // find the beginning of the value
353 Stop = Colon + 1;
354 for (; isspace(*Stop) != 0; ++Stop);
355 if (Stop >= End)
356 return false;
357 lastTagData.StartValue = Stop - Section;
c1a22377 358 }
0a8e3465 359
c1a22377 360 Stop = (const char *)memchr(Stop,'\n',End - Stop);
8710a36a 361
c1a22377 362 if (Stop == 0)
0852eaef 363 return false;
81e9789b 364
75ab11ae
MV
365 for (; Stop+1 < End && Stop[1] == '\r'; Stop++)
366 /* nothing */
367 ;
c1a22377 368
f3bcc383
AL
369 // Double newline marks the end of the record
370 if (Stop+1 < End && Stop[1] == '\n')
578bfd0a 371 {
8710a36a
DK
372 if (lastTagData.EndTag != 0)
373 {
374 if (LookupTable[lastTagHash] != 0)
375 lastTagData.NextInBucket = LookupTable[lastTagHash];
376 LookupTable[lastTagHash] = TagCount;
377 Tags.push_back(lastTagData);
378 }
379
380 TagData const td(Stop - Section);
381 Tags.push_back(td);
81e9789b 382 TrimRecord(false,End);
0852eaef 383 return true;
578bfd0a
AL
384 }
385
c1a22377
AL
386 Stop++;
387 }
138d4b3d 388
0852eaef 389 return false;
578bfd0a
AL
390}
391 /*}}}*/
81e9789b
MV
392// TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
393// ---------------------------------------------------------------------
394/* There should be exactly 2 newline at the end of the record, no more. */
395void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
396{
397 if (BeforeRecord == true)
398 return;
399 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
400}
401 /*}}}*/
b2e465d6
AL
402// TagSection::Trim - Trim off any trailing garbage /*{{{*/
403// ---------------------------------------------------------------------
404/* There should be exactly 1 newline at the end of the buffer, no more. */
405void pkgTagSection::Trim()
406{
407 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
408}
409 /*}}}*/
8710a36a
DK
410// TagSection::Exists - return True if a tag exists /*{{{*/
411bool pkgTagSection::Exists(const char* const Tag) const
c8b860fb
MV
412{
413 unsigned int tmp;
414 return Find(Tag, tmp);
415}
416 /*}}}*/
578bfd0a
AL
417// TagSection::Find - Locate a tag /*{{{*/
418// ---------------------------------------------------------------------
419/* This searches the section for a tag that matches the given string. */
c8b860fb 420bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
578bfd0a 421{
8710a36a
DK
422 size_t const Length = strlen(Tag);
423 unsigned int Bucket = LookupTable[AlphaHash(Tag, Length)];
424 if (Bucket == 0)
c1a22377 425 return false;
8710a36a
DK
426
427 for (; Bucket != 0; Bucket = Tags[Bucket - 1].NextInBucket)
578bfd0a 428 {
8710a36a 429 if ((Tags[Bucket - 1].EndTag - Tags[Bucket - 1].StartTag) != Length)
578bfd0a
AL
430 continue;
431
8710a36a
DK
432 char const * const St = Section + Tags[Bucket - 1].StartTag;
433 if (strncasecmp(Tag,St,Length) != 0)
b2e465d6 434 continue;
8710a36a
DK
435
436 Pos = Bucket - 1;
b2e465d6
AL
437 return true;
438 }
439
440 Pos = 0;
441 return false;
442}
b2e465d6
AL
443bool pkgTagSection::Find(const char *Tag,const char *&Start,
444 const char *&End) const
445{
8710a36a
DK
446 unsigned int Pos;
447 if (Find(Tag, Pos) == false)
b2e465d6 448 return false;
578bfd0a 449
8710a36a
DK
450 Start = Section + Tags[Pos].StartValue;
451 // Strip off the gunk from the end
452 End = Section + Tags[Pos + 1].StartTag;
453 if (unlikely(Start > End))
454 return _error->Error("Internal parsing error");
455
456 for (; isspace(End[-1]) != 0 && End > Start; --End);
457
458 return true;
578bfd0a
AL
459}
460 /*}}}*/
0e66b144 461// TagSection::FindS - Find a string /*{{{*/
a05599f1
AL
462// ---------------------------------------------------------------------
463/* */
b2e465d6 464string pkgTagSection::FindS(const char *Tag) const
a05599f1
AL
465{
466 const char *Start;
467 const char *End;
468 if (Find(Tag,Start,End) == false)
469 return string();
470 return string(Start,End);
471}
472 /*}}}*/
473// TagSection::FindI - Find an integer /*{{{*/
474// ---------------------------------------------------------------------
475/* */
b2e465d6 476signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
a05599f1
AL
477{
478 const char *Start;
b0b4efb9
AL
479 const char *Stop;
480 if (Find(Tag,Start,Stop) == false)
481 return Default;
482
483 // Copy it into a temp buffer so we can use strtol
484 char S[300];
485 if ((unsigned)(Stop - Start) >= sizeof(S))
486 return Default;
487 strncpy(S,Start,Stop-Start);
488 S[Stop - Start] = 0;
489
490 char *End;
491 signed long Result = strtol(S,&End,10);
492 if (S == End)
493 return Default;
494 return Result;
495}
496 /*}}}*/
e2c66de5
DK
497// TagSection::FindULL - Find an unsigned long long integer /*{{{*/
498// ---------------------------------------------------------------------
499/* */
500unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long const &Default) const
501{
502 const char *Start;
503 const char *Stop;
504 if (Find(Tag,Start,Stop) == false)
505 return Default;
506
507 // Copy it into a temp buffer so we can use strtoull
508 char S[100];
509 if ((unsigned)(Stop - Start) >= sizeof(S))
510 return Default;
511 strncpy(S,Start,Stop-Start);
512 S[Stop - Start] = 0;
513
514 char *End;
515 unsigned long long Result = strtoull(S,&End,10);
516 if (S == End)
517 return Default;
518 return Result;
519}
520 /*}}}*/
a2fdb57f
MV
521// TagSection::FindB - Find boolean value /*{{{*/
522// ---------------------------------------------------------------------
523/* */
524bool pkgTagSection::FindB(const char *Tag, bool const &Default) const
525{
526 const char *Start, *Stop;
527 if (Find(Tag, Start, Stop) == false)
528 return Default;
529 return StringToBool(string(Start, Stop));
530}
531 /*}}}*/
b0b4efb9
AL
532// TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
533// ---------------------------------------------------------------------
534/* The bits marked in Flag are masked on/off in Flags */
535bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
b2e465d6 536 unsigned long Flag) const
b0b4efb9
AL
537{
538 const char *Start;
539 const char *Stop;
540 if (Find(Tag,Start,Stop) == false)
541 return true;
fe0f7911
DK
542 return FindFlag(Flags, Flag, Start, Stop);
543}
d64e130a 544bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
fe0f7911
DK
545 char const* Start, char const* Stop)
546{
547 switch (StringToBool(string(Start, Stop)))
548 {
b0b4efb9
AL
549 case 0:
550 Flags &= ~Flag;
551 return true;
552
553 case 1:
554 Flags |= Flag;
555 return true;
556
557 default:
b2e465d6 558 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
b0b4efb9
AL
559 return true;
560 }
561 return true;
a05599f1
AL
562}
563 /*}}}*/
8710a36a
DK
564APT_PURE unsigned int pkgTagSection::Count() const { /*{{{*/
565 if (Tags.empty() == true)
566 return 0;
567 // the last element is just marking the end and isn't a real one
568 return Tags.size() - 1;
569}
570 /*}}}*/
b2e465d6
AL
571// TFRewrite - Rewrite a control record /*{{{*/
572// ---------------------------------------------------------------------
573/* This writes the control record to stdout rewriting it as necessary. The
574 override map item specificies the rewriting rules to follow. This also
575 takes the time to sort the feild list. */
576
577/* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
578 array. */
579static const char *iTFRewritePackageOrder[] = {
580 "Package",
581 "Essential",
582 "Status",
583 "Priority",
584 "Section",
585 "Installed-Size",
586 "Maintainer",
47e7ebb3 587 "Original-Maintainer",
b2e465d6
AL
588 "Architecture",
589 "Source",
590 "Version",
591 "Revision", // Obsolete
592 "Config-Version", // Obsolete
593 "Replaces",
594 "Provides",
595 "Depends",
596 "Pre-Depends",
597 "Recommends",
598 "Suggests",
599 "Conflicts",
308c7d30 600 "Breaks",
b2e465d6
AL
601 "Conffiles",
602 "Filename",
603 "Size",
604 "MD5Sum",
cde41ae8
MV
605 "SHA1",
606 "SHA256",
d9b9e9e2 607 "SHA512",
b2e465d6
AL
608 "MSDOS-Filename", // Obsolete
609 "Description",
610 0};
611static const char *iTFRewriteSourceOrder[] = {"Package",
612 "Source",
613 "Binary",
614 "Version",
615 "Priority",
616 "Section",
617 "Maintainer",
47e7ebb3 618 "Original-Maintainer",
b2e465d6
AL
619 "Build-Depends",
620 "Build-Depends-Indep",
621 "Build-Conflicts",
622 "Build-Conflicts-Indep",
623 "Architecture",
624 "Standards-Version",
625 "Format",
626 "Directory",
627 "Files",
628 0};
629
630/* Two levels of initialization are used because gcc will set the symbol
631 size of an array to the length of the array, causing dynamic relinking
632 errors. Doing this makes the symbol size constant */
633const char **TFRewritePackageOrder = iTFRewritePackageOrder;
634const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
635
636bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
637 TFRewriteData *Rewrite)
638{
639 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
640 for (unsigned I = 0; I != 256; I++)
641 Visited[I] = 0;
642
643 // Set new tag up as necessary.
644 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
645 {
646 if (Rewrite[J].NewTag == 0)
647 Rewrite[J].NewTag = Rewrite[J].Tag;
648 }
649
650 // Write all all of the tags, in order.
9e51c0b6 651 if (Order != NULL)
b2e465d6 652 {
9e51c0b6 653 for (unsigned int I = 0; Order[I] != 0; I++)
b2e465d6 654 {
9e51c0b6
MV
655 bool Rewritten = false;
656
657 // See if this is a field that needs to be rewritten
658 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
659 {
660 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
661 {
662 Visited[J] |= 2;
663 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
664 {
665 if (isspace(Rewrite[J].Rewrite[0]))
666 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
667 else
668 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
669 }
670 Rewritten = true;
671 break;
672 }
673 }
b2e465d6 674
9e51c0b6
MV
675 // See if it is in the fragment
676 unsigned Pos;
677 if (Tags.Find(Order[I],Pos) == false)
678 continue;
679 Visited[Pos] |= 1;
680
681 if (Rewritten == true)
682 continue;
b2e465d6 683
9e51c0b6
MV
684 /* Write out this element, taking a moment to rewrite the tag
685 in case of changes of case. */
686 const char *Start;
687 const char *Stop;
688 Tags.Get(Start,Stop,Pos);
b2e465d6 689
9e51c0b6
MV
690 if (fputs(Order[I],Output) < 0)
691 return _error->Errno("fputs","IO Error to output");
692 Start += strlen(Order[I]);
693 if (fwrite(Start,Stop - Start,1,Output) != 1)
694 return _error->Errno("fwrite","IO Error to output");
695 if (Stop[-1] != '\n')
696 fprintf(Output,"\n");
697 }
698 }
b2e465d6
AL
699
700 // Now write all the old tags that were missed.
701 for (unsigned int I = 0; I != Tags.Count(); I++)
702 {
703 if ((Visited[I] & 1) == 1)
704 continue;
705
706 const char *Start;
707 const char *Stop;
708 Tags.Get(Start,Stop,I);
709 const char *End = Start;
710 for (; End < Stop && *End != ':'; End++);
711
712 // See if this is a field that needs to be rewritten
713 bool Rewritten = false;
714 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
715 {
716 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
717 {
718 Visited[J] |= 2;
719 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
720 {
721 if (isspace(Rewrite[J].Rewrite[0]))
722 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
723 else
724 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
725 }
726
727 Rewritten = true;
728 break;
729 }
730 }
731
732 if (Rewritten == true)
733 continue;
734
735 // Write out this element
736 if (fwrite(Start,Stop - Start,1,Output) != 1)
737 return _error->Errno("fwrite","IO Error to output");
738 if (Stop[-1] != '\n')
739 fprintf(Output,"\n");
740 }
741
742 // Now write all the rewrites that were missed
743 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
744 {
745 if ((Visited[J] & 2) == 2)
746 continue;
747
748 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
749 {
750 if (isspace(Rewrite[J].Rewrite[0]))
751 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
752 else
753 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
754 }
755 }
756
757 return true;
758}
759 /*}}}*/