]>
git.saurik.com Git - apt-legacy.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 // TagFile::pkgTagFile - Constructor /*{{{*/
28 // ---------------------------------------------------------------------
30 pkgTagFile::pkgTagFile(FileFd
*pFd
,unsigned long Size
) :
33 if (Fd
.IsOpen() == false || Fd
.Size() == 0)
38 Start
= End
= Buffer
= 0;
44 Map
= new MMap(*pFd
, MMap::ReadOnly
);
45 Buffer
= reinterpret_cast<char *>(Map
->Data());
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 * If that fails too, double the buffer size and try again until a
64 * maximum buffer is reached.
66 bool pkgTagFile::Step(pkgTagSection
&Tag
)
68 if (Tag
.Scan(Start
,End
- Start
) == false)
73 return _error
->Error(_("Unable to parse package file %s (1)"),
77 iOffset
+= Tag
.size();
83 // TagFile::Fill - Top up the buffer /*{{{*/
84 // ---------------------------------------------------------------------
85 /* This takes the bit at the end of the buffer and puts it at the start
86 then fills the rest from the file */
87 bool pkgTagFile::Fill()
89 unsigned int Size(Map
->Size());
93 Start
= Buffer
+ iOffset
;
97 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
98 // ---------------------------------------------------------------------
99 /* This jumps to a pre-recorded file location and reads the record
101 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long Offset
)
103 // We are within a buffer space of the next hit..
104 if (Offset
>= iOffset
&& iOffset
+ (End
- Start
) > Offset
)
106 unsigned long Dist
= Offset
- iOffset
;
112 // Reposition and reload..
115 End
= Start
= Buffer
;
120 if (Tag
.Scan(Start
,End
- Start
) == false)
121 return _error
->Error(_("Unable to parse package file %s (2)"),Fd
.Name().c_str());
126 // TagSection::Scan - Scan for the end of the header information /*{{{*/
127 // ---------------------------------------------------------------------
128 /* This looks for the first double new line in the data stream. It also
129 indexes the tags in the section. This very simple hash function for the
130 last 8 letters gives very good performance on the debian package files */
131 inline static unsigned long AlphaHash(const char *Text
, const char *End
= 0)
133 unsigned long Res
= 0;
134 for (; Text
!= End
&& *Text
!= ':' && *Text
!= 0; Text
++)
135 Res
= ((unsigned long)(*Text
) & 0xDF) ^ (Res
<< 1);
139 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
)
141 const char *End
= Start
+ MaxLength
;
142 Stop
= Section
= Start
;
143 memset(AlphaIndexes
,0,sizeof(AlphaIndexes
));
149 while (TagCount
+1 < sizeof(Indexes
)/sizeof(Indexes
[0]) && Stop
< End
)
151 TrimRecord(true,End
);
153 // Start a new index and add it to the hash
154 if (isspace(Stop
[0]) == 0)
156 Indexes
[TagCount
++] = Stop
- Section
;
157 unsigned long hash(AlphaHash(Stop
, End
));
158 while (AlphaIndexes
[hash
] != 0)
159 hash
= (hash
+ 1) % (sizeof(AlphaIndexes
) / sizeof(AlphaIndexes
[0]));
160 AlphaIndexes
[hash
] = TagCount
;
163 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
170 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++);
172 // Double newline marks the end of the record
173 if (Stop
+1 == End
|| Stop
[1] == '\n')
175 Indexes
[TagCount
] = Stop
- Section
;
176 TrimRecord(false,End
);
186 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
187 // ---------------------------------------------------------------------
188 /* There should be exactly 2 newline at the end of the record, no more. */
189 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
)
191 if (BeforeRecord
== true)
193 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
196 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
197 // ---------------------------------------------------------------------
198 /* There should be exactly 1 newline at the end of the buffer, no more. */
199 void pkgTagSection::Trim()
201 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
204 // TagSection::Find - Locate a tag /*{{{*/
205 // ---------------------------------------------------------------------
206 /* This searches the section for a tag that matches the given string. */
207 bool pkgTagSection::Find(const char *Tag
,unsigned &Pos
) const
209 unsigned int Length
= strlen(Tag
);
210 unsigned int J
= AlphaHash(Tag
);
212 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
213 J
= (J
+1)%(sizeof(AlphaIndexes
)/sizeof(AlphaIndexes
[0])))
215 unsigned int I
= AlphaIndexes
[J
];
221 St
= Section
+ Indexes
[I
];
222 if (strncasecmp(Tag
,St
,Length
) != 0)
225 // Make sure the colon is in the right place
226 const char *C
= St
+ Length
;
227 for (; isspace(*C
) != 0; C
++);
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
,const char *&Start
,
242 const char *&End
) const
244 unsigned int Length
= strlen(Tag
);
245 unsigned int J
= AlphaHash(Tag
);
247 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
248 J
= (J
+1)%(sizeof(AlphaIndexes
)/sizeof(AlphaIndexes
[0])))
250 unsigned int I
= AlphaIndexes
[J
];
256 St
= Section
+ Indexes
[I
];
257 if (strncasecmp(Tag
,St
,Length
) != 0)
260 // Make sure the colon is in the right place
261 const char *C
= St
+ Length
;
262 for (; isspace(*C
) != 0; C
++);
266 // Strip off the gunk from the start end
268 End
= Section
+ Indexes
[I
+1];
270 return _error
->Error("Internal parsing error");
272 for (; (isspace(*Start
) != 0 || *Start
== ':') && Start
< End
; Start
++);
273 for (; isspace(End
[-1]) != 0 && End
> Start
; End
--);
282 // TagSection::FindS - Find a string /*{{{*/
283 // ---------------------------------------------------------------------
285 string
pkgTagSection::FindS(const char *Tag
) const
289 if (Find(Tag
,Start
,End
) == false)
291 return string(Start
,End
);
294 // TagSection::FindI - Find an integer /*{{{*/
295 // ---------------------------------------------------------------------
297 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
301 if (Find(Tag
,Start
,Stop
) == false)
304 // Copy it into a temp buffer so we can use strtol
306 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
308 strncpy(S
,Start
,Stop
-Start
);
312 signed long Result
= strtol(S
,&End
,10);
318 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
319 // ---------------------------------------------------------------------
320 /* The bits marked in Flag are masked on/off in Flags */
321 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
322 unsigned long Flag
) const
326 if (Find(Tag
,Start
,Stop
) == false)
329 switch (StringToBool(string(Start
,Stop
)))
340 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
346 // TFRewrite - Rewrite a control record /*{{{*/
347 // ---------------------------------------------------------------------
348 /* This writes the control record to stdout rewriting it as necessary. The
349 override map item specificies the rewriting rules to follow. This also
350 takes the time to sort the feild list. */
352 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
354 static const char *iTFRewritePackageOrder
[] = {
365 "Revision", // Obsolete
366 "Config-Version", // Obsolete
381 "MSDOS-Filename", // Obsolete
384 static const char *iTFRewriteSourceOrder
[] = {"Package",
392 "Build-Depends-Indep",
394 "Build-Conflicts-Indep",
402 /* Two levels of initialization are used because gcc will set the symbol
403 size of an array to the length of the array, causing dynamic relinking
404 errors. Doing this makes the symbol size constant */
405 const char **TFRewritePackageOrder
= iTFRewritePackageOrder
;
406 const char **TFRewriteSourceOrder
= iTFRewriteSourceOrder
;
408 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
409 TFRewriteData
*Rewrite
)
411 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
412 for (unsigned I
= 0; I
!= 256; I
++)
415 // Set new tag up as necessary.
416 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
418 if (Rewrite
[J
].NewTag
== 0)
419 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
422 // Write all all of the tags, in order.
423 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
425 bool Rewritten
= false;
427 // See if this is a field that needs to be rewritten
428 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
430 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
433 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
435 if (isspace(Rewrite
[J
].Rewrite
[0]))
436 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
438 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
446 // See if it is in the fragment
448 if (Tags
.Find(Order
[I
],Pos
) == false)
452 if (Rewritten
== true)
455 /* Write out this element, taking a moment to rewrite the tag
456 in case of changes of case. */
459 Tags
.Get(Start
,Stop
,Pos
);
461 if (fputs(Order
[I
],Output
) < 0)
462 return _error
->Errno("fputs","IO Error to output");
463 Start
+= strlen(Order
[I
]);
464 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
465 return _error
->Errno("fwrite","IO Error to output");
466 if (Stop
[-1] != '\n')
467 fprintf(Output
,"\n");
470 // Now write all the old tags that were missed.
471 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
473 if ((Visited
[I
] & 1) == 1)
478 Tags
.Get(Start
,Stop
,I
);
479 const char *End
= Start
;
480 for (; End
< Stop
&& *End
!= ':'; End
++);
482 // See if this is a field that needs to be rewritten
483 bool Rewritten
= false;
484 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
486 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
489 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
491 if (isspace(Rewrite
[J
].Rewrite
[0]))
492 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
494 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
502 if (Rewritten
== true)
505 // Write out this element
506 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
507 return _error
->Errno("fwrite","IO Error to output");
508 if (Stop
[-1] != '\n')
509 fprintf(Output
,"\n");
512 // Now write all the rewrites that were missed
513 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
515 if ((Visited
[J
] & 2) == 2)
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
);