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