use malloc instead of new[] in pkgTagFile
[ntk/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;
0aae6d14 167 if (Actual != dataSize || d->Fd.Eof() == true)
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 /*}}}*/
578bfd0a
AL
236// TagSection::Scan - Scan for the end of the header information /*{{{*/
237// ---------------------------------------------------------------------
c176c4d0
DK
238/* This looks for the first double new line in the data stream.
239 It also indexes the tags in the section. */
0852eaef 240bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
578bfd0a
AL
241{
242 const char *End = Start + MaxLength;
243 Stop = Section = Start;
c1a22377 244 memset(AlphaIndexes,0,sizeof(AlphaIndexes));
c7b5ce1c 245
2ca99a0d 246 if (Stop == 0)
0852eaef 247 return false;
81e9789b 248
578bfd0a 249 TagCount = 0;
fd71171a 250 while (TagCount+1 < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
578bfd0a 251 {
81e9789b
MV
252 TrimRecord(true,End);
253
90d64280 254 // Start a new index and add it to the hash
c1a22377
AL
255 if (isspace(Stop[0]) == 0)
256 {
257 Indexes[TagCount++] = Stop - Section;
b2e465d6 258 AlphaIndexes[AlphaHash(Stop,End)] = TagCount;
c1a22377 259 }
0a8e3465 260
c1a22377 261 Stop = (const char *)memchr(Stop,'\n',End - Stop);
0a8e3465 262
c1a22377 263 if (Stop == 0)
0852eaef 264 return false;
81e9789b 265
b8b7c37d 266 for (; Stop+1 < End && Stop[1] == '\r'; Stop++);
c1a22377 267
f3bcc383
AL
268 // Double newline marks the end of the record
269 if (Stop+1 < End && Stop[1] == '\n')
578bfd0a 270 {
578bfd0a 271 Indexes[TagCount] = Stop - Section;
81e9789b 272 TrimRecord(false,End);
0852eaef 273 return true;
578bfd0a
AL
274 }
275
c1a22377
AL
276 Stop++;
277 }
138d4b3d 278
0852eaef 279 return false;
578bfd0a
AL
280}
281 /*}}}*/
81e9789b
MV
282// TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
283// ---------------------------------------------------------------------
284/* There should be exactly 2 newline at the end of the record, no more. */
285void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
286{
287 if (BeforeRecord == true)
288 return;
289 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
290}
291 /*}}}*/
b2e465d6
AL
292// TagSection::Trim - Trim off any trailing garbage /*{{{*/
293// ---------------------------------------------------------------------
294/* There should be exactly 1 newline at the end of the buffer, no more. */
295void pkgTagSection::Trim()
296{
297 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
298}
299 /*}}}*/
c8b860fb
MV
300// TagSection::Exists - return True if a tag exists /*{{{*/
301bool pkgTagSection::Exists(const char* const Tag)
302{
303 unsigned int tmp;
304 return Find(Tag, tmp);
305}
306 /*}}}*/
578bfd0a
AL
307// TagSection::Find - Locate a tag /*{{{*/
308// ---------------------------------------------------------------------
309/* This searches the section for a tag that matches the given string. */
c8b860fb 310bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
578bfd0a
AL
311{
312 unsigned int Length = strlen(Tag);
b2e465d6 313 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
c1a22377
AL
314 if (I == 0)
315 return false;
316 I--;
317
318 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
319 I = (I+1)%TagCount)
578bfd0a 320 {
c1a22377
AL
321 const char *St;
322 St = Section + Indexes[I];
323 if (strncasecmp(Tag,St,Length) != 0)
578bfd0a
AL
324 continue;
325
b2e465d6
AL
326 // Make sure the colon is in the right place
327 const char *C = St + Length;
328 for (; isspace(*C) != 0; C++);
329 if (*C != ':')
330 continue;
331 Pos = I;
332 return true;
333 }
334
335 Pos = 0;
336 return false;
337}
338 /*}}}*/
339// TagSection::Find - Locate a tag /*{{{*/
340// ---------------------------------------------------------------------
341/* This searches the section for a tag that matches the given string. */
342bool pkgTagSection::Find(const char *Tag,const char *&Start,
343 const char *&End) const
344{
345 unsigned int Length = strlen(Tag);
346 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
347 if (I == 0)
348 return false;
349 I--;
350
351 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
352 I = (I+1)%TagCount)
353 {
354 const char *St;
355 St = Section + Indexes[I];
356 if (strncasecmp(Tag,St,Length) != 0)
357 continue;
358
578bfd0a 359 // Make sure the colon is in the right place
c1a22377 360 const char *C = St + Length;
578bfd0a
AL
361 for (; isspace(*C) != 0; C++);
362 if (*C != ':')
363 continue;
364
365 // Strip off the gunk from the start end
366 Start = C;
367 End = Section + Indexes[I+1];
06bba740
AL
368 if (Start >= End)
369 return _error->Error("Internal parsing error");
370
578bfd0a
AL
371 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
372 for (; isspace(End[-1]) != 0 && End > Start; End--);
06bba740 373
578bfd0a
AL
374 return true;
375 }
c1a22377 376
578bfd0a
AL
377 Start = End = 0;
378 return false;
379}
380 /*}}}*/
0e66b144 381// TagSection::FindS - Find a string /*{{{*/
a05599f1
AL
382// ---------------------------------------------------------------------
383/* */
b2e465d6 384string pkgTagSection::FindS(const char *Tag) const
a05599f1
AL
385{
386 const char *Start;
387 const char *End;
388 if (Find(Tag,Start,End) == false)
389 return string();
390 return string(Start,End);
391}
392 /*}}}*/
393// TagSection::FindI - Find an integer /*{{{*/
394// ---------------------------------------------------------------------
395/* */
b2e465d6 396signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
a05599f1
AL
397{
398 const char *Start;
b0b4efb9
AL
399 const char *Stop;
400 if (Find(Tag,Start,Stop) == false)
401 return Default;
402
403 // Copy it into a temp buffer so we can use strtol
404 char S[300];
405 if ((unsigned)(Stop - Start) >= sizeof(S))
406 return Default;
407 strncpy(S,Start,Stop-Start);
408 S[Stop - Start] = 0;
409
410 char *End;
411 signed long Result = strtol(S,&End,10);
412 if (S == End)
413 return Default;
414 return Result;
415}
416 /*}}}*/
e2c66de5
DK
417// TagSection::FindULL - Find an unsigned long long integer /*{{{*/
418// ---------------------------------------------------------------------
419/* */
420unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long const &Default) const
421{
422 const char *Start;
423 const char *Stop;
424 if (Find(Tag,Start,Stop) == false)
425 return Default;
426
427 // Copy it into a temp buffer so we can use strtoull
428 char S[100];
429 if ((unsigned)(Stop - Start) >= sizeof(S))
430 return Default;
431 strncpy(S,Start,Stop-Start);
432 S[Stop - Start] = 0;
433
434 char *End;
435 unsigned long long Result = strtoull(S,&End,10);
436 if (S == End)
437 return Default;
438 return Result;
439}
440 /*}}}*/
b0b4efb9
AL
441// TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
442// ---------------------------------------------------------------------
443/* The bits marked in Flag are masked on/off in Flags */
444bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
b2e465d6 445 unsigned long Flag) const
b0b4efb9
AL
446{
447 const char *Start;
448 const char *Stop;
449 if (Find(Tag,Start,Stop) == false)
450 return true;
fe0f7911
DK
451 return FindFlag(Flags, Flag, Start, Stop);
452}
453bool const pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
454 char const* Start, char const* Stop)
455{
456 switch (StringToBool(string(Start, Stop)))
457 {
b0b4efb9
AL
458 case 0:
459 Flags &= ~Flag;
460 return true;
461
462 case 1:
463 Flags |= Flag;
464 return true;
465
466 default:
b2e465d6 467 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
b0b4efb9
AL
468 return true;
469 }
470 return true;
a05599f1
AL
471}
472 /*}}}*/
b2e465d6
AL
473// TFRewrite - Rewrite a control record /*{{{*/
474// ---------------------------------------------------------------------
475/* This writes the control record to stdout rewriting it as necessary. The
476 override map item specificies the rewriting rules to follow. This also
477 takes the time to sort the feild list. */
478
479/* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
480 array. */
481static const char *iTFRewritePackageOrder[] = {
482 "Package",
483 "Essential",
484 "Status",
485 "Priority",
486 "Section",
487 "Installed-Size",
488 "Maintainer",
47e7ebb3 489 "Original-Maintainer",
b2e465d6
AL
490 "Architecture",
491 "Source",
492 "Version",
493 "Revision", // Obsolete
494 "Config-Version", // Obsolete
495 "Replaces",
496 "Provides",
497 "Depends",
498 "Pre-Depends",
499 "Recommends",
500 "Suggests",
501 "Conflicts",
308c7d30 502 "Breaks",
b2e465d6
AL
503 "Conffiles",
504 "Filename",
505 "Size",
506 "MD5Sum",
cde41ae8
MV
507 "SHA1",
508 "SHA256",
d9b9e9e2 509 "SHA512",
b2e465d6
AL
510 "MSDOS-Filename", // Obsolete
511 "Description",
512 0};
513static const char *iTFRewriteSourceOrder[] = {"Package",
514 "Source",
515 "Binary",
516 "Version",
517 "Priority",
518 "Section",
519 "Maintainer",
47e7ebb3 520 "Original-Maintainer",
b2e465d6
AL
521 "Build-Depends",
522 "Build-Depends-Indep",
523 "Build-Conflicts",
524 "Build-Conflicts-Indep",
525 "Architecture",
526 "Standards-Version",
527 "Format",
528 "Directory",
529 "Files",
530 0};
531
532/* Two levels of initialization are used because gcc will set the symbol
533 size of an array to the length of the array, causing dynamic relinking
534 errors. Doing this makes the symbol size constant */
535const char **TFRewritePackageOrder = iTFRewritePackageOrder;
536const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
537
538bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
539 TFRewriteData *Rewrite)
540{
541 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
542 for (unsigned I = 0; I != 256; I++)
543 Visited[I] = 0;
544
545 // Set new tag up as necessary.
546 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
547 {
548 if (Rewrite[J].NewTag == 0)
549 Rewrite[J].NewTag = Rewrite[J].Tag;
550 }
551
552 // Write all all of the tags, in order.
553 for (unsigned int I = 0; Order[I] != 0; I++)
554 {
555 bool Rewritten = false;
556
557 // See if this is a field that needs to be rewritten
558 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
559 {
560 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
561 {
562 Visited[J] |= 2;
563 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
564 {
565 if (isspace(Rewrite[J].Rewrite[0]))
566 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
567 else
568 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
569 }
570
571 Rewritten = true;
572 break;
573 }
574 }
575
576 // See if it is in the fragment
577 unsigned Pos;
578 if (Tags.Find(Order[I],Pos) == false)
579 continue;
580 Visited[Pos] |= 1;
581
582 if (Rewritten == true)
583 continue;
584
585 /* Write out this element, taking a moment to rewrite the tag
586 in case of changes of case. */
587 const char *Start;
588 const char *Stop;
589 Tags.Get(Start,Stop,Pos);
590
591 if (fputs(Order[I],Output) < 0)
592 return _error->Errno("fputs","IO Error to output");
593 Start += strlen(Order[I]);
594 if (fwrite(Start,Stop - Start,1,Output) != 1)
595 return _error->Errno("fwrite","IO Error to output");
596 if (Stop[-1] != '\n')
597 fprintf(Output,"\n");
598 }
599
600 // Now write all the old tags that were missed.
601 for (unsigned int I = 0; I != Tags.Count(); I++)
602 {
603 if ((Visited[I] & 1) == 1)
604 continue;
605
606 const char *Start;
607 const char *Stop;
608 Tags.Get(Start,Stop,I);
609 const char *End = Start;
610 for (; End < Stop && *End != ':'; End++);
611
612 // See if this is a field that needs to be rewritten
613 bool Rewritten = false;
614 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
615 {
616 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
617 {
618 Visited[J] |= 2;
619 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
620 {
621 if (isspace(Rewrite[J].Rewrite[0]))
622 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
623 else
624 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
625 }
626
627 Rewritten = true;
628 break;
629 }
630 }
631
632 if (Rewritten == true)
633 continue;
634
635 // Write out this element
636 if (fwrite(Start,Stop - Start,1,Output) != 1)
637 return _error->Errno("fwrite","IO Error to output");
638 if (Stop[-1] != '\n')
639 fprintf(Output,"\n");
640 }
641
642 // Now write all the rewrites that were missed
643 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
644 {
645 if ((Visited[J] & 2) == 2)
646 continue;
647
648 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
649 {
650 if (isspace(Rewrite[J].Rewrite[0]))
651 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
652 else
653 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
654 }
655 }
656
657 return true;
658}
659 /*}}}*/