]>
git.saurik.com Git - apt.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 /*{{{*/
16 #include <apt-pkg/tagfile.h>
17 #include <apt-pkg/error.h>
18 #include <apt-pkg/strutl.h>
19 #include <apt-pkg/fileutl.h>
30 class pkgTagFilePrivate
33 pkgTagFilePrivate(FileFd
*pFd
, unsigned long long Size
) : Fd(*pFd
), Buffer(NULL
),
34 Start(NULL
), End(NULL
),
35 Done(false), iOffset(0),
44 unsigned long long iOffset
;
45 unsigned long long Size
;
48 // TagFile::pkgTagFile - Constructor /*{{{*/
49 // ---------------------------------------------------------------------
51 pkgTagFile::pkgTagFile(FileFd
*pFd
,unsigned long long Size
)
53 d
= new pkgTagFilePrivate(pFd
, Size
);
55 if (d
->Fd
.IsOpen() == false)
57 d
->Start
= d
->End
= d
->Buffer
= 0;
63 d
->Buffer
= new char[Size
];
64 d
->Start
= d
->End
= d
->Buffer
;
70 // TagFile::~pkgTagFile - Destructor /*{{{*/
71 // ---------------------------------------------------------------------
73 pkgTagFile::~pkgTagFile()
79 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
80 unsigned long pkgTagFile::Offset()
85 // TagFile::Resize - Resize the internal buffer /*{{{*/
86 // ---------------------------------------------------------------------
87 /* Resize the internal buffer (double it in size). Fail if a maximum size
90 bool pkgTagFile::Resize()
93 unsigned long long EndSize
= d
->End
- d
->Start
;
95 // fail is the buffer grows too big
96 if(d
->Size
> 1024*1024+1)
99 // get new buffer and use it
100 tmp
= new char[2*d
->Size
];
101 memcpy(tmp
, d
->Buffer
, d
->Size
);
106 // update the start/end pointers to the new buffer
107 d
->Start
= d
->Buffer
;
108 d
->End
= d
->Start
+ EndSize
;
112 // TagFile::Step - Advance to the next section /*{{{*/
113 // ---------------------------------------------------------------------
114 /* If the Section Scanner fails we refill the buffer and try again.
115 * If that fails too, double the buffer size and try again until a
116 * maximum buffer is reached.
118 bool pkgTagFile::Step(pkgTagSection
&Tag
)
120 while (Tag
.Scan(d
->Start
,d
->End
- d
->Start
) == false)
125 if(Tag
.Scan(d
->Start
,d
->End
- d
->Start
))
128 if (Resize() == false)
129 return _error
->Error(_("Unable to parse package file %s (1)"),
130 d
->Fd
.Name().c_str());
132 d
->Start
+= Tag
.size();
133 d
->iOffset
+= Tag
.size();
139 // TagFile::Fill - Top up the buffer /*{{{*/
140 // ---------------------------------------------------------------------
141 /* This takes the bit at the end of the buffer and puts it at the start
142 then fills the rest from the file */
143 bool pkgTagFile::Fill()
145 unsigned long long EndSize
= d
->End
- d
->Start
;
146 unsigned long long Actual
= 0;
148 memmove(d
->Buffer
,d
->Start
,EndSize
);
149 d
->Start
= d
->Buffer
;
150 d
->End
= d
->Buffer
+ EndSize
;
152 if (d
->Done
== false)
154 // See if only a bit of the file is left
155 if (d
->Fd
.Read(d
->End
, d
->Size
- (d
->End
- d
->Buffer
),&Actual
) == false)
157 if (Actual
!= d
->Size
- (d
->End
- d
->Buffer
))
164 if (EndSize
<= 3 && Actual
== 0)
166 if (d
->Size
- (d
->End
- d
->Buffer
) < 4)
169 // Append a double new line if one does not exist
170 unsigned int LineCount
= 0;
171 for (const char *E
= d
->End
- 1; E
- d
->End
< 6 && (*E
== '\n' || *E
== '\r'); E
--)
174 for (; LineCount
< 2; LineCount
++)
183 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
184 // ---------------------------------------------------------------------
185 /* This jumps to a pre-recorded file location and reads the record
187 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long long Offset
)
189 // We are within a buffer space of the next hit..
190 if (Offset
>= d
->iOffset
&& d
->iOffset
+ (d
->End
- d
->Start
) > Offset
)
192 unsigned long long Dist
= Offset
- d
->iOffset
;
198 // Reposition and reload..
201 if (d
->Fd
.Seek(Offset
) == false)
203 d
->End
= d
->Start
= d
->Buffer
;
208 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
) == true)
211 // This appends a double new line (for the real eof handling)
215 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
) == false)
216 return _error
->Error(_("Unable to parse package file %s (2)"),d
->Fd
.Name().c_str());
221 // pkgTagSection::pkgTagSection - Constructor /*{{{*/
222 // ---------------------------------------------------------------------
224 pkgTagSection::pkgTagSection()
225 : Section(0), TagCount(0), Stop(0), d(NULL
)
227 memset(&Indexes
, 0, sizeof(Indexes
));
228 memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
));
231 // TagSection::Scan - Scan for the end of the header information /*{{{*/
232 // ---------------------------------------------------------------------
233 /* This looks for the first double new line in the data stream.
234 It also indexes the tags in the section. */
235 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
)
237 const char *End
= Start
+ MaxLength
;
238 Stop
= Section
= Start
;
239 memset(AlphaIndexes
,0,sizeof(AlphaIndexes
));
245 while (TagCount
+1 < sizeof(Indexes
)/sizeof(Indexes
[0]) && Stop
< End
)
247 TrimRecord(true,End
);
249 // Start a new index and add it to the hash
250 if (isspace(Stop
[0]) == 0)
252 Indexes
[TagCount
++] = Stop
- Section
;
253 AlphaIndexes
[AlphaHash(Stop
,End
)] = TagCount
;
256 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
261 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++);
263 // Double newline marks the end of the record
264 if (Stop
+1 < End
&& Stop
[1] == '\n')
266 Indexes
[TagCount
] = Stop
- Section
;
267 TrimRecord(false,End
);
277 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
278 // ---------------------------------------------------------------------
279 /* There should be exactly 2 newline at the end of the record, no more. */
280 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
)
282 if (BeforeRecord
== true)
284 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
287 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
288 // ---------------------------------------------------------------------
289 /* There should be exactly 1 newline at the end of the buffer, no more. */
290 void pkgTagSection::Trim()
292 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
295 // TagSection::Exists - return True if a tag exists /*{{{*/
296 bool pkgTagSection::Exists(const char* const Tag
)
299 return Find(Tag
, tmp
);
302 // TagSection::Find - Locate a tag /*{{{*/
303 // ---------------------------------------------------------------------
304 /* This searches the section for a tag that matches the given string. */
305 bool pkgTagSection::Find(const char *Tag
,unsigned int &Pos
) const
307 unsigned int Length
= strlen(Tag
);
308 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
313 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
317 St
= Section
+ Indexes
[I
];
318 if (strncasecmp(Tag
,St
,Length
) != 0)
321 // Make sure the colon is in the right place
322 const char *C
= St
+ Length
;
323 for (; isspace(*C
) != 0; C
++);
334 // TagSection::Find - Locate a tag /*{{{*/
335 // ---------------------------------------------------------------------
336 /* This searches the section for a tag that matches the given string. */
337 bool pkgTagSection::Find(const char *Tag
,const char *&Start
,
338 const char *&End
) const
340 unsigned int Length
= strlen(Tag
);
341 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
346 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
350 St
= Section
+ Indexes
[I
];
351 if (strncasecmp(Tag
,St
,Length
) != 0)
354 // Make sure the colon is in the right place
355 const char *C
= St
+ Length
;
356 for (; isspace(*C
) != 0; C
++);
360 // Strip off the gunk from the start end
362 End
= Section
+ Indexes
[I
+1];
364 return _error
->Error("Internal parsing error");
366 for (; (isspace(*Start
) != 0 || *Start
== ':') && Start
< End
; Start
++);
367 for (; isspace(End
[-1]) != 0 && End
> Start
; End
--);
376 // TagSection::FindS - Find a string /*{{{*/
377 // ---------------------------------------------------------------------
379 string
pkgTagSection::FindS(const char *Tag
) const
383 if (Find(Tag
,Start
,End
) == false)
385 return string(Start
,End
);
388 // TagSection::FindI - Find an integer /*{{{*/
389 // ---------------------------------------------------------------------
391 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
395 if (Find(Tag
,Start
,Stop
) == false)
398 // Copy it into a temp buffer so we can use strtol
400 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
402 strncpy(S
,Start
,Stop
-Start
);
406 signed long Result
= strtol(S
,&End
,10);
412 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
413 // ---------------------------------------------------------------------
415 unsigned long long pkgTagSection::FindULL(const char *Tag
, unsigned long long const &Default
) const
419 if (Find(Tag
,Start
,Stop
) == false)
422 // Copy it into a temp buffer so we can use strtoull
424 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
426 strncpy(S
,Start
,Stop
-Start
);
430 unsigned long long Result
= strtoull(S
,&End
,10);
436 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
437 // ---------------------------------------------------------------------
438 /* The bits marked in Flag are masked on/off in Flags */
439 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
440 unsigned long Flag
) const
444 if (Find(Tag
,Start
,Stop
) == false)
446 return FindFlag(Flags
, Flag
, Start
, Stop
);
448 bool const pkgTagSection::FindFlag(unsigned long &Flags
, unsigned long Flag
,
449 char const* Start
, char const* Stop
)
451 switch (StringToBool(string(Start
, Stop
)))
462 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
468 // TFRewrite - Rewrite a control record /*{{{*/
469 // ---------------------------------------------------------------------
470 /* This writes the control record to stdout rewriting it as necessary. The
471 override map item specificies the rewriting rules to follow. This also
472 takes the time to sort the feild list. */
474 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
476 static const char *iTFRewritePackageOrder
[] = {
484 "Original-Maintainer",
488 "Revision", // Obsolete
489 "Config-Version", // Obsolete
505 "MSDOS-Filename", // Obsolete
508 static const char *iTFRewriteSourceOrder
[] = {"Package",
515 "Original-Maintainer",
517 "Build-Depends-Indep",
519 "Build-Conflicts-Indep",
527 /* Two levels of initialization are used because gcc will set the symbol
528 size of an array to the length of the array, causing dynamic relinking
529 errors. Doing this makes the symbol size constant */
530 const char **TFRewritePackageOrder
= iTFRewritePackageOrder
;
531 const char **TFRewriteSourceOrder
= iTFRewriteSourceOrder
;
533 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
534 TFRewriteData
*Rewrite
)
536 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
537 for (unsigned I
= 0; I
!= 256; I
++)
540 // Set new tag up as necessary.
541 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
543 if (Rewrite
[J
].NewTag
== 0)
544 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
547 // Write all all of the tags, in order.
548 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
550 bool Rewritten
= false;
552 // See if this is a field that needs to be rewritten
553 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
555 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
558 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
560 if (isspace(Rewrite
[J
].Rewrite
[0]))
561 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
563 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
571 // See if it is in the fragment
573 if (Tags
.Find(Order
[I
],Pos
) == false)
577 if (Rewritten
== true)
580 /* Write out this element, taking a moment to rewrite the tag
581 in case of changes of case. */
584 Tags
.Get(Start
,Stop
,Pos
);
586 if (fputs(Order
[I
],Output
) < 0)
587 return _error
->Errno("fputs","IO Error to output");
588 Start
+= strlen(Order
[I
]);
589 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
590 return _error
->Errno("fwrite","IO Error to output");
591 if (Stop
[-1] != '\n')
592 fprintf(Output
,"\n");
595 // Now write all the old tags that were missed.
596 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
598 if ((Visited
[I
] & 1) == 1)
603 Tags
.Get(Start
,Stop
,I
);
604 const char *End
= Start
;
605 for (; End
< Stop
&& *End
!= ':'; End
++);
607 // See if this is a field that needs to be rewritten
608 bool Rewritten
= false;
609 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
611 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
614 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
616 if (isspace(Rewrite
[J
].Rewrite
[0]))
617 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
619 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
627 if (Rewritten
== true)
630 // Write out this element
631 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
632 return _error
->Errno("fwrite","IO Error to output");
633 if (Stop
[-1] != '\n')
634 fprintf(Output
,"\n");
637 // Now write all the rewrites that were missed
638 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
640 if ((Visited
[J
] & 2) == 2)
643 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
645 if (isspace(Rewrite
[J
].Rewrite
[0]))
646 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
648 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);