]> git.saurik.com Git - apt.git/blame - apt-pkg/tagfile.cc
fix foldmarker in algorithms.h
[apt.git] / apt-pkg / tagfile.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
b3d44315 3// $Id: tagfile.cc,v 1.37.2.2 2003/12/31 16:02:30 mdz 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 /*{{{*/
ea542140
DK
14#include<config.h>
15
094a497d
AL
16#include <apt-pkg/tagfile.h>
17#include <apt-pkg/error.h>
cdcc6d34 18#include <apt-pkg/strutl.h>
472ff00e 19#include <apt-pkg/fileutl.h>
578bfd0a
AL
20
21#include <string>
22#include <stdio.h>
851a45a8 23#include <ctype.h>
ea542140
DK
24
25#include <apti18n.h>
578bfd0a
AL
26 /*}}}*/
27
851a45a8
AL
28using std::string;
29
1abbce9e
MV
30class pkgTagFilePrivate
31{
32public:
650faab0 33 pkgTagFilePrivate(FileFd *pFd, unsigned long long Size) : Fd(*pFd), Size(Size)
1abbce9e
MV
34 {
35 }
36 FileFd &Fd;
37 char *Buffer;
38 char *Start;
39 char *End;
40 bool Done;
650faab0
DK
41 unsigned long long iOffset;
42 unsigned long long Size;
1abbce9e
MV
43};
44
578bfd0a
AL
45// TagFile::pkgTagFile - Constructor /*{{{*/
46// ---------------------------------------------------------------------
47/* */
650faab0 48pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long long Size)
578bfd0a 49{
1abbce9e
MV
50 d = new pkgTagFilePrivate(pFd, Size);
51
52 if (d->Fd.IsOpen() == false)
0e72dd52 53 {
1abbce9e
MV
54 d->Start = d->End = d->Buffer = 0;
55 d->Done = true;
56 d->iOffset = 0;
0e72dd52
AL
57 return;
58 }
59
1abbce9e
MV
60 d->Buffer = new char[Size];
61 d->Start = d->End = d->Buffer;
62 d->Done = false;
63 d->iOffset = 0;
2ca99a0d 64 Fill();
578bfd0a
AL
65}
66 /*}}}*/
b2e465d6 67// TagFile::~pkgTagFile - Destructor /*{{{*/
29f7b36c
AL
68// ---------------------------------------------------------------------
69/* */
70pkgTagFile::~pkgTagFile()
71{
1abbce9e
MV
72 delete [] d->Buffer;
73 delete d;
74}
75 /*}}}*/
76// TagFile::Offset - Return the current offset in the buffer /*{{{*/
77unsigned long pkgTagFile::Offset()
78{
79 return d->iOffset;
29f7b36c
AL
80}
81 /*}}}*/
75c541fd 82// TagFile::Resize - Resize the internal buffer /*{{{*/
578bfd0a 83// ---------------------------------------------------------------------
75c541fd
MV
84/* Resize the internal buffer (double it in size). Fail if a maximum size
85 * size is reached.
86 */
87bool pkgTagFile::Resize()
578bfd0a 88{
75c541fd 89 char *tmp;
650faab0 90 unsigned long long EndSize = d->End - d->Start;
75c541fd
MV
91
92 // fail is the buffer grows too big
1abbce9e 93 if(d->Size > 1024*1024+1)
432b168c
MV
94 return false;
95
75c541fd 96 // get new buffer and use it
1abbce9e
MV
97 tmp = new char[2*d->Size];
98 memcpy(tmp, d->Buffer, d->Size);
99 d->Size = d->Size*2;
100 delete [] d->Buffer;
101 d->Buffer = tmp;
99c2e5ac 102
75c541fd 103 // update the start/end pointers to the new buffer
1abbce9e
MV
104 d->Start = d->Buffer;
105 d->End = d->Start + EndSize;
75c541fd
MV
106 return true;
107}
81e9789b 108 /*}}}*/
578bfd0a
AL
109// TagFile::Step - Advance to the next section /*{{{*/
110// ---------------------------------------------------------------------
75c541fd
MV
111/* If the Section Scanner fails we refill the buffer and try again.
112 * If that fails too, double the buffer size and try again until a
113 * maximum buffer is reached.
114 */
578bfd0a
AL
115bool pkgTagFile::Step(pkgTagSection &Tag)
116{
1abbce9e 117 while (Tag.Scan(d->Start,d->End - d->Start) == false)
0852eaef 118 {
99c2e5ac
MV
119 if (Fill() == false)
120 return false;
121
1abbce9e 122 if(Tag.Scan(d->Start,d->End - d->Start))
75c541fd
MV
123 break;
124
125 if (Resize() == false)
99c2e5ac 126 return _error->Error(_("Unable to parse package file %s (1)"),
1abbce9e 127 d->Fd.Name().c_str());
613f9499 128 }
1abbce9e
MV
129 d->Start += Tag.size();
130 d->iOffset += Tag.size();
b2e465d6
AL
131
132 Tag.Trim();
99c2e5ac
MV
133 return true;
134}
135 /*}}}*/
136// TagFile::Fill - Top up the buffer /*{{{*/
137// ---------------------------------------------------------------------
138/* This takes the bit at the end of the buffer and puts it at the start
139 then fills the rest from the file */
140bool pkgTagFile::Fill()
141{
650faab0
DK
142 unsigned long long EndSize = d->End - d->Start;
143 unsigned long long Actual = 0;
99c2e5ac 144
1abbce9e
MV
145 memmove(d->Buffer,d->Start,EndSize);
146 d->Start = d->Buffer;
147 d->End = d->Buffer + EndSize;
99c2e5ac 148
1abbce9e 149 if (d->Done == false)
99c2e5ac
MV
150 {
151 // See if only a bit of the file is left
1abbce9e 152 if (d->Fd.Read(d->End, d->Size - (d->End - d->Buffer),&Actual) == false)
99c2e5ac 153 return false;
1abbce9e
MV
154 if (Actual != d->Size - (d->End - d->Buffer))
155 d->Done = true;
156 d->End += Actual;
99c2e5ac
MV
157 }
158
1abbce9e 159 if (d->Done == true)
99c2e5ac
MV
160 {
161 if (EndSize <= 3 && Actual == 0)
162 return false;
1abbce9e 163 if (d->Size - (d->End - d->Buffer) < 4)
99c2e5ac
MV
164 return true;
165
166 // Append a double new line if one does not exist
167 unsigned int LineCount = 0;
1abbce9e 168 for (const char *E = d->End - 1; E - d->End < 6 && (*E == '\n' || *E == '\r'); E--)
99c2e5ac
MV
169 if (*E == '\n')
170 LineCount++;
171 for (; LineCount < 2; LineCount++)
1abbce9e 172 *d->End++ = '\n';
99c2e5ac
MV
173
174 return true;
175 }
176
578bfd0a
AL
177 return true;
178}
179 /*}}}*/
ad00ae81
AL
180// TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
181// ---------------------------------------------------------------------
03e39e59
AL
182/* This jumps to a pre-recorded file location and reads the record
183 that is there */
650faab0 184bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
ad00ae81 185{
b2e465d6 186 // We are within a buffer space of the next hit..
1abbce9e 187 if (Offset >= d->iOffset && d->iOffset + (d->End - d->Start) > Offset)
b2e465d6 188 {
650faab0 189 unsigned long long Dist = Offset - d->iOffset;
1abbce9e
MV
190 d->Start += Dist;
191 d->iOffset += Dist;
b2e465d6
AL
192 return Step(Tag);
193 }
194
2ca99a0d 195 // Reposition and reload..
1abbce9e
MV
196 d->iOffset = Offset;
197 d->Done = false;
198 if (d->Fd.Seek(Offset) == false)
2ca99a0d 199 return false;
1abbce9e 200 d->End = d->Start = d->Buffer;
99c2e5ac 201
2ca99a0d
MV
202 if (Fill() == false)
203 return false;
99c2e5ac 204
1abbce9e 205 if (Tag.Scan(d->Start, d->End - d->Start) == true)
2ca99a0d
MV
206 return true;
207
208 // This appends a double new line (for the real eof handling)
209 if (Fill() == false)
210 return false;
0852eaef 211
1abbce9e
MV
212 if (Tag.Scan(d->Start, d->End - d->Start) == false)
213 return _error->Error(_("Unable to parse package file %s (2)"),d->Fd.Name().c_str());
06bba740 214
ad00ae81
AL
215 return true;
216}
217 /*}}}*/
578bfd0a
AL
218// TagSection::Scan - Scan for the end of the header information /*{{{*/
219// ---------------------------------------------------------------------
c176c4d0
DK
220/* This looks for the first double new line in the data stream.
221 It also indexes the tags in the section. */
0852eaef 222bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
578bfd0a
AL
223{
224 const char *End = Start + MaxLength;
225 Stop = Section = Start;
c1a22377 226 memset(AlphaIndexes,0,sizeof(AlphaIndexes));
c7b5ce1c 227
2ca99a0d 228 if (Stop == 0)
0852eaef 229 return false;
81e9789b 230
578bfd0a 231 TagCount = 0;
fd71171a 232 while (TagCount+1 < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
578bfd0a 233 {
81e9789b
MV
234 TrimRecord(true,End);
235
90d64280 236 // Start a new index and add it to the hash
c1a22377
AL
237 if (isspace(Stop[0]) == 0)
238 {
239 Indexes[TagCount++] = Stop - Section;
b2e465d6 240 AlphaIndexes[AlphaHash(Stop,End)] = TagCount;
c1a22377 241 }
0a8e3465 242
c1a22377 243 Stop = (const char *)memchr(Stop,'\n',End - Stop);
0a8e3465 244
c1a22377 245 if (Stop == 0)
0852eaef 246 return false;
81e9789b 247
b8b7c37d 248 for (; Stop+1 < End && Stop[1] == '\r'; Stop++);
c1a22377 249
f3bcc383
AL
250 // Double newline marks the end of the record
251 if (Stop+1 < End && Stop[1] == '\n')
578bfd0a 252 {
578bfd0a 253 Indexes[TagCount] = Stop - Section;
81e9789b 254 TrimRecord(false,End);
0852eaef 255 return true;
578bfd0a
AL
256 }
257
c1a22377
AL
258 Stop++;
259 }
138d4b3d 260
0852eaef 261 return false;
578bfd0a
AL
262}
263 /*}}}*/
81e9789b
MV
264// TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
265// ---------------------------------------------------------------------
266/* There should be exactly 2 newline at the end of the record, no more. */
267void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
268{
269 if (BeforeRecord == true)
270 return;
271 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
272}
273 /*}}}*/
b2e465d6
AL
274// TagSection::Trim - Trim off any trailing garbage /*{{{*/
275// ---------------------------------------------------------------------
276/* There should be exactly 1 newline at the end of the buffer, no more. */
277void pkgTagSection::Trim()
278{
279 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
280}
281 /*}}}*/
578bfd0a
AL
282// TagSection::Find - Locate a tag /*{{{*/
283// ---------------------------------------------------------------------
284/* This searches the section for a tag that matches the given string. */
b2e465d6 285bool pkgTagSection::Find(const char *Tag,unsigned &Pos) const
578bfd0a
AL
286{
287 unsigned int Length = strlen(Tag);
b2e465d6 288 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
c1a22377
AL
289 if (I == 0)
290 return false;
291 I--;
292
293 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
294 I = (I+1)%TagCount)
578bfd0a 295 {
c1a22377
AL
296 const char *St;
297 St = Section + Indexes[I];
298 if (strncasecmp(Tag,St,Length) != 0)
578bfd0a
AL
299 continue;
300
b2e465d6
AL
301 // Make sure the colon is in the right place
302 const char *C = St + Length;
303 for (; isspace(*C) != 0; C++);
304 if (*C != ':')
305 continue;
306 Pos = I;
307 return true;
308 }
309
310 Pos = 0;
311 return false;
312}
313 /*}}}*/
314// TagSection::Find - Locate a tag /*{{{*/
315// ---------------------------------------------------------------------
316/* This searches the section for a tag that matches the given string. */
317bool pkgTagSection::Find(const char *Tag,const char *&Start,
318 const char *&End) const
319{
320 unsigned int Length = strlen(Tag);
321 unsigned int I = AlphaIndexes[AlphaHash(Tag)];
322 if (I == 0)
323 return false;
324 I--;
325
326 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
327 I = (I+1)%TagCount)
328 {
329 const char *St;
330 St = Section + Indexes[I];
331 if (strncasecmp(Tag,St,Length) != 0)
332 continue;
333
578bfd0a 334 // Make sure the colon is in the right place
c1a22377 335 const char *C = St + Length;
578bfd0a
AL
336 for (; isspace(*C) != 0; C++);
337 if (*C != ':')
338 continue;
339
340 // Strip off the gunk from the start end
341 Start = C;
342 End = Section + Indexes[I+1];
06bba740
AL
343 if (Start >= End)
344 return _error->Error("Internal parsing error");
345
578bfd0a
AL
346 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
347 for (; isspace(End[-1]) != 0 && End > Start; End--);
06bba740 348
578bfd0a
AL
349 return true;
350 }
c1a22377 351
578bfd0a
AL
352 Start = End = 0;
353 return false;
354}
355 /*}}}*/
0e66b144 356// TagSection::FindS - Find a string /*{{{*/
a05599f1
AL
357// ---------------------------------------------------------------------
358/* */
b2e465d6 359string pkgTagSection::FindS(const char *Tag) const
a05599f1
AL
360{
361 const char *Start;
362 const char *End;
363 if (Find(Tag,Start,End) == false)
364 return string();
365 return string(Start,End);
366}
367 /*}}}*/
368// TagSection::FindI - Find an integer /*{{{*/
369// ---------------------------------------------------------------------
370/* */
b2e465d6 371signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
a05599f1
AL
372{
373 const char *Start;
b0b4efb9
AL
374 const char *Stop;
375 if (Find(Tag,Start,Stop) == false)
376 return Default;
377
378 // Copy it into a temp buffer so we can use strtol
379 char S[300];
380 if ((unsigned)(Stop - Start) >= sizeof(S))
381 return Default;
382 strncpy(S,Start,Stop-Start);
383 S[Stop - Start] = 0;
384
385 char *End;
386 signed long Result = strtol(S,&End,10);
387 if (S == End)
388 return Default;
389 return Result;
390}
391 /*}}}*/
e2c66de5
DK
392// TagSection::FindULL - Find an unsigned long long integer /*{{{*/
393// ---------------------------------------------------------------------
394/* */
395unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long const &Default) const
396{
397 const char *Start;
398 const char *Stop;
399 if (Find(Tag,Start,Stop) == false)
400 return Default;
401
402 // Copy it into a temp buffer so we can use strtoull
403 char S[100];
404 if ((unsigned)(Stop - Start) >= sizeof(S))
405 return Default;
406 strncpy(S,Start,Stop-Start);
407 S[Stop - Start] = 0;
408
409 char *End;
410 unsigned long long Result = strtoull(S,&End,10);
411 if (S == End)
412 return Default;
413 return Result;
414}
415 /*}}}*/
b0b4efb9
AL
416// TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
417// ---------------------------------------------------------------------
418/* The bits marked in Flag are masked on/off in Flags */
419bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
b2e465d6 420 unsigned long Flag) const
b0b4efb9
AL
421{
422 const char *Start;
423 const char *Stop;
424 if (Find(Tag,Start,Stop) == false)
425 return true;
fe0f7911
DK
426 return FindFlag(Flags, Flag, Start, Stop);
427}
428bool const pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
429 char const* Start, char const* Stop)
430{
431 switch (StringToBool(string(Start, Stop)))
432 {
b0b4efb9
AL
433 case 0:
434 Flags &= ~Flag;
435 return true;
436
437 case 1:
438 Flags |= Flag;
439 return true;
440
441 default:
b2e465d6 442 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
b0b4efb9
AL
443 return true;
444 }
445 return true;
a05599f1
AL
446}
447 /*}}}*/
b2e465d6
AL
448// TFRewrite - Rewrite a control record /*{{{*/
449// ---------------------------------------------------------------------
450/* This writes the control record to stdout rewriting it as necessary. The
451 override map item specificies the rewriting rules to follow. This also
452 takes the time to sort the feild list. */
453
454/* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
455 array. */
456static const char *iTFRewritePackageOrder[] = {
457 "Package",
458 "Essential",
459 "Status",
460 "Priority",
461 "Section",
462 "Installed-Size",
463 "Maintainer",
47e7ebb3 464 "Original-Maintainer",
b2e465d6
AL
465 "Architecture",
466 "Source",
467 "Version",
468 "Revision", // Obsolete
469 "Config-Version", // Obsolete
470 "Replaces",
471 "Provides",
472 "Depends",
473 "Pre-Depends",
474 "Recommends",
475 "Suggests",
476 "Conflicts",
308c7d30 477 "Breaks",
b2e465d6
AL
478 "Conffiles",
479 "Filename",
480 "Size",
481 "MD5Sum",
cde41ae8
MV
482 "SHA1",
483 "SHA256",
d9b9e9e2 484 "SHA512",
b2e465d6
AL
485 "MSDOS-Filename", // Obsolete
486 "Description",
487 0};
488static const char *iTFRewriteSourceOrder[] = {"Package",
489 "Source",
490 "Binary",
491 "Version",
492 "Priority",
493 "Section",
494 "Maintainer",
47e7ebb3 495 "Original-Maintainer",
b2e465d6
AL
496 "Build-Depends",
497 "Build-Depends-Indep",
498 "Build-Conflicts",
499 "Build-Conflicts-Indep",
500 "Architecture",
501 "Standards-Version",
502 "Format",
503 "Directory",
504 "Files",
505 0};
506
507/* Two levels of initialization are used because gcc will set the symbol
508 size of an array to the length of the array, causing dynamic relinking
509 errors. Doing this makes the symbol size constant */
510const char **TFRewritePackageOrder = iTFRewritePackageOrder;
511const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
512
513bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
514 TFRewriteData *Rewrite)
515{
516 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
517 for (unsigned I = 0; I != 256; I++)
518 Visited[I] = 0;
519
520 // Set new tag up as necessary.
521 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
522 {
523 if (Rewrite[J].NewTag == 0)
524 Rewrite[J].NewTag = Rewrite[J].Tag;
525 }
526
527 // Write all all of the tags, in order.
528 for (unsigned int I = 0; Order[I] != 0; I++)
529 {
530 bool Rewritten = false;
531
532 // See if this is a field that needs to be rewritten
533 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
534 {
535 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
536 {
537 Visited[J] |= 2;
538 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
539 {
540 if (isspace(Rewrite[J].Rewrite[0]))
541 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
542 else
543 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
544 }
545
546 Rewritten = true;
547 break;
548 }
549 }
550
551 // See if it is in the fragment
552 unsigned Pos;
553 if (Tags.Find(Order[I],Pos) == false)
554 continue;
555 Visited[Pos] |= 1;
556
557 if (Rewritten == true)
558 continue;
559
560 /* Write out this element, taking a moment to rewrite the tag
561 in case of changes of case. */
562 const char *Start;
563 const char *Stop;
564 Tags.Get(Start,Stop,Pos);
565
566 if (fputs(Order[I],Output) < 0)
567 return _error->Errno("fputs","IO Error to output");
568 Start += strlen(Order[I]);
569 if (fwrite(Start,Stop - Start,1,Output) != 1)
570 return _error->Errno("fwrite","IO Error to output");
571 if (Stop[-1] != '\n')
572 fprintf(Output,"\n");
573 }
574
575 // Now write all the old tags that were missed.
576 for (unsigned int I = 0; I != Tags.Count(); I++)
577 {
578 if ((Visited[I] & 1) == 1)
579 continue;
580
581 const char *Start;
582 const char *Stop;
583 Tags.Get(Start,Stop,I);
584 const char *End = Start;
585 for (; End < Stop && *End != ':'; End++);
586
587 // See if this is a field that needs to be rewritten
588 bool Rewritten = false;
589 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
590 {
591 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
592 {
593 Visited[J] |= 2;
594 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
595 {
596 if (isspace(Rewrite[J].Rewrite[0]))
597 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
598 else
599 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
600 }
601
602 Rewritten = true;
603 break;
604 }
605 }
606
607 if (Rewritten == true)
608 continue;
609
610 // Write out this element
611 if (fwrite(Start,Stop - Start,1,Output) != 1)
612 return _error->Errno("fwrite","IO Error to output");
613 if (Stop[-1] != '\n')
614 fprintf(Output,"\n");
615 }
616
617 // Now write all the rewrites that were missed
618 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
619 {
620 if ((Visited[J] & 2) == 2)
621 continue;
622
623 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
624 {
625 if (isspace(Rewrite[J].Rewrite[0]))
626 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
627 else
628 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
629 }
630 }
631
632 return true;
633}
634 /*}}}*/