]>
git.saurik.com Git - apt.git/blob - apt-pkg/tagfile.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: tagfile.cc,v 1.37.2.2 2003/12/31 16:02:30 mdz Exp $
4 /* ######################################################################
6 Fast scanner for RFC-822 type header information
8 This uses a rotating buffer to load the package information into.
9 The scanner runs over it and isolates and indexes a single section.
11 ##################################################################### */
13 // Include Files /*{{{*/
14 #include <apt-pkg/tagfile.h>
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/strutl.h>
27 class pkgTagFilePrivate
30 pkgTagFilePrivate(FileFd
*pFd
, unsigned long Size
) : Fd(*pFd
), Size(Size
)
38 unsigned long iOffset
;
42 // TagFile::pkgTagFile - Constructor /*{{{*/
43 // ---------------------------------------------------------------------
45 pkgTagFile::pkgTagFile(FileFd
*pFd
,unsigned long Size
)
47 d
= new pkgTagFilePrivate(pFd
, Size
);
49 if (d
->Fd
.IsOpen() == false)
51 d
->Start
= d
->End
= d
->Buffer
= 0;
57 d
->Buffer
= new char[Size
];
58 d
->Start
= d
->End
= d
->Buffer
;
64 // TagFile::~pkgTagFile - Destructor /*{{{*/
65 // ---------------------------------------------------------------------
67 pkgTagFile::~pkgTagFile()
73 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
74 unsigned long pkgTagFile::Offset()
79 // TagFile::Resize - Resize the internal buffer /*{{{*/
80 // ---------------------------------------------------------------------
81 /* Resize the internal buffer (double it in size). Fail if a maximum size
84 bool pkgTagFile::Resize()
87 unsigned long EndSize
= d
->End
- d
->Start
;
89 // fail is the buffer grows too big
90 if(d
->Size
> 1024*1024+1)
93 // get new buffer and use it
94 tmp
= new char[2*d
->Size
];
95 memcpy(tmp
, d
->Buffer
, d
->Size
);
100 // update the start/end pointers to the new buffer
101 d
->Start
= d
->Buffer
;
102 d
->End
= d
->Start
+ EndSize
;
106 // TagFile::Step - Advance to the next section /*{{{*/
107 // ---------------------------------------------------------------------
108 /* If the Section Scanner fails we refill the buffer and try again.
109 * If that fails too, double the buffer size and try again until a
110 * maximum buffer is reached.
112 bool pkgTagFile::Step(pkgTagSection
&Tag
)
114 while (Tag
.Scan(d
->Start
,d
->End
- d
->Start
) == false)
119 if(Tag
.Scan(d
->Start
,d
->End
- d
->Start
))
122 if (Resize() == false)
123 return _error
->Error(_("Unable to parse package file %s (1)"),
124 d
->Fd
.Name().c_str());
126 d
->Start
+= Tag
.size();
127 d
->iOffset
+= Tag
.size();
133 // TagFile::Fill - Top up the buffer /*{{{*/
134 // ---------------------------------------------------------------------
135 /* This takes the bit at the end of the buffer and puts it at the start
136 then fills the rest from the file */
137 bool pkgTagFile::Fill()
139 unsigned long EndSize
= d
->End
- d
->Start
;
140 unsigned long Actual
= 0;
142 memmove(d
->Buffer
,d
->Start
,EndSize
);
143 d
->Start
= d
->Buffer
;
144 d
->End
= d
->Buffer
+ EndSize
;
146 if (d
->Done
== false)
148 // See if only a bit of the file is left
149 if (d
->Fd
.Read(d
->End
, d
->Size
- (d
->End
- d
->Buffer
),&Actual
) == false)
151 if (Actual
!= d
->Size
- (d
->End
- d
->Buffer
))
158 if (EndSize
<= 3 && Actual
== 0)
160 if (d
->Size
- (d
->End
- d
->Buffer
) < 4)
163 // Append a double new line if one does not exist
164 unsigned int LineCount
= 0;
165 for (const char *E
= d
->End
- 1; E
- d
->End
< 6 && (*E
== '\n' || *E
== '\r'); E
--)
168 for (; LineCount
< 2; LineCount
++)
177 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
178 // ---------------------------------------------------------------------
179 /* This jumps to a pre-recorded file location and reads the record
181 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long Offset
)
183 // We are within a buffer space of the next hit..
184 if (Offset
>= d
->iOffset
&& d
->iOffset
+ (d
->End
- d
->Start
) > Offset
)
186 unsigned long Dist
= Offset
- d
->iOffset
;
192 // Reposition and reload..
195 if (d
->Fd
.Seek(Offset
) == false)
197 d
->End
= d
->Start
= d
->Buffer
;
202 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
) == true)
205 // This appends a double new line (for the real eof handling)
209 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
) == false)
210 return _error
->Error(_("Unable to parse package file %s (2)"),d
->Fd
.Name().c_str());
215 // TagSection::Scan - Scan for the end of the header information /*{{{*/
216 // ---------------------------------------------------------------------
217 /* This looks for the first double new line in the data stream.
218 It also indexes the tags in the section. */
219 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
)
221 const char *End
= Start
+ MaxLength
;
222 Stop
= Section
= Start
;
223 memset(AlphaIndexes
,0,sizeof(AlphaIndexes
));
229 while (TagCount
+1 < sizeof(Indexes
)/sizeof(Indexes
[0]) && Stop
< End
)
231 TrimRecord(true,End
);
233 // Start a new index and add it to the hash
234 if (isspace(Stop
[0]) == 0)
236 Indexes
[TagCount
++] = Stop
- Section
;
237 AlphaIndexes
[AlphaHash(Stop
,End
)] = TagCount
;
240 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
245 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++);
247 // Double newline marks the end of the record
248 if (Stop
+1 < End
&& Stop
[1] == '\n')
250 Indexes
[TagCount
] = Stop
- Section
;
251 TrimRecord(false,End
);
261 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
262 // ---------------------------------------------------------------------
263 /* There should be exactly 2 newline at the end of the record, no more. */
264 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
)
266 if (BeforeRecord
== true)
268 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
271 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
272 // ---------------------------------------------------------------------
273 /* There should be exactly 1 newline at the end of the buffer, no more. */
274 void pkgTagSection::Trim()
276 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
279 // TagSection::Find - Locate a tag /*{{{*/
280 // ---------------------------------------------------------------------
281 /* This searches the section for a tag that matches the given string. */
282 bool pkgTagSection::Find(const char *Tag
,unsigned &Pos
) const
284 unsigned int Length
= strlen(Tag
);
285 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
290 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
294 St
= Section
+ Indexes
[I
];
295 if (strncasecmp(Tag
,St
,Length
) != 0)
298 // Make sure the colon is in the right place
299 const char *C
= St
+ Length
;
300 for (; isspace(*C
) != 0; C
++);
311 // TagSection::Find - Locate a tag /*{{{*/
312 // ---------------------------------------------------------------------
313 /* This searches the section for a tag that matches the given string. */
314 bool pkgTagSection::Find(const char *Tag
,const char *&Start
,
315 const char *&End
) const
317 unsigned int Length
= strlen(Tag
);
318 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
323 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
327 St
= Section
+ Indexes
[I
];
328 if (strncasecmp(Tag
,St
,Length
) != 0)
331 // Make sure the colon is in the right place
332 const char *C
= St
+ Length
;
333 for (; isspace(*C
) != 0; C
++);
337 // Strip off the gunk from the start end
339 End
= Section
+ Indexes
[I
+1];
341 return _error
->Error("Internal parsing error");
343 for (; (isspace(*Start
) != 0 || *Start
== ':') && Start
< End
; Start
++);
344 for (; isspace(End
[-1]) != 0 && End
> Start
; End
--);
353 // TagSection::FindS - Find a string /*{{{*/
354 // ---------------------------------------------------------------------
356 string
pkgTagSection::FindS(const char *Tag
) const
360 if (Find(Tag
,Start
,End
) == false)
362 return string(Start
,End
);
365 // TagSection::FindI - Find an integer /*{{{*/
366 // ---------------------------------------------------------------------
368 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
372 if (Find(Tag
,Start
,Stop
) == false)
375 // Copy it into a temp buffer so we can use strtol
377 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
379 strncpy(S
,Start
,Stop
-Start
);
383 signed long Result
= strtol(S
,&End
,10);
389 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
390 // ---------------------------------------------------------------------
392 unsigned long long pkgTagSection::FindULL(const char *Tag
, unsigned long long const &Default
) const
396 if (Find(Tag
,Start
,Stop
) == false)
399 // Copy it into a temp buffer so we can use strtoull
401 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
403 strncpy(S
,Start
,Stop
-Start
);
407 unsigned long long Result
= strtoull(S
,&End
,10);
413 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
414 // ---------------------------------------------------------------------
415 /* The bits marked in Flag are masked on/off in Flags */
416 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
417 unsigned long Flag
) const
421 if (Find(Tag
,Start
,Stop
) == false)
424 switch (StringToBool(string(Start
,Stop
)))
435 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
441 // TFRewrite - Rewrite a control record /*{{{*/
442 // ---------------------------------------------------------------------
443 /* This writes the control record to stdout rewriting it as necessary. The
444 override map item specificies the rewriting rules to follow. This also
445 takes the time to sort the feild list. */
447 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
449 static const char *iTFRewritePackageOrder
[] = {
457 "Original-Maintainer",
461 "Revision", // Obsolete
462 "Config-Version", // Obsolete
477 "MSDOS-Filename", // Obsolete
480 static const char *iTFRewriteSourceOrder
[] = {"Package",
487 "Original-Maintainer",
489 "Build-Depends-Indep",
491 "Build-Conflicts-Indep",
499 /* Two levels of initialization are used because gcc will set the symbol
500 size of an array to the length of the array, causing dynamic relinking
501 errors. Doing this makes the symbol size constant */
502 const char **TFRewritePackageOrder
= iTFRewritePackageOrder
;
503 const char **TFRewriteSourceOrder
= iTFRewriteSourceOrder
;
505 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
506 TFRewriteData
*Rewrite
)
508 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
509 for (unsigned I
= 0; I
!= 256; I
++)
512 // Set new tag up as necessary.
513 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
515 if (Rewrite
[J
].NewTag
== 0)
516 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
519 // Write all all of the tags, in order.
520 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
522 bool Rewritten
= false;
524 // See if this is a field that needs to be rewritten
525 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
527 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
530 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
532 if (isspace(Rewrite
[J
].Rewrite
[0]))
533 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
535 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
543 // See if it is in the fragment
545 if (Tags
.Find(Order
[I
],Pos
) == false)
549 if (Rewritten
== true)
552 /* Write out this element, taking a moment to rewrite the tag
553 in case of changes of case. */
556 Tags
.Get(Start
,Stop
,Pos
);
558 if (fputs(Order
[I
],Output
) < 0)
559 return _error
->Errno("fputs","IO Error to output");
560 Start
+= strlen(Order
[I
]);
561 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
562 return _error
->Errno("fwrite","IO Error to output");
563 if (Stop
[-1] != '\n')
564 fprintf(Output
,"\n");
567 // Now write all the old tags that were missed.
568 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
570 if ((Visited
[I
] & 1) == 1)
575 Tags
.Get(Start
,Stop
,I
);
576 const char *End
= Start
;
577 for (; End
< Stop
&& *End
!= ':'; End
++);
579 // See if this is a field that needs to be rewritten
580 bool Rewritten
= false;
581 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
583 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
586 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
588 if (isspace(Rewrite
[J
].Rewrite
[0]))
589 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
591 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
599 if (Rewritten
== true)
602 // Write out this element
603 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
604 return _error
->Errno("fwrite","IO Error to output");
605 if (Stop
[-1] != '\n')
606 fprintf(Output
,"\n");
609 // Now write all the rewrites that were missed
610 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
612 if ((Visited
[J
] & 2) == 2)
615 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
617 if (isspace(Rewrite
[J
].Rewrite
[0]))
618 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
620 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);