]> git.saurik.com Git - apt.git/blame - apt-pkg/tagfile.cc
Fix status file parser so that if a record is larger th...
[apt.git] / apt-pkg / tagfile.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7bb525cb 3// $Id: tagfile.cc,v 1.36 2003/04/27 05:59:14 doogie Exp $
578bfd0a
AL
4/* ######################################################################
5
6 Fast scanner for RFC-822 type header information
7
ad00ae81 8 This uses a rotating buffer to load the package information into.
578bfd0a
AL
9 The scanner runs over it and isolates and indexes a single section.
10
11 ##################################################################### */
12 /*}}}*/
13// Include Files /*{{{*/
6c139d6e 14#ifdef __GNUG__
094a497d 15#pragma implementation "apt-pkg/tagfile.h"
6c139d6e
AL
16#endif
17
094a497d
AL
18#include <apt-pkg/tagfile.h>
19#include <apt-pkg/error.h>
cdcc6d34 20#include <apt-pkg/strutl.h>
578bfd0a 21
b2e465d6
AL
22#include <apti18n.h>
23
578bfd0a
AL
24#include <string>
25#include <stdio.h>
851a45a8 26#include <ctype.h>
578bfd0a
AL
27 /*}}}*/
28
851a45a8
AL
29using std::string;
30
578bfd0a
AL
31// TagFile::pkgTagFile - Constructor /*{{{*/
32// ---------------------------------------------------------------------
33/* */
b2e465d6 34pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long Size) : Fd(*pFd), Size(Size)
578bfd0a 35{
0e72dd52
AL
36 if (Fd.IsOpen() == false)
37 {
38 Buffer = 0;
39 Start = End = Buffer = 0;
f604cf55 40 Done = true;
0e72dd52
AL
41 iOffset = 0;
42 return;
43 }
44
7bb525cb 45 CurSize = Size;
ad00ae81
AL
46 Buffer = new char[Size];
47 Start = End = Buffer;
f604cf55 48 Done = false;
dcb79bae 49 iOffset = 0;
578bfd0a
AL
50 Fill();
51}
52 /*}}}*/
b2e465d6 53// TagFile::~pkgTagFile - Destructor /*{{{*/
29f7b36c
AL
54// ---------------------------------------------------------------------
55/* */
56pkgTagFile::~pkgTagFile()
57{
58 delete [] Buffer;
59}
60 /*}}}*/
578bfd0a
AL
61// TagFile::Step - Advance to the next section /*{{{*/
62// ---------------------------------------------------------------------
63/* If the Section Scanner fails we refill the buffer and try again. */
64bool pkgTagFile::Step(pkgTagSection &Tag)
65{
7bb525cb
AL
66 pkgTagSection::ScanFlags ret = Tag.Scan(Start,End - Start);
67 if (ret == pkgTagSection::ScanEOF) {
68 CurSize <<= 1;
578bfd0a
AL
69 if (Fill() == false)
70 return false;
7bb525cb
AL
71 do {
72 ret = Tag.Scan(Start,End - Start);
73 if (ret == pkgTagSection::ScanEOF) {
74 CurSize <<= 1;
75 if (Fill() == false)
76 break;
77 }
78 } while (ret == pkgTagSection::ScanEOF);
613f9499 79 }
7bb525cb
AL
80 if (ret != pkgTagSection::ScanSuccess)
81 return _error->Error(_("Unable to parse package file %s (1)"),
82 Fd.Name().c_str());
dcb79bae
AL
83 Start += Tag.size();
84 iOffset += Tag.size();
b2e465d6
AL
85
86 Tag.Trim();
578bfd0a
AL
87 return true;
88}
89 /*}}}*/
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 */
94bool pkgTagFile::Fill()
95{
ad00ae81 96 unsigned long EndSize = End - Start;
613f9499 97 unsigned long Actual = 0;
578bfd0a 98
c7b5ce1c
AL
99 memmove(Buffer,Start,EndSize);
100 Start = Buffer;
101 End = Buffer + EndSize;
102
fe06d72c
AL
103 if (Done == false)
104 {
105 // See if only a bit of the file is left
fe06d72c
AL
106 if (Fd.Read(End,Size - (End - Buffer),&Actual) == false)
107 return false;
108 if (Actual != Size - (End - Buffer))
109 Done = true;
110 End += Actual;
111 }
112
f604cf55 113 if (Done == true)
578bfd0a 114 {
f5ec3b68 115 if (EndSize <= 3 && Actual == 0)
578bfd0a 116 return false;
c7b5ce1c
AL
117 if (Size - (End - Buffer) < 4)
118 return true;
119
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--)
123 if (*E == '\n')
124 LineCount++;
125 for (; LineCount < 2; LineCount++)
126 *End++ = '\n';
127
578bfd0a
AL
128 return true;
129 }
130
578bfd0a
AL
131 return true;
132}
133 /*}}}*/
ad00ae81
AL
134// TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
135// ---------------------------------------------------------------------
03e39e59
AL
136/* This jumps to a pre-recorded file location and reads the record
137 that is there */
ad00ae81
AL
138bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long Offset)
139{
b2e465d6
AL
140 // We are within a buffer space of the next hit..
141 if (Offset >= iOffset && iOffset + (End - Start) > Offset)
142 {
143 unsigned long Dist = Offset - iOffset;
144 Start += Dist;
145 iOffset += Dist;
146 return Step(Tag);
147 }
148
149 // Reposition and reload..
ad00ae81 150 iOffset = Offset;
f604cf55 151 Done = false;
ad00ae81
AL
152 if (Fd.Seek(Offset) == false)
153 return false;
154 End = Start = Buffer;
155
138d4b3d
AL
156 if (Fill() == false)
157 return false;
158
7bb525cb
AL
159 pkgTagSection::ScanFlags ret = Tag.Scan(Start,End - Start);
160 if (ret == pkgTagSection::ScanEOF) {
161 CurSize <<= 1;
162 if (Fill() == false)
163 return false;
164 do {
165 ret = Tag.Scan(Start,End - Start);
166 if (ret == pkgTagSection::ScanEOF) {
167 CurSize <<= 1;
168 if (Fill() == false)
169 break;
170 }
171 } while (ret == pkgTagSection::ScanEOF);
172 }
173 if (ret != pkgTagSection::ScanSuccess)
b2e465d6 174 return _error->Error(_("Unable to parse package file %s (2)"),Fd.Name().c_str());
06bba740 175
ad00ae81
AL
176 return true;
177}
178 /*}}}*/
578bfd0a
AL
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
c1a22377
AL
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 */
b2e465d6
AL
184inline static unsigned long AlphaHash(const char *Text, const char *End = 0)
185{
186 unsigned long Res = 0;
187 for (; Text != End && *Text != ':' && *Text != 0; Text++)
188 Res = (unsigned long)(*Text) ^ (Res << 2);
189 return Res & 0xFF;
190}
191
7bb525cb 192enum pkgTagSection::ScanFlags pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
578bfd0a
AL
193{
194 const char *End = Start + MaxLength;
195 Stop = Section = Start;
c1a22377 196 memset(AlphaIndexes,0,sizeof(AlphaIndexes));
c7b5ce1c
AL
197
198 if (Stop == 0)
7bb525cb 199 return ScanError;
578bfd0a
AL
200
201 TagCount = 0;
fd71171a 202 while (TagCount+1 < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
578bfd0a 203 {
90d64280 204 // Start a new index and add it to the hash
c1a22377
AL
205 if (isspace(Stop[0]) == 0)
206 {
207 Indexes[TagCount++] = Stop - Section;
b2e465d6 208 AlphaIndexes[AlphaHash(Stop,End)] = TagCount;
c1a22377 209 }
0a8e3465 210
c1a22377 211 Stop = (const char *)memchr(Stop,'\n',End - Stop);
0a8e3465 212
c1a22377 213 if (Stop == 0)
7bb525cb 214 return ScanEOF;
138d4b3d 215
b8b7c37d 216 for (; Stop+1 < End && Stop[1] == '\r'; Stop++);
c1a22377 217
f3bcc383
AL
218 // Double newline marks the end of the record
219 if (Stop+1 < End && Stop[1] == '\n')
578bfd0a 220 {
578bfd0a 221 Indexes[TagCount] = Stop - Section;
fd71171a 222 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
7bb525cb 223 return ScanSuccess;
578bfd0a
AL
224 }
225
c1a22377
AL
226 Stop++;
227 }
138d4b3d 228
7bb525cb 229 return ScanEOF;
578bfd0a
AL
230}
231 /*}}}*/
b2e465d6
AL
232// TagSection::Trim - Trim off any trailing garbage /*{{{*/
233// ---------------------------------------------------------------------
234/* There should be exactly 1 newline at the end of the buffer, no more. */
235void pkgTagSection::Trim()
236{
237 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
238}
239 /*}}}*/
578bfd0a
AL
240// TagSection::Find - Locate a tag /*{{{*/
241// ---------------------------------------------------------------------
242/* This searches the section for a tag that matches the given string. */
b2e465d6 243bool pkgTagSection::Find(const char *Tag,unsigned &Pos) const
578bfd0a
AL
244{
245 unsigned int Length = strlen(Tag);
b2e465d6 246 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
c1a22377
AL
247 if (I == 0)
248 return false;
249 I--;
250
251 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
252 I = (I+1)%TagCount)
578bfd0a 253 {
c1a22377
AL
254 const char *St;
255 St = Section + Indexes[I];
256 if (strncasecmp(Tag,St,Length) != 0)
578bfd0a
AL
257 continue;
258
b2e465d6
AL
259 // Make sure the colon is in the right place
260 const char *C = St + Length;
261 for (; isspace(*C) != 0; C++);
262 if (*C != ':')
263 continue;
264 Pos = I;
265 return true;
266 }
267
268 Pos = 0;
269 return false;
270}
271 /*}}}*/
272// TagSection::Find - Locate a tag /*{{{*/
273// ---------------------------------------------------------------------
274/* This searches the section for a tag that matches the given string. */
275bool pkgTagSection::Find(const char *Tag,const char *&Start,
276 const char *&End) const
277{
278 unsigned int Length = strlen(Tag);
279 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
280 if (I == 0)
281 return false;
282 I--;
283
284 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
285 I = (I+1)%TagCount)
286 {
287 const char *St;
288 St = Section + Indexes[I];
289 if (strncasecmp(Tag,St,Length) != 0)
290 continue;
291
578bfd0a 292 // Make sure the colon is in the right place
c1a22377 293 const char *C = St + Length;
578bfd0a
AL
294 for (; isspace(*C) != 0; C++);
295 if (*C != ':')
296 continue;
297
298 // Strip off the gunk from the start end
299 Start = C;
300 End = Section + Indexes[I+1];
06bba740
AL
301 if (Start >= End)
302 return _error->Error("Internal parsing error");
303
578bfd0a
AL
304 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
305 for (; isspace(End[-1]) != 0 && End > Start; End--);
06bba740 306
578bfd0a
AL
307 return true;
308 }
c1a22377 309
578bfd0a
AL
310 Start = End = 0;
311 return false;
312}
313 /*}}}*/
0e66b144 314// TagSection::FindS - Find a string /*{{{*/
a05599f1
AL
315// ---------------------------------------------------------------------
316/* */
b2e465d6 317string pkgTagSection::FindS(const char *Tag) const
a05599f1
AL
318{
319 const char *Start;
320 const char *End;
321 if (Find(Tag,Start,End) == false)
322 return string();
323 return string(Start,End);
324}
325 /*}}}*/
326// TagSection::FindI - Find an integer /*{{{*/
327// ---------------------------------------------------------------------
328/* */
b2e465d6 329signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
a05599f1
AL
330{
331 const char *Start;
b0b4efb9
AL
332 const char *Stop;
333 if (Find(Tag,Start,Stop) == false)
334 return Default;
335
336 // Copy it into a temp buffer so we can use strtol
337 char S[300];
338 if ((unsigned)(Stop - Start) >= sizeof(S))
339 return Default;
340 strncpy(S,Start,Stop-Start);
341 S[Stop - Start] = 0;
342
343 char *End;
344 signed long Result = strtol(S,&End,10);
345 if (S == End)
346 return Default;
347 return Result;
348}
349 /*}}}*/
350// TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
351// ---------------------------------------------------------------------
352/* The bits marked in Flag are masked on/off in Flags */
353bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
b2e465d6 354 unsigned long Flag) const
b0b4efb9
AL
355{
356 const char *Start;
357 const char *Stop;
358 if (Find(Tag,Start,Stop) == false)
359 return true;
a05599f1 360
b0b4efb9
AL
361 switch (StringToBool(string(Start,Stop)))
362 {
363 case 0:
364 Flags &= ~Flag;
365 return true;
366
367 case 1:
368 Flags |= Flag;
369 return true;
370
371 default:
b2e465d6 372 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
b0b4efb9
AL
373 return true;
374 }
375 return true;
a05599f1
AL
376}
377 /*}}}*/
b2e465d6
AL
378
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. */
384
385/* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
386 array. */
387static const char *iTFRewritePackageOrder[] = {
388 "Package",
389 "Essential",
390 "Status",
391 "Priority",
392 "Section",
393 "Installed-Size",
394 "Maintainer",
395 "Architecture",
396 "Source",
397 "Version",
398 "Revision", // Obsolete
399 "Config-Version", // Obsolete
400 "Replaces",
401 "Provides",
402 "Depends",
403 "Pre-Depends",
404 "Recommends",
405 "Suggests",
406 "Conflicts",
407 "Conffiles",
408 "Filename",
409 "Size",
410 "MD5Sum",
a7c835af 411 "SHA1Sum",
b2e465d6
AL
412 "MSDOS-Filename", // Obsolete
413 "Description",
414 0};
415static const char *iTFRewriteSourceOrder[] = {"Package",
416 "Source",
417 "Binary",
418 "Version",
419 "Priority",
420 "Section",
421 "Maintainer",
422 "Build-Depends",
423 "Build-Depends-Indep",
424 "Build-Conflicts",
425 "Build-Conflicts-Indep",
426 "Architecture",
427 "Standards-Version",
428 "Format",
429 "Directory",
430 "Files",
431 0};
432
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 */
436const char **TFRewritePackageOrder = iTFRewritePackageOrder;
437const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
438
439bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
440 TFRewriteData *Rewrite)
441{
442 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
443 for (unsigned I = 0; I != 256; I++)
444 Visited[I] = 0;
445
446 // Set new tag up as necessary.
447 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
448 {
449 if (Rewrite[J].NewTag == 0)
450 Rewrite[J].NewTag = Rewrite[J].Tag;
451 }
452
453 // Write all all of the tags, in order.
454 for (unsigned int I = 0; Order[I] != 0; I++)
455 {
456 bool Rewritten = false;
457
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++)
460 {
461 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
462 {
463 Visited[J] |= 2;
464 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
465 {
466 if (isspace(Rewrite[J].Rewrite[0]))
467 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
468 else
469 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
470 }
471
472 Rewritten = true;
473 break;
474 }
475 }
476
477 // See if it is in the fragment
478 unsigned Pos;
479 if (Tags.Find(Order[I],Pos) == false)
480 continue;
481 Visited[Pos] |= 1;
482
483 if (Rewritten == true)
484 continue;
485
486 /* Write out this element, taking a moment to rewrite the tag
487 in case of changes of case. */
488 const char *Start;
489 const char *Stop;
490 Tags.Get(Start,Stop,Pos);
491
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");
499 }
500
501 // Now write all the old tags that were missed.
502 for (unsigned int I = 0; I != Tags.Count(); I++)
503 {
504 if ((Visited[I] & 1) == 1)
505 continue;
506
507 const char *Start;
508 const char *Stop;
509 Tags.Get(Start,Stop,I);
510 const char *End = Start;
511 for (; End < Stop && *End != ':'; End++);
512
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++)
516 {
517 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
518 {
519 Visited[J] |= 2;
520 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
521 {
522 if (isspace(Rewrite[J].Rewrite[0]))
523 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
524 else
525 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
526 }
527
528 Rewritten = true;
529 break;
530 }
531 }
532
533 if (Rewritten == true)
534 continue;
535
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");
541 }
542
543 // Now write all the rewrites that were missed
544 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
545 {
546 if ((Visited[J] & 2) == 2)
547 continue;
548
549 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
550 {
551 if (isspace(Rewrite[J].Rewrite[0]))
552 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
553 else
554 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
555 }
556 }
557
558 return true;
559}
560 /*}}}*/