]>
git.saurik.com Git - apt.git/blob - apt-pkg/tagfile.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: tagfile.cc,v 1.31 2001/10/04 05:13:23 jgg 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 /*{{{*/
15 #pragma implementation "apt-pkg/tagfile.h"
18 #include <apt-pkg/tagfile.h>
19 #include <apt-pkg/error.h>
20 #include <apt-pkg/strutl.h>
31 // TagFile::pkgTagFile - Constructor /*{{{*/
32 // ---------------------------------------------------------------------
34 pkgTagFile::pkgTagFile(FileFd
*pFd
,unsigned long Size
) : Fd(*pFd
), Size(Size
)
36 if (Fd
.IsOpen() == false)
39 Start
= End
= Buffer
= 0;
45 Buffer
= new char[Size
];
52 // TagFile::~pkgTagFile - Destructor /*{{{*/
53 // ---------------------------------------------------------------------
55 pkgTagFile::~pkgTagFile()
60 // TagFile::Step - Advance to the next section /*{{{*/
61 // ---------------------------------------------------------------------
62 /* If the Section Scanner fails we refill the buffer and try again. */
63 bool pkgTagFile::Step(pkgTagSection
&Tag
)
65 if (Tag
.Scan(Start
,End
- Start
) == false)
70 if (Tag
.Scan(Start
,End
- Start
) == false)
71 return _error
->Error(_("Unable to parse package file %s (1)"),Fd
.Name().c_str());
74 iOffset
+= Tag
.size();
81 // TagFile::Fill - Top up the buffer /*{{{*/
82 // ---------------------------------------------------------------------
83 /* This takes the bit at the end of the buffer and puts it at the start
84 then fills the rest from the file */
85 bool pkgTagFile::Fill()
87 unsigned long EndSize
= End
- Start
;
89 memmove(Buffer
,Start
,EndSize
);
91 End
= Buffer
+ EndSize
;
97 if (Size
- (End
- Buffer
) < 4)
100 // Append a double new line if one does not exist
101 unsigned int LineCount
= 0;
102 for (const char *E
= End
- 1; E
- End
< 6 && (*E
== '\n' || *E
== '\r'); E
--)
105 for (; LineCount
< 2; LineCount
++)
111 // See if only a bit of the file is left
112 unsigned long Actual
;
113 if (Fd
.Read(End
,Size
- (End
- Buffer
),&Actual
) == false)
115 if (Actual
!= Size
- (End
- Buffer
))
119 if (Left < Size - (End - Buffer))
121 if (Fd.Read(End,Left) == false)
129 if (Fd.Read(End,Size - (End - Buffer)) == false)
132 Left -= Size - (End - Buffer);
139 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
140 // ---------------------------------------------------------------------
141 /* This jumps to a pre-recorded file location and reads the record
143 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long Offset
)
145 // We are within a buffer space of the next hit..
146 if (Offset
>= iOffset
&& iOffset
+ (End
- Start
) > Offset
)
148 unsigned long Dist
= Offset
- iOffset
;
154 // Reposition and reload..
157 if (Fd
.Seek(Offset
) == false)
159 End
= Start
= Buffer
;
164 if (Tag
.Scan(Start
,End
- Start
) == true)
167 // This appends a double new line (for the real eof handling)
171 if (Tag
.Scan(Start
,End
- Start
) == false)
172 return _error
->Error(_("Unable to parse package file %s (2)"),Fd
.Name().c_str());
177 // TagSection::Scan - Scan for the end of the header information /*{{{*/
178 // ---------------------------------------------------------------------
179 /* This looks for the first double new line in the data stream. It also
180 indexes the tags in the section. This very simple hash function for the
181 first 3 letters gives very good performance on the debian package files */
182 inline static unsigned long AlphaHash(const char *Text
, const char *End
= 0)
184 unsigned long Res
= 0;
185 for (; Text
!= End
&& *Text
!= ':' && *Text
!= 0; Text
++)
186 Res
= (unsigned long)(*Text
) ^ (Res
<< 2);
190 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
)
192 const char *End
= Start
+ MaxLength
;
193 Stop
= Section
= Start
;
194 memset(AlphaIndexes
,0,sizeof(AlphaIndexes
));
200 while (TagCount
+1 < sizeof(Indexes
)/sizeof(Indexes
[0]) && Stop
< End
)
202 // Start a new index and add it to the hash
203 if (isspace(Stop
[0]) == 0)
205 Indexes
[TagCount
++] = Stop
- Section
;
206 AlphaIndexes
[AlphaHash(Stop
,End
)] = TagCount
;
209 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
214 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++);
216 // Double newline marks the end of the record
217 if (Stop
+1 < End
&& Stop
[1] == '\n')
219 Indexes
[TagCount
] = Stop
- Section
;
220 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
230 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
231 // ---------------------------------------------------------------------
232 /* There should be exactly 1 newline at the end of the buffer, no more. */
233 void pkgTagSection::Trim()
235 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
238 // TagSection::Find - Locate a tag /*{{{*/
239 // ---------------------------------------------------------------------
240 /* This searches the section for a tag that matches the given string. */
241 bool pkgTagSection::Find(const char *Tag
,unsigned &Pos
) const
243 unsigned int Length
= strlen(Tag
);
244 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
249 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
253 St
= Section
+ Indexes
[I
];
254 if (strncasecmp(Tag
,St
,Length
) != 0)
257 // Make sure the colon is in the right place
258 const char *C
= St
+ Length
;
259 for (; isspace(*C
) != 0; C
++);
270 // TagSection::Find - Locate a tag /*{{{*/
271 // ---------------------------------------------------------------------
272 /* This searches the section for a tag that matches the given string. */
273 bool pkgTagSection::Find(const char *Tag
,const char *&Start
,
274 const char *&End
) const
276 unsigned int Length
= strlen(Tag
);
277 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
282 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
286 St
= Section
+ Indexes
[I
];
287 if (strncasecmp(Tag
,St
,Length
) != 0)
290 // Make sure the colon is in the right place
291 const char *C
= St
+ Length
;
292 for (; isspace(*C
) != 0; C
++);
296 // Strip off the gunk from the start end
298 End
= Section
+ Indexes
[I
+1];
300 return _error
->Error("Internal parsing error");
302 for (; (isspace(*Start
) != 0 || *Start
== ':') && Start
< End
; Start
++);
303 for (; isspace(End
[-1]) != 0 && End
> Start
; End
--);
312 // TagSection::FindS - Find a string /*{{{*/
313 // ---------------------------------------------------------------------
315 string
pkgTagSection::FindS(const char *Tag
) const
319 if (Find(Tag
,Start
,End
) == false)
321 return string(Start
,End
);
324 // TagSection::FindI - Find an integer /*{{{*/
325 // ---------------------------------------------------------------------
327 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
331 if (Find(Tag
,Start
,Stop
) == false)
334 // Copy it into a temp buffer so we can use strtol
336 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
338 strncpy(S
,Start
,Stop
-Start
);
342 signed long Result
= strtol(S
,&End
,10);
348 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
349 // ---------------------------------------------------------------------
350 /* The bits marked in Flag are masked on/off in Flags */
351 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
352 unsigned long Flag
) const
356 if (Find(Tag
,Start
,Stop
) == false)
359 switch (StringToBool(string(Start
,Stop
)))
370 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
377 // TFRewrite - Rewrite a control record /*{{{*/
378 // ---------------------------------------------------------------------
379 /* This writes the control record to stdout rewriting it as necessary. The
380 override map item specificies the rewriting rules to follow. This also
381 takes the time to sort the feild list. */
383 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
385 static const char *iTFRewritePackageOrder
[] = {
396 "Revision", // Obsolete
397 "Config-Version", // Obsolete
410 "MSDOS-Filename", // Obsolete
413 static const char *iTFRewriteSourceOrder
[] = {"Package",
421 "Build-Depends-Indep",
423 "Build-Conflicts-Indep",
431 /* Two levels of initialization are used because gcc will set the symbol
432 size of an array to the length of the array, causing dynamic relinking
433 errors. Doing this makes the symbol size constant */
434 const char **TFRewritePackageOrder
= iTFRewritePackageOrder
;
435 const char **TFRewriteSourceOrder
= iTFRewriteSourceOrder
;
437 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
438 TFRewriteData
*Rewrite
)
440 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
441 for (unsigned I
= 0; I
!= 256; I
++)
444 // Set new tag up as necessary.
445 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
447 if (Rewrite
[J
].NewTag
== 0)
448 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
451 // Write all all of the tags, in order.
452 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
454 bool Rewritten
= false;
456 // See if this is a field that needs to be rewritten
457 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
459 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
462 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
464 if (isspace(Rewrite
[J
].Rewrite
[0]))
465 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
467 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
475 // See if it is in the fragment
477 if (Tags
.Find(Order
[I
],Pos
) == false)
481 if (Rewritten
== true)
484 /* Write out this element, taking a moment to rewrite the tag
485 in case of changes of case. */
488 Tags
.Get(Start
,Stop
,Pos
);
490 if (fputs(Order
[I
],Output
) < 0)
491 return _error
->Errno("fputs","IO Error to output");
492 Start
+= strlen(Order
[I
]);
493 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
494 return _error
->Errno("fwrite","IO Error to output");
495 if (Stop
[-1] != '\n')
496 fprintf(Output
,"\n");
499 // Now write all the old tags that were missed.
500 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
502 if ((Visited
[I
] & 1) == 1)
507 Tags
.Get(Start
,Stop
,I
);
508 const char *End
= Start
;
509 for (; End
< Stop
&& *End
!= ':'; End
++);
511 // See if this is a field that needs to be rewritten
512 bool Rewritten
= false;
513 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
515 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
518 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
520 if (isspace(Rewrite
[J
].Rewrite
[0]))
521 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
523 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
531 if (Rewritten
== true)
534 // Write out this element
535 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
536 return _error
->Errno("fwrite","IO Error to output");
537 if (Stop
[-1] != '\n')
538 fprintf(Output
,"\n");
541 // Now write all the rewrites that were missed
542 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
544 if ((Visited
[J
] & 2) == 2)
547 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
549 if (isspace(Rewrite
[J
].Rewrite
[0]))
550 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
552 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);