]>
git.saurik.com Git - apt.git/blob - apt-pkg/tagfile.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: tagfile.cc,v 1.29 2001/04/22 05:42:52 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 if (Fd
.IsOpen() == false)
36 Start
= End
= Buffer
= 0;
42 Buffer
= new char[Size
];
49 // TagFile::~pkgTagFile - Destructor /*{{{*/
50 // ---------------------------------------------------------------------
52 pkgTagFile::~pkgTagFile()
57 // TagFile::Step - Advance to the next section /*{{{*/
58 // ---------------------------------------------------------------------
59 /* If the Section Scanner fails we refill the buffer and try again. */
60 bool pkgTagFile::Step(pkgTagSection
&Tag
)
62 if (Tag
.Scan(Start
,End
- Start
) == false)
67 if (Tag
.Scan(Start
,End
- Start
) == false)
68 return _error
->Error(_("Unable to parse package file %s (1)"),Fd
.Name().c_str());
71 iOffset
+= Tag
.size();
78 // TagFile::Fill - Top up the buffer /*{{{*/
79 // ---------------------------------------------------------------------
80 /* This takes the bit at the end of the buffer and puts it at the start
81 then fills the rest from the file */
82 bool pkgTagFile::Fill()
84 unsigned long EndSize
= End
- Start
;
86 memmove(Buffer
,Start
,EndSize
);
88 End
= Buffer
+ EndSize
;
94 if (Size
- (End
- Buffer
) < 4)
97 // Append a double new line if one does not exist
98 unsigned int LineCount
= 0;
99 for (const char *E
= End
- 1; E
- End
< 6 && (*E
== '\n' || *E
== '\r'); E
--)
102 for (; LineCount
< 2; LineCount
++)
108 // See if only a bit of the file is left
109 unsigned long Actual
;
110 if (Fd
.Read(End
,Size
- (End
- Buffer
),&Actual
) == false)
112 if (Actual
!= Size
- (End
- Buffer
))
116 if (Left < Size - (End - Buffer))
118 if (Fd.Read(End,Left) == false)
126 if (Fd.Read(End,Size - (End - Buffer)) == false)
129 Left -= Size - (End - Buffer);
136 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
137 // ---------------------------------------------------------------------
138 /* This jumps to a pre-recorded file location and reads the record
140 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long Offset
)
142 // We are within a buffer space of the next hit..
143 if (Offset
>= iOffset
&& iOffset
+ (End
- Start
) > Offset
)
145 unsigned long Dist
= Offset
- iOffset
;
151 // Reposition and reload..
154 if (Fd
.Seek(Offset
) == false)
156 End
= Start
= Buffer
;
161 if (Tag
.Scan(Start
,End
- Start
) == true)
164 // This appends a double new line (for the real eof handling)
168 if (Tag
.Scan(Start
,End
- Start
) == false)
169 return _error
->Error(_("Unable to parse package file %s (2)"),Fd
.Name().c_str());
174 // TagSection::Scan - Scan for the end of the header information /*{{{*/
175 // ---------------------------------------------------------------------
176 /* This looks for the first double new line in the data stream. It also
177 indexes the tags in the section. This very simple hash function for the
178 first 3 letters gives very good performance on the debian package files */
179 inline static unsigned long AlphaHash(const char *Text
, const char *End
= 0)
181 unsigned long Res
= 0;
182 for (; Text
!= End
&& *Text
!= ':' && *Text
!= 0; Text
++)
183 Res
= (unsigned long)(*Text
) ^ (Res
<< 2);
187 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
)
189 const char *End
= Start
+ MaxLength
;
190 Stop
= Section
= Start
;
191 memset(AlphaIndexes
,0,sizeof(AlphaIndexes
));
197 while (TagCount
< sizeof(Indexes
)/sizeof(Indexes
[0]) && Stop
< End
)
199 // Start a new index and add it to the hash
200 if (isspace(Stop
[0]) == 0)
202 Indexes
[TagCount
++] = Stop
- Section
;
203 AlphaIndexes
[AlphaHash(Stop
,End
)] = TagCount
;
206 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
211 for (; Stop
[1] == '\r' && Stop
+1 < End
; Stop
++);
213 // Double newline marks the end of the record
214 if (Stop
+1 < End
&& Stop
[1] == '\n')
216 Indexes
[TagCount
] = Stop
- Section
;
217 for (; (Stop
[0] == '\n' || Stop
[0] == '\r') && Stop
< End
; Stop
++);
227 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
228 // ---------------------------------------------------------------------
229 /* There should be exactly 1 newline at the end of the buffer, no more. */
230 void pkgTagSection::Trim()
232 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
235 // TagSection::Find - Locate a tag /*{{{*/
236 // ---------------------------------------------------------------------
237 /* This searches the section for a tag that matches the given string. */
238 bool pkgTagSection::Find(const char *Tag
,unsigned &Pos
) const
240 unsigned int Length
= strlen(Tag
);
241 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
246 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
250 St
= Section
+ Indexes
[I
];
251 if (strncasecmp(Tag
,St
,Length
) != 0)
254 // Make sure the colon is in the right place
255 const char *C
= St
+ Length
;
256 for (; isspace(*C
) != 0; C
++);
267 // TagSection::Find - Locate a tag /*{{{*/
268 // ---------------------------------------------------------------------
269 /* This searches the section for a tag that matches the given string. */
270 bool pkgTagSection::Find(const char *Tag
,const char *&Start
,
271 const char *&End
) const
273 unsigned int Length
= strlen(Tag
);
274 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
279 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
283 St
= Section
+ Indexes
[I
];
284 if (strncasecmp(Tag
,St
,Length
) != 0)
287 // Make sure the colon is in the right place
288 const char *C
= St
+ Length
;
289 for (; isspace(*C
) != 0; C
++);
293 // Strip off the gunk from the start end
295 End
= Section
+ Indexes
[I
+1];
297 return _error
->Error("Internal parsing error");
299 for (; (isspace(*Start
) != 0 || *Start
== ':') && Start
< End
; Start
++);
300 for (; isspace(End
[-1]) != 0 && End
> Start
; End
--);
309 // TagSection::FindS - Find a string /*{{{*/
310 // ---------------------------------------------------------------------
312 string
pkgTagSection::FindS(const char *Tag
) const
316 if (Find(Tag
,Start
,End
) == false)
318 return string(Start
,End
);
321 // TagSection::FindI - Find an integer /*{{{*/
322 // ---------------------------------------------------------------------
324 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
328 if (Find(Tag
,Start
,Stop
) == false)
331 // Copy it into a temp buffer so we can use strtol
333 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
335 strncpy(S
,Start
,Stop
-Start
);
339 signed long Result
= strtol(S
,&End
,10);
345 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
346 // ---------------------------------------------------------------------
347 /* The bits marked in Flag are masked on/off in Flags */
348 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
349 unsigned long Flag
) const
353 if (Find(Tag
,Start
,Stop
) == false)
356 switch (StringToBool(string(Start
,Stop
)))
367 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
374 // TFRewrite - Rewrite a control record /*{{{*/
375 // ---------------------------------------------------------------------
376 /* This writes the control record to stdout rewriting it as necessary. The
377 override map item specificies the rewriting rules to follow. This also
378 takes the time to sort the feild list. */
380 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
382 static const char *iTFRewritePackageOrder
[] = {
393 "Revision", // Obsolete
394 "Config-Version", // Obsolete
407 "MSDOS-Filename", // Obsolete
410 static const char *iTFRewriteSourceOrder
[] = {"Package",
418 "Build-Depends-Indep",
420 "Build-Conflicts-Indep",
428 /* Two levels of initialization are used because gcc will set the symbol
429 size of an array to the length of the array, causing dynamic relinking
430 errors. Doing this makes the symbol size constant */
431 const char **TFRewritePackageOrder
= iTFRewritePackageOrder
;
432 const char **TFRewriteSourceOrder
= iTFRewriteSourceOrder
;
434 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
435 TFRewriteData
*Rewrite
)
437 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
438 for (unsigned I
= 0; I
!= 256; I
++)
441 // Set new tag up as necessary.
442 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
444 if (Rewrite
[J
].NewTag
== 0)
445 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
448 // Write all all of the tags, in order.
449 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
451 bool Rewritten
= false;
453 // See if this is a field that needs to be rewritten
454 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
456 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
459 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
461 if (isspace(Rewrite
[J
].Rewrite
[0]))
462 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
464 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
472 // See if it is in the fragment
474 if (Tags
.Find(Order
[I
],Pos
) == false)
478 if (Rewritten
== true)
481 /* Write out this element, taking a moment to rewrite the tag
482 in case of changes of case. */
485 Tags
.Get(Start
,Stop
,Pos
);
487 if (fputs(Order
[I
],Output
) < 0)
488 return _error
->Errno("fputs","IO Error to output");
489 Start
+= strlen(Order
[I
]);
490 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
491 return _error
->Errno("fwrite","IO Error to output");
492 if (Stop
[-1] != '\n')
493 fprintf(Output
,"\n");
496 // Now write all the old tags that were missed.
497 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
499 if ((Visited
[I
] & 1) == 1)
504 Tags
.Get(Start
,Stop
,I
);
505 const char *End
= Start
;
506 for (; End
< Stop
&& *End
!= ':'; End
++);
508 // See if this is a field that needs to be rewritten
509 bool Rewritten
= false;
510 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
512 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
515 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
517 if (isspace(Rewrite
[J
].Rewrite
[0]))
518 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
520 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
528 if (Rewritten
== true)
531 // Write out this element
532 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
533 return _error
->Errno("fwrite","IO Error to output");
534 if (Stop
[-1] != '\n')
535 fprintf(Output
,"\n");
538 // Now write all the rewrites that were missed
539 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
541 if ((Visited
[J
] & 2) == 2)
544 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
546 if (isspace(Rewrite
[J
].Rewrite
[0]))
547 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
549 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);