]>
git.saurik.com Git - apt.git/blob - apt-pkg/tagfile.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: tagfile.cc,v 1.36 2003/04/27 05:59:14 doogie 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;
46 Buffer
= new char[Size
];
53 // TagFile::~pkgTagFile - Destructor /*{{{*/
54 // ---------------------------------------------------------------------
56 pkgTagFile::~pkgTagFile()
61 // TagFile::Step - Advance to the next section /*{{{*/
62 // ---------------------------------------------------------------------
63 /* If the Section Scanner fails we refill the buffer and try again. */
64 bool pkgTagFile::Step(pkgTagSection
&Tag
)
66 pkgTagSection::ScanFlags ret
= Tag
.Scan(Start
,End
- Start
);
67 if (ret
== pkgTagSection::ScanEOF
) {
72 ret
= Tag
.Scan(Start
,End
- Start
);
73 if (ret
== pkgTagSection::ScanEOF
) {
78 } while (ret
== pkgTagSection::ScanEOF
);
80 if (ret
!= pkgTagSection::ScanSuccess
)
81 return _error
->Error(_("Unable to parse package file %s (1)"),
84 iOffset
+= Tag
.size();
90 // TagFile::Fill - Top up the buffer /*{{{*/
91 // ---------------------------------------------------------------------
92 /* This takes the bit at the end of the buffer and puts it at the start
93 then fills the rest from the file */
94 bool pkgTagFile::Fill()
96 unsigned long EndSize
= End
- Start
;
97 unsigned long Actual
= 0;
99 memmove(Buffer
,Start
,EndSize
);
101 End
= Buffer
+ EndSize
;
105 // See if only a bit of the file is left
106 if (Fd
.Read(End
,Size
- (End
- Buffer
),&Actual
) == false)
108 if (Actual
!= Size
- (End
- Buffer
))
115 if (EndSize
<= 3 && Actual
== 0)
117 if (Size
- (End
- Buffer
) < 4)
120 // Append a double new line if one does not exist
121 unsigned int LineCount
= 0;
122 for (const char *E
= End
- 1; E
- End
< 6 && (*E
== '\n' || *E
== '\r'); E
--)
125 for (; LineCount
< 2; LineCount
++)
134 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
135 // ---------------------------------------------------------------------
136 /* This jumps to a pre-recorded file location and reads the record
138 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long Offset
)
140 // We are within a buffer space of the next hit..
141 if (Offset
>= iOffset
&& iOffset
+ (End
- Start
) > Offset
)
143 unsigned long Dist
= Offset
- iOffset
;
149 // Reposition and reload..
152 if (Fd
.Seek(Offset
) == false)
154 End
= Start
= Buffer
;
159 pkgTagSection::ScanFlags ret
= Tag
.Scan(Start
,End
- Start
);
160 if (ret
== pkgTagSection::ScanEOF
) {
165 ret
= Tag
.Scan(Start
,End
- Start
);
166 if (ret
== pkgTagSection::ScanEOF
) {
171 } while (ret
== pkgTagSection::ScanEOF
);
173 if (ret
!= pkgTagSection::ScanSuccess
)
174 return _error
->Error(_("Unable to parse package file %s (2)"),Fd
.Name().c_str());
179 // TagSection::Scan - Scan for the end of the header information /*{{{*/
180 // ---------------------------------------------------------------------
181 /* This looks for the first double new line in the data stream. It also
182 indexes the tags in the section. This very simple hash function for the
183 first 3 letters gives very good performance on the debian package files */
184 inline static unsigned long AlphaHash(const char *Text
, const char *End
= 0)
186 unsigned long Res
= 0;
187 for (; Text
!= End
&& *Text
!= ':' && *Text
!= 0; Text
++)
188 Res
= (unsigned long)(*Text
) ^ (Res
<< 2);
192 enum pkgTagSection::ScanFlags
pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
)
194 const char *End
= Start
+ MaxLength
;
195 Stop
= Section
= Start
;
196 memset(AlphaIndexes
,0,sizeof(AlphaIndexes
));
202 while (TagCount
+1 < sizeof(Indexes
)/sizeof(Indexes
[0]) && Stop
< End
)
204 // Start a new index and add it to the hash
205 if (isspace(Stop
[0]) == 0)
207 Indexes
[TagCount
++] = Stop
- Section
;
208 AlphaIndexes
[AlphaHash(Stop
,End
)] = TagCount
;
211 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
216 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++);
218 // Double newline marks the end of the record
219 if (Stop
+1 < End
&& Stop
[1] == '\n')
221 Indexes
[TagCount
] = Stop
- Section
;
222 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
232 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
233 // ---------------------------------------------------------------------
234 /* There should be exactly 1 newline at the end of the buffer, no more. */
235 void pkgTagSection::Trim()
237 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
240 // TagSection::Find - Locate a tag /*{{{*/
241 // ---------------------------------------------------------------------
242 /* This searches the section for a tag that matches the given string. */
243 bool pkgTagSection::Find(const char *Tag
,unsigned &Pos
) const
245 unsigned int Length
= strlen(Tag
);
246 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
251 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
255 St
= Section
+ Indexes
[I
];
256 if (strncasecmp(Tag
,St
,Length
) != 0)
259 // Make sure the colon is in the right place
260 const char *C
= St
+ Length
;
261 for (; isspace(*C
) != 0; C
++);
272 // TagSection::Find - Locate a tag /*{{{*/
273 // ---------------------------------------------------------------------
274 /* This searches the section for a tag that matches the given string. */
275 bool pkgTagSection::Find(const char *Tag
,const char *&Start
,
276 const char *&End
) const
278 unsigned int Length
= strlen(Tag
);
279 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
284 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
288 St
= Section
+ Indexes
[I
];
289 if (strncasecmp(Tag
,St
,Length
) != 0)
292 // Make sure the colon is in the right place
293 const char *C
= St
+ Length
;
294 for (; isspace(*C
) != 0; C
++);
298 // Strip off the gunk from the start end
300 End
= Section
+ Indexes
[I
+1];
302 return _error
->Error("Internal parsing error");
304 for (; (isspace(*Start
) != 0 || *Start
== ':') && Start
< End
; Start
++);
305 for (; isspace(End
[-1]) != 0 && End
> Start
; End
--);
314 // TagSection::FindS - Find a string /*{{{*/
315 // ---------------------------------------------------------------------
317 string
pkgTagSection::FindS(const char *Tag
) const
321 if (Find(Tag
,Start
,End
) == false)
323 return string(Start
,End
);
326 // TagSection::FindI - Find an integer /*{{{*/
327 // ---------------------------------------------------------------------
329 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
333 if (Find(Tag
,Start
,Stop
) == false)
336 // Copy it into a temp buffer so we can use strtol
338 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
340 strncpy(S
,Start
,Stop
-Start
);
344 signed long Result
= strtol(S
,&End
,10);
350 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
351 // ---------------------------------------------------------------------
352 /* The bits marked in Flag are masked on/off in Flags */
353 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
354 unsigned long Flag
) const
358 if (Find(Tag
,Start
,Stop
) == false)
361 switch (StringToBool(string(Start
,Stop
)))
372 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
379 // TFRewrite - Rewrite a control record /*{{{*/
380 // ---------------------------------------------------------------------
381 /* This writes the control record to stdout rewriting it as necessary. The
382 override map item specificies the rewriting rules to follow. This also
383 takes the time to sort the feild list. */
385 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
387 static const char *iTFRewritePackageOrder
[] = {
398 "Revision", // Obsolete
399 "Config-Version", // Obsolete
412 "MSDOS-Filename", // Obsolete
415 static const char *iTFRewriteSourceOrder
[] = {"Package",
423 "Build-Depends-Indep",
425 "Build-Conflicts-Indep",
433 /* Two levels of initialization are used because gcc will set the symbol
434 size of an array to the length of the array, causing dynamic relinking
435 errors. Doing this makes the symbol size constant */
436 const char **TFRewritePackageOrder
= iTFRewritePackageOrder
;
437 const char **TFRewriteSourceOrder
= iTFRewriteSourceOrder
;
439 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
440 TFRewriteData
*Rewrite
)
442 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
443 for (unsigned I
= 0; I
!= 256; I
++)
446 // Set new tag up as necessary.
447 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
449 if (Rewrite
[J
].NewTag
== 0)
450 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
453 // Write all all of the tags, in order.
454 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
456 bool Rewritten
= false;
458 // See if this is a field that needs to be rewritten
459 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
461 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
464 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
466 if (isspace(Rewrite
[J
].Rewrite
[0]))
467 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
469 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
477 // See if it is in the fragment
479 if (Tags
.Find(Order
[I
],Pos
) == false)
483 if (Rewritten
== true)
486 /* Write out this element, taking a moment to rewrite the tag
487 in case of changes of case. */
490 Tags
.Get(Start
,Stop
,Pos
);
492 if (fputs(Order
[I
],Output
) < 0)
493 return _error
->Errno("fputs","IO Error to output");
494 Start
+= strlen(Order
[I
]);
495 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
496 return _error
->Errno("fwrite","IO Error to output");
497 if (Stop
[-1] != '\n')
498 fprintf(Output
,"\n");
501 // Now write all the old tags that were missed.
502 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
504 if ((Visited
[I
] & 1) == 1)
509 Tags
.Get(Start
,Stop
,I
);
510 const char *End
= Start
;
511 for (; End
< Stop
&& *End
!= ':'; End
++);
513 // See if this is a field that needs to be rewritten
514 bool Rewritten
= false;
515 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
517 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
520 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
522 if (isspace(Rewrite
[J
].Rewrite
[0]))
523 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
525 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
533 if (Rewritten
== true)
536 // Write out this element
537 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
538 return _error
->Errno("fwrite","IO Error to output");
539 if (Stop
[-1] != '\n')
540 fprintf(Output
,"\n");
543 // Now write all the rewrites that were missed
544 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
546 if ((Visited
[J
] & 2) == 2)
549 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
551 if (isspace(Rewrite
[J
].Rewrite
[0]))
552 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
554 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);