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