]>
git.saurik.com Git - apt.git/blob - apt-pkg/tagfile.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: tagfile.cc,v 1.26 2001/02/20 07:03:17 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>
28 // TagFile::pkgTagFile - Constructor /*{{{*/
29 // ---------------------------------------------------------------------
31 pkgTagFile::pkgTagFile(FileFd
*pFd
,unsigned long Size
) : Fd(*pFd
), Size(Size
)
33 Buffer
= new char[Size
];
36 TotalSize
= Fd
.Size();
41 // TagFile::~pkgTagFile - Destructor /*{{{*/
42 // ---------------------------------------------------------------------
44 pkgTagFile::~pkgTagFile()
49 // TagFile::Step - Advance to the next section /*{{{*/
50 // ---------------------------------------------------------------------
51 /* If the Section Scanner fails we refill the buffer and try again. */
52 bool pkgTagFile::Step(pkgTagSection
&Tag
)
54 if (Tag
.Scan(Start
,End
- Start
) == false)
59 if (Tag
.Scan(Start
,End
- Start
) == false)
60 return _error
->Error(_("Unable to parse package file %s (1)"),Fd
.Name().c_str());
63 iOffset
+= Tag
.size();
70 // TagFile::Fill - Top up the buffer /*{{{*/
71 // ---------------------------------------------------------------------
72 /* This takes the bit at the end of the buffer and puts it at the start
73 then fills the rest from the file */
74 bool pkgTagFile::Fill()
76 unsigned long EndSize
= End
- Start
;
78 memmove(Buffer
,Start
,EndSize
);
80 End
= Buffer
+ EndSize
;
86 if (Size
- (End
- Buffer
) < 4)
89 // Append a double new line if one does not exist
90 unsigned int LineCount
= 0;
91 for (const char *E
= End
- 1; E
- End
< 6 && (*E
== '\n' || *E
== '\r'); E
--)
94 for (; LineCount
< 2; LineCount
++)
100 // See if only a bit of the file is left
101 if (Left
< Size
- (End
- Buffer
))
103 if (Fd
.Read(End
,Left
) == false)
111 if (Fd
.Read(End
,Size
- (End
- Buffer
)) == false)
114 Left
-= Size
- (End
- Buffer
);
120 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
121 // ---------------------------------------------------------------------
122 /* This jumps to a pre-recorded file location and reads the record
124 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long Offset
)
126 // We are within a buffer space of the next hit..
127 if (Offset
>= iOffset
&& iOffset
+ (End
- Start
) > Offset
)
129 unsigned long Dist
= Offset
- iOffset
;
135 // Reposition and reload..
137 Left
= TotalSize
- Offset
;
138 if (Fd
.Seek(Offset
) == false)
140 End
= Start
= Buffer
;
145 if (Tag
.Scan(Start
,End
- Start
) == true)
148 // This appends a double new line (for the real eof handling)
152 if (Tag
.Scan(Start
,End
- Start
) == false)
153 return _error
->Error(_("Unable to parse package file %s (2)"),Fd
.Name().c_str());
158 // TagSection::Scan - Scan for the end of the header information /*{{{*/
159 // ---------------------------------------------------------------------
160 /* This looks for the first double new line in the data stream. It also
161 indexes the tags in the section. This very simple hash function for the
162 first 3 letters gives very good performance on the debian package files */
163 inline static unsigned long AlphaHash(const char *Text
, const char *End
= 0)
165 unsigned long Res
= 0;
166 for (; Text
!= End
&& *Text
!= ':' && *Text
!= 0; Text
++)
167 Res
= (unsigned long)(*Text
) ^ (Res
<< 2);
171 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
)
173 const char *End
= Start
+ MaxLength
;
174 Stop
= Section
= Start
;
175 memset(AlphaIndexes
,0,sizeof(AlphaIndexes
));
181 while (TagCount
< sizeof(Indexes
)/sizeof(Indexes
[0]) && Stop
< End
)
183 // Start a new index and add it to the hash
184 if (isspace(Stop
[0]) == 0)
186 Indexes
[TagCount
++] = Stop
- Section
;
187 AlphaIndexes
[AlphaHash(Stop
,End
)] = TagCount
;
190 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
195 for (; Stop
[1] == '\r' && Stop
+1 < End
; Stop
++);
197 // Double newline marks the end of the record
198 if (Stop
+1 < End
&& Stop
[1] == '\n')
200 Indexes
[TagCount
] = Stop
- Section
;
201 for (; (Stop
[0] == '\n' || Stop
[0] == '\r') && Stop
< End
; Stop
++);
211 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
212 // ---------------------------------------------------------------------
213 /* There should be exactly 1 newline at the end of the buffer, no more. */
214 void pkgTagSection::Trim()
216 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
219 // TagSection::Find - Locate a tag /*{{{*/
220 // ---------------------------------------------------------------------
221 /* This searches the section for a tag that matches the given string. */
222 bool pkgTagSection::Find(const char *Tag
,unsigned &Pos
) const
224 unsigned int Length
= strlen(Tag
);
225 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
230 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
234 St
= Section
+ Indexes
[I
];
235 if (strncasecmp(Tag
,St
,Length
) != 0)
238 // Make sure the colon is in the right place
239 const char *C
= St
+ Length
;
240 for (; isspace(*C
) != 0; C
++);
251 // TagSection::Find - Locate a tag /*{{{*/
252 // ---------------------------------------------------------------------
253 /* This searches the section for a tag that matches the given string. */
254 bool pkgTagSection::Find(const char *Tag
,const char *&Start
,
255 const char *&End
) const
257 unsigned int Length
= strlen(Tag
);
258 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
263 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
267 St
= Section
+ Indexes
[I
];
268 if (strncasecmp(Tag
,St
,Length
) != 0)
271 // Make sure the colon is in the right place
272 const char *C
= St
+ Length
;
273 for (; isspace(*C
) != 0; C
++);
277 // Strip off the gunk from the start end
279 End
= Section
+ Indexes
[I
+1];
281 return _error
->Error("Internal parsing error");
283 for (; (isspace(*Start
) != 0 || *Start
== ':') && Start
< End
; Start
++);
284 for (; isspace(End
[-1]) != 0 && End
> Start
; End
--);
293 // TagSection::FindS - Find a string /*{{{*/
294 // ---------------------------------------------------------------------
296 string
pkgTagSection::FindS(const char *Tag
) const
300 if (Find(Tag
,Start
,End
) == false)
302 return string(Start
,End
);
305 // TagSection::FindI - Find an integer /*{{{*/
306 // ---------------------------------------------------------------------
308 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
312 if (Find(Tag
,Start
,Stop
) == false)
315 // Copy it into a temp buffer so we can use strtol
317 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
319 strncpy(S
,Start
,Stop
-Start
);
323 signed long Result
= strtol(S
,&End
,10);
329 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
330 // ---------------------------------------------------------------------
331 /* The bits marked in Flag are masked on/off in Flags */
332 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
333 unsigned long Flag
) const
337 if (Find(Tag
,Start
,Stop
) == false)
340 switch (StringToBool(string(Start
,Stop
)))
351 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
358 // TFRewrite - Rewrite a control record /*{{{*/
359 // ---------------------------------------------------------------------
360 /* This writes the control record to stdout rewriting it as necessary. The
361 override map item specificies the rewriting rules to follow. This also
362 takes the time to sort the feild list. */
364 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
366 static const char *iTFRewritePackageOrder
[] = {
377 "Revision", // Obsolete
378 "Config-Version", // Obsolete
390 "MSDOS-Filename", // Obsolete
393 static const char *iTFRewriteSourceOrder
[] = {"Package",
401 "Build-Depends-Indep",
403 "Build-Conflicts-Indep",
411 /* Two levels of initialization are used because gcc will set the symbol
412 size of an array to the length of the array, causing dynamic relinking
413 errors. Doing this makes the symbol size constant */
414 const char **TFRewritePackageOrder
= iTFRewritePackageOrder
;
415 const char **TFRewriteSourceOrder
= iTFRewriteSourceOrder
;
417 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
418 TFRewriteData
*Rewrite
)
420 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
421 for (unsigned I
= 0; I
!= 256; I
++)
424 // Set new tag up as necessary.
425 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
427 if (Rewrite
[J
].NewTag
== 0)
428 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
431 // Write all all of the tags, in order.
432 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
434 bool Rewritten
= false;
436 // See if this is a field that needs to be rewritten
437 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
439 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
442 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
444 if (isspace(Rewrite
[J
].Rewrite
[0]))
445 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
447 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
455 // See if it is in the fragment
457 if (Tags
.Find(Order
[I
],Pos
) == false)
461 if (Rewritten
== true)
464 /* Write out this element, taking a moment to rewrite the tag
465 in case of changes of case. */
468 Tags
.Get(Start
,Stop
,Pos
);
470 if (fputs(Order
[I
],Output
) < 0)
471 return _error
->Errno("fputs","IO Error to output");
472 Start
+= strlen(Order
[I
]);
473 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
474 return _error
->Errno("fwrite","IO Error to output");
475 if (Stop
[-1] != '\n')
476 fprintf(Output
,"\n");
479 // Now write all the old tags that were missed.
480 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
482 if ((Visited
[I
] & 1) == 1)
487 Tags
.Get(Start
,Stop
,I
);
488 const char *End
= Start
;
489 for (; End
< Stop
&& *End
!= ':'; End
++);
491 // See if this is a field that needs to be rewritten
492 bool Rewritten
= false;
493 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
495 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
498 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
500 if (isspace(Rewrite
[J
].Rewrite
[0]))
501 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
503 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
511 if (Rewritten
== true)
514 // Write out this element
515 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
516 return _error
->Errno("fwrite","IO Error to output");
517 if (Stop
[-1] != '\n')
518 fprintf(Output
,"\n");
521 // Now write all the rewrites that were missed
522 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
524 if ((Visited
[J
] & 2) == 2)
527 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
529 if (isspace(Rewrite
[J
].Rewrite
[0]))
530 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
532 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);