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