]>
git.saurik.com Git - apt-legacy.git/blob - apt-pkg/tagfile.cc
7ef5df38e808ab5f03ebb957bf23ddc63a738612
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 // Start a new index and add it to the hash
152 if (isspace(Stop
[0]) == 0)
154 Indexes
[TagCount
++] = Stop
- Section
;
155 unsigned long hash(AlphaHash(Stop
, End
));
156 while (AlphaIndexes
[hash
] != 0)
157 hash
= (hash
+ 1) % (sizeof(AlphaIndexes
) / sizeof(AlphaIndexes
[0]));
158 AlphaIndexes
[hash
] = TagCount
;
161 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
168 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++);
170 // Double newline marks the end of the record
171 if (Stop
+1 == End
|| Stop
[1] == '\n')
173 Indexes
[TagCount
] = Stop
- Section
;
174 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
184 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
185 // ---------------------------------------------------------------------
186 /* There should be exactly 1 newline at the end of the buffer, no more. */
187 void pkgTagSection::Trim()
189 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
192 // TagSection::Find - Locate a tag /*{{{*/
193 // ---------------------------------------------------------------------
194 /* This searches the section for a tag that matches the given string. */
195 bool pkgTagSection::Find(const char *Tag
,unsigned &Pos
) const
197 unsigned int Length
= strlen(Tag
);
198 unsigned int J
= AlphaHash(Tag
);
200 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
201 J
= (J
+1)%(sizeof(AlphaIndexes
)/sizeof(AlphaIndexes
[0])))
203 unsigned int I
= AlphaIndexes
[J
];
209 St
= Section
+ Indexes
[I
];
210 if (strncasecmp(Tag
,St
,Length
) != 0)
213 // Make sure the colon is in the right place
214 const char *C
= St
+ Length
;
215 for (; isspace(*C
) != 0; C
++);
226 // TagSection::Find - Locate a tag /*{{{*/
227 // ---------------------------------------------------------------------
228 /* This searches the section for a tag that matches the given string. */
229 bool pkgTagSection::Find(const char *Tag
,const char *&Start
,
230 const char *&End
) const
232 unsigned int Length
= strlen(Tag
);
233 unsigned int J
= AlphaHash(Tag
);
235 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
236 J
= (J
+1)%(sizeof(AlphaIndexes
)/sizeof(AlphaIndexes
[0])))
238 unsigned int I
= AlphaIndexes
[J
];
244 St
= Section
+ Indexes
[I
];
245 if (strncasecmp(Tag
,St
,Length
) != 0)
248 // Make sure the colon is in the right place
249 const char *C
= St
+ Length
;
250 for (; isspace(*C
) != 0; C
++);
254 // Strip off the gunk from the start end
256 End
= Section
+ Indexes
[I
+1];
258 return _error
->Error("Internal parsing error");
260 for (; (isspace(*Start
) != 0 || *Start
== ':') && Start
< End
; Start
++);
261 for (; isspace(End
[-1]) != 0 && End
> Start
; End
--);
270 // TagSection::FindS - Find a string /*{{{*/
271 // ---------------------------------------------------------------------
273 string
pkgTagSection::FindS(const char *Tag
) const
277 if (Find(Tag
,Start
,End
) == false)
279 return string(Start
,End
);
282 // TagSection::FindI - Find an integer /*{{{*/
283 // ---------------------------------------------------------------------
285 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
289 if (Find(Tag
,Start
,Stop
) == false)
292 // Copy it into a temp buffer so we can use strtol
294 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
296 strncpy(S
,Start
,Stop
-Start
);
300 signed long Result
= strtol(S
,&End
,10);
306 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
307 // ---------------------------------------------------------------------
308 /* The bits marked in Flag are masked on/off in Flags */
309 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
310 unsigned long Flag
) const
314 if (Find(Tag
,Start
,Stop
) == false)
317 switch (StringToBool(string(Start
,Stop
)))
328 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
335 // TFRewrite - Rewrite a control record /*{{{*/
336 // ---------------------------------------------------------------------
337 /* This writes the control record to stdout rewriting it as necessary. The
338 override map item specificies the rewriting rules to follow. This also
339 takes the time to sort the feild list. */
341 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
343 static const char *iTFRewritePackageOrder
[] = {
354 "Revision", // Obsolete
355 "Config-Version", // Obsolete
370 "MSDOS-Filename", // Obsolete
373 static const char *iTFRewriteSourceOrder
[] = {"Package",
381 "Build-Depends-Indep",
383 "Build-Conflicts-Indep",
391 /* Two levels of initialization are used because gcc will set the symbol
392 size of an array to the length of the array, causing dynamic relinking
393 errors. Doing this makes the symbol size constant */
394 const char **TFRewritePackageOrder
= iTFRewritePackageOrder
;
395 const char **TFRewriteSourceOrder
= iTFRewriteSourceOrder
;
397 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
398 TFRewriteData
*Rewrite
)
400 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
401 for (unsigned I
= 0; I
!= 256; I
++)
404 // Set new tag up as necessary.
405 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
407 if (Rewrite
[J
].NewTag
== 0)
408 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
411 // Write all all of the tags, in order.
412 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
414 bool Rewritten
= false;
416 // See if this is a field that needs to be rewritten
417 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
419 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
422 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
424 if (isspace(Rewrite
[J
].Rewrite
[0]))
425 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
427 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
435 // See if it is in the fragment
437 if (Tags
.Find(Order
[I
],Pos
) == false)
441 if (Rewritten
== true)
444 /* Write out this element, taking a moment to rewrite the tag
445 in case of changes of case. */
448 Tags
.Get(Start
,Stop
,Pos
);
450 if (fputs(Order
[I
],Output
) < 0)
451 return _error
->Errno("fputs","IO Error to output");
452 Start
+= strlen(Order
[I
]);
453 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
454 return _error
->Errno("fwrite","IO Error to output");
455 if (Stop
[-1] != '\n')
456 fprintf(Output
,"\n");
459 // Now write all the old tags that were missed.
460 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
462 if ((Visited
[I
] & 1) == 1)
467 Tags
.Get(Start
,Stop
,I
);
468 const char *End
= Start
;
469 for (; End
< Stop
&& *End
!= ':'; End
++);
471 // See if this is a field that needs to be rewritten
472 bool Rewritten
= false;
473 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
475 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
478 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
480 if (isspace(Rewrite
[J
].Rewrite
[0]))
481 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
483 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
491 if (Rewritten
== true)
494 // Write out this element
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 rewrites that were missed
502 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
504 if ((Visited
[J
] & 2) == 2)
507 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
509 if (isspace(Rewrite
[J
].Rewrite
[0]))
510 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
512 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);