]> git.saurik.com Git - apt-legacy.git/blame - apt-pkg/tagfile.cc
Fix compilation of http when embedding into Cydia.
[apt-legacy.git] / apt-pkg / tagfile.cc
CommitLineData
da6ee469
JF
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: tagfile.cc,v 1.37.2.2 2003/12/31 16:02:30 mdz Exp $
4/* ######################################################################
5
6 Fast scanner for RFC-822 type header information
7
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.
10
11 ##################################################################### */
12 /*}}}*/
13// Include Files /*{{{*/
da6ee469
JF
14#include <apt-pkg/tagfile.h>
15#include <apt-pkg/error.h>
16#include <apt-pkg/strutl.h>
17
18#include <apti18n.h>
19
20#include <string>
21#include <stdio.h>
22#include <ctype.h>
23 /*}}}*/
24
25using std::string;
26
27// TagFile::pkgTagFile - Constructor /*{{{*/
28// ---------------------------------------------------------------------
29/* */
30pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long Size) :
acdafb44 31 Fd(*pFd)
da6ee469 32{
acdafb44 33 if (Fd.IsOpen() == false || Fd.Size() == 0)
da6ee469 34 {
260efb63 35 _error->Discard();
acdafb44 36 Map = NULL;
da6ee469
JF
37 Buffer = 0;
38 Start = End = Buffer = 0;
39 Done = true;
40 iOffset = 0;
41 return;
42 }
43
acdafb44
JF
44 Map = new MMap(*pFd, MMap::ReadOnly);
45 Buffer = reinterpret_cast<char *>(Map->Data());
da6ee469
JF
46 Start = End = Buffer;
47 Done = false;
48 iOffset = 0;
49 Fill();
50}
51 /*}}}*/
52// TagFile::~pkgTagFile - Destructor /*{{{*/
53// ---------------------------------------------------------------------
54/* */
55pkgTagFile::~pkgTagFile()
56{
acdafb44 57 delete Map;
da6ee469
JF
58}
59 /*}}}*/
da6ee469
JF
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.
65 */
66bool pkgTagFile::Step(pkgTagSection &Tag)
67{
acdafb44 68 if (Tag.Scan(Start,End - Start) == false)
da6ee469 69 {
acdafb44
JF
70 if (Start == End)
71 return false;
72 else
da6ee469
JF
73 return _error->Error(_("Unable to parse package file %s (1)"),
74 Fd.Name().c_str());
75 }
76 Start += Tag.size();
77 iOffset += Tag.size();
78
79 Tag.Trim();
80 return true;
81}
82 /*}}}*/
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 */
87bool pkgTagFile::Fill()
88{
acdafb44
JF
89 unsigned int Size(Map->Size());
90 End = Buffer + Size;
91 if (iOffset >= Size)
92 return false;
93 Start = Buffer + iOffset;
da6ee469
JF
94 return true;
95}
96 /*}}}*/
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
100 that is there */
101bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long Offset)
102{
103 // We are within a buffer space of the next hit..
104 if (Offset >= iOffset && iOffset + (End - Start) > Offset)
105 {
106 unsigned long Dist = Offset - iOffset;
107 Start += Dist;
108 iOffset += Dist;
109 return Step(Tag);
110 }
111
112 // Reposition and reload..
113 iOffset = Offset;
114 Done = false;
da6ee469
JF
115 End = Start = Buffer;
116
117 if (Fill() == false)
118 return false;
119
da6ee469
JF
120 if (Tag.Scan(Start,End - Start) == false)
121 return _error->Error(_("Unable to parse package file %s (2)"),Fd.Name().c_str());
122
123 return true;
124}
125 /*}}}*/
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 */
131inline static unsigned long AlphaHash(const char *Text, const char *End = 0)
132{
133 unsigned long Res = 0;
134 for (; Text != End && *Text != ':' && *Text != 0; Text++)
135 Res = ((unsigned long)(*Text) & 0xDF) ^ (Res << 1);
136 return Res & 0xFF;
137}
138
139bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
140{
141 const char *End = Start + MaxLength;
142 Stop = Section = Start;
143 memset(AlphaIndexes,0,sizeof(AlphaIndexes));
144
145 if (Stop == 0)
146 return false;
0e5943eb 147
da6ee469
JF
148 TagCount = 0;
149 while (TagCount+1 < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
150 {
0e5943eb
JF
151 TrimRecord(true,End);
152
da6ee469
JF
153 // Start a new index and add it to the hash
154 if (isspace(Stop[0]) == 0)
155 {
156 Indexes[TagCount++] = Stop - Section;
acdafb44
JF
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;
da6ee469
JF
161 }
162
163 Stop = (const char *)memchr(Stop,'\n',End - Stop);
164
acdafb44
JF
165 if (Stop == 0) {
166 Stop = End;
167 goto end;
168 }
0e5943eb 169
da6ee469
JF
170 for (; Stop+1 < End && Stop[1] == '\r'; Stop++);
171
172 // Double newline marks the end of the record
acdafb44
JF
173 if (Stop+1 == End || Stop[1] == '\n')
174 end: {
da6ee469 175 Indexes[TagCount] = Stop - Section;
0e5943eb 176 TrimRecord(false,End);
da6ee469
JF
177 return true;
178 }
179
180 Stop++;
181 }
182
183 return false;
184}
185 /*}}}*/
0e5943eb
JF
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. */
189void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
190{
191 if (BeforeRecord == true)
192 return;
193 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
194}
195 /*}}}*/
da6ee469
JF
196// TagSection::Trim - Trim off any trailing garbage /*{{{*/
197// ---------------------------------------------------------------------
198/* There should be exactly 1 newline at the end of the buffer, no more. */
199void pkgTagSection::Trim()
200{
201 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
202}
203 /*}}}*/
204// TagSection::Find - Locate a tag /*{{{*/
205// ---------------------------------------------------------------------
206/* This searches the section for a tag that matches the given string. */
207bool pkgTagSection::Find(const char *Tag,unsigned &Pos) const
208{
209 unsigned int Length = strlen(Tag);
acdafb44 210 unsigned int J = AlphaHash(Tag);
da6ee469 211
acdafb44
JF
212 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
213 J = (J+1)%(sizeof(AlphaIndexes)/sizeof(AlphaIndexes[0])))
da6ee469 214 {
acdafb44
JF
215 unsigned int I = AlphaIndexes[J];
216 if (I == 0)
217 return false;
218 I--;
219
da6ee469
JF
220 const char *St;
221 St = Section + Indexes[I];
222 if (strncasecmp(Tag,St,Length) != 0)
223 continue;
224
225 // Make sure the colon is in the right place
226 const char *C = St + Length;
227 for (; isspace(*C) != 0; C++);
228 if (*C != ':')
229 continue;
230 Pos = I;
231 return true;
232 }
233
234 Pos = 0;
235 return false;
236}
237 /*}}}*/
238// TagSection::Find - Locate a tag /*{{{*/
239// ---------------------------------------------------------------------
240/* This searches the section for a tag that matches the given string. */
241bool pkgTagSection::Find(const char *Tag,const char *&Start,
242 const char *&End) const
243{
244 unsigned int Length = strlen(Tag);
acdafb44 245 unsigned int J = AlphaHash(Tag);
da6ee469 246
acdafb44
JF
247 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
248 J = (J+1)%(sizeof(AlphaIndexes)/sizeof(AlphaIndexes[0])))
da6ee469 249 {
acdafb44
JF
250 unsigned int I = AlphaIndexes[J];
251 if (I == 0)
252 return false;
253 I--;
254
da6ee469
JF
255 const char *St;
256 St = Section + Indexes[I];
257 if (strncasecmp(Tag,St,Length) != 0)
258 continue;
259
260 // Make sure the colon is in the right place
261 const char *C = St + Length;
262 for (; isspace(*C) != 0; C++);
263 if (*C != ':')
264 continue;
265
266 // Strip off the gunk from the start end
267 Start = C;
268 End = Section + Indexes[I+1];
269 if (Start >= End)
270 return _error->Error("Internal parsing error");
271
272 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
273 for (; isspace(End[-1]) != 0 && End > Start; End--);
274
275 return true;
276 }
277
278 Start = End = 0;
279 return false;
280}
281 /*}}}*/
282// TagSection::FindS - Find a string /*{{{*/
283// ---------------------------------------------------------------------
284/* */
285string pkgTagSection::FindS(const char *Tag) const
286{
287 const char *Start;
288 const char *End;
289 if (Find(Tag,Start,End) == false)
290 return string();
291 return string(Start,End);
292}
293 /*}}}*/
294// TagSection::FindI - Find an integer /*{{{*/
295// ---------------------------------------------------------------------
296/* */
297signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
298{
299 const char *Start;
300 const char *Stop;
301 if (Find(Tag,Start,Stop) == false)
302 return Default;
303
304 // Copy it into a temp buffer so we can use strtol
305 char S[300];
306 if ((unsigned)(Stop - Start) >= sizeof(S))
307 return Default;
308 strncpy(S,Start,Stop-Start);
309 S[Stop - Start] = 0;
310
311 char *End;
312 signed long Result = strtol(S,&End,10);
313 if (S == End)
314 return Default;
315 return Result;
316}
317 /*}}}*/
318// TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
319// ---------------------------------------------------------------------
320/* The bits marked in Flag are masked on/off in Flags */
321bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
322 unsigned long Flag) const
323{
324 const char *Start;
325 const char *Stop;
326 if (Find(Tag,Start,Stop) == false)
327 return true;
328
329 switch (StringToBool(string(Start,Stop)))
330 {
331 case 0:
332 Flags &= ~Flag;
333 return true;
334
335 case 1:
336 Flags |= Flag;
337 return true;
338
339 default:
340 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
341 return true;
342 }
343 return true;
344}
345 /*}}}*/
da6ee469
JF
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. */
351
352/* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
353 array. */
354static const char *iTFRewritePackageOrder[] = {
355 "Package",
356 "Essential",
357 "Status",
358 "Priority",
359 "Section",
360 "Installed-Size",
361 "Maintainer",
362 "Architecture",
363 "Source",
364 "Version",
365 "Revision", // Obsolete
366 "Config-Version", // Obsolete
367 "Replaces",
368 "Provides",
369 "Depends",
370 "Pre-Depends",
371 "Recommends",
372 "Suggests",
373 "Conflicts",
00ec24d0 374 "Breaks",
da6ee469
JF
375 "Conffiles",
376 "Filename",
377 "Size",
378 "MD5Sum",
379 "SHA1",
380 "SHA256",
381 "MSDOS-Filename", // Obsolete
382 "Description",
383 0};
384static const char *iTFRewriteSourceOrder[] = {"Package",
385 "Source",
386 "Binary",
387 "Version",
388 "Priority",
389 "Section",
390 "Maintainer",
391 "Build-Depends",
392 "Build-Depends-Indep",
393 "Build-Conflicts",
394 "Build-Conflicts-Indep",
395 "Architecture",
396 "Standards-Version",
397 "Format",
398 "Directory",
399 "Files",
400 0};
401
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 */
405const char **TFRewritePackageOrder = iTFRewritePackageOrder;
406const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
407
408bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
409 TFRewriteData *Rewrite)
410{
411 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
412 for (unsigned I = 0; I != 256; I++)
413 Visited[I] = 0;
414
415 // Set new tag up as necessary.
416 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
417 {
418 if (Rewrite[J].NewTag == 0)
419 Rewrite[J].NewTag = Rewrite[J].Tag;
420 }
421
422 // Write all all of the tags, in order.
423 for (unsigned int I = 0; Order[I] != 0; I++)
424 {
425 bool Rewritten = false;
426
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++)
429 {
430 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
431 {
432 Visited[J] |= 2;
433 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
434 {
435 if (isspace(Rewrite[J].Rewrite[0]))
436 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
437 else
438 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
439 }
440
441 Rewritten = true;
442 break;
443 }
444 }
445
446 // See if it is in the fragment
447 unsigned Pos;
448 if (Tags.Find(Order[I],Pos) == false)
449 continue;
450 Visited[Pos] |= 1;
451
452 if (Rewritten == true)
453 continue;
454
455 /* Write out this element, taking a moment to rewrite the tag
456 in case of changes of case. */
457 const char *Start;
458 const char *Stop;
459 Tags.Get(Start,Stop,Pos);
460
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");
468 }
469
470 // Now write all the old tags that were missed.
471 for (unsigned int I = 0; I != Tags.Count(); I++)
472 {
473 if ((Visited[I] & 1) == 1)
474 continue;
475
476 const char *Start;
477 const char *Stop;
478 Tags.Get(Start,Stop,I);
479 const char *End = Start;
480 for (; End < Stop && *End != ':'; End++);
481
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++)
485 {
486 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
487 {
488 Visited[J] |= 2;
489 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
490 {
491 if (isspace(Rewrite[J].Rewrite[0]))
492 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
493 else
494 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
495 }
496
497 Rewritten = true;
498 break;
499 }
500 }
501
502 if (Rewritten == true)
503 continue;
504
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");
510 }
511
512 // Now write all the rewrites that were missed
513 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
514 {
515 if ((Visited[J] & 2) == 2)
516 continue;
517
518 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
519 {
520 if (isspace(Rewrite[J].Rewrite[0]))
521 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
522 else
523 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
524 }
525 }
526
527 return true;
528}
529 /*}}}*/