]>
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 /* The size is increased by 4 because if we start with the Size of the
54 filename we need to try to read 1 char more to see an EOF faster, 1
55 char the end-pointer can be on and maybe 2 newlines need to be added
56 to the end of the file -> 4 extra chars */
58 d
= new pkgTagFilePrivate(pFd
, Size
);
60 if (d
->Fd
.IsOpen() == false)
61 d
->Start
= d
->End
= d
->Buffer
= 0;
63 d
->Buffer
= (char*)malloc(sizeof(char) * Size
);
65 if (d
->Buffer
== NULL
)
70 d
->Start
= d
->End
= d
->Buffer
;
76 // TagFile::~pkgTagFile - Destructor /*{{{*/
77 // ---------------------------------------------------------------------
79 pkgTagFile::~pkgTagFile()
85 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
86 unsigned long pkgTagFile::Offset()
91 // TagFile::Resize - Resize the internal buffer /*{{{*/
92 // ---------------------------------------------------------------------
93 /* Resize the internal buffer (double it in size). Fail if a maximum size
96 bool pkgTagFile::Resize()
98 // fail is the buffer grows too big
99 if(d
->Size
> 1024*1024+1)
102 return Resize(d
->Size
* 2);
104 bool pkgTagFile::Resize(unsigned long long const newSize
)
106 unsigned long long const EndSize
= d
->End
- d
->Start
;
108 // get new buffer and use it
109 char* newBuffer
= (char*)realloc(d
->Buffer
, sizeof(char) * newSize
);
110 if (newBuffer
== NULL
)
112 d
->Buffer
= newBuffer
;
115 // update the start/end pointers to the new buffer
116 d
->Start
= d
->Buffer
;
117 d
->End
= d
->Start
+ EndSize
;
121 // TagFile::Step - Advance to the next section /*{{{*/
122 // ---------------------------------------------------------------------
123 /* If the Section Scanner fails we refill the buffer and try again.
124 * If that fails too, double the buffer size and try again until a
125 * maximum buffer is reached.
127 bool pkgTagFile::Step(pkgTagSection
&Tag
)
129 while (Tag
.Scan(d
->Start
,d
->End
- d
->Start
) == false)
134 if(Tag
.Scan(d
->Start
,d
->End
- d
->Start
))
137 if (Resize() == false)
138 return _error
->Error(_("Unable to parse package file %s (1)"),
139 d
->Fd
.Name().c_str());
141 d
->Start
+= Tag
.size();
142 d
->iOffset
+= Tag
.size();
148 // TagFile::Fill - Top up the buffer /*{{{*/
149 // ---------------------------------------------------------------------
150 /* This takes the bit at the end of the buffer and puts it at the start
151 then fills the rest from the file */
152 bool pkgTagFile::Fill()
154 unsigned long long EndSize
= d
->End
- d
->Start
;
155 unsigned long long Actual
= 0;
157 memmove(d
->Buffer
,d
->Start
,EndSize
);
158 d
->Start
= d
->Buffer
;
159 d
->End
= d
->Buffer
+ EndSize
;
161 if (d
->Done
== false)
163 // See if only a bit of the file is left
164 unsigned long long const dataSize
= d
->Size
- ((d
->End
- d
->Buffer
) + 1);
165 if (d
->Fd
.Read(d
->End
, dataSize
, &Actual
) == false)
167 if (Actual
!= dataSize
|| d
->Fd
.Eof() == true)
174 if (EndSize
<= 3 && Actual
== 0)
176 if (d
->Size
- (d
->End
- d
->Buffer
) < 4)
179 // Append a double new line if one does not exist
180 unsigned int LineCount
= 0;
181 for (const char *E
= d
->End
- 1; E
- d
->End
< 6 && (*E
== '\n' || *E
== '\r'); E
--)
186 if ((unsigned)(d
->End
- d
->Buffer
) >= d
->Size
)
188 for (; LineCount
< 2; LineCount
++)
198 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
199 // ---------------------------------------------------------------------
200 /* This jumps to a pre-recorded file location and reads the record
202 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long long Offset
)
204 // We are within a buffer space of the next hit..
205 if (Offset
>= d
->iOffset
&& d
->iOffset
+ (d
->End
- d
->Start
) > Offset
)
207 unsigned long long Dist
= Offset
- d
->iOffset
;
213 // Reposition and reload..
216 if (d
->Fd
.Seek(Offset
) == false)
218 d
->End
= d
->Start
= d
->Buffer
;
223 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
) == true)
226 // This appends a double new line (for the real eof handling)
230 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
) == false)
231 return _error
->Error(_("Unable to parse package file %s (2)"),d
->Fd
.Name().c_str());
236 // TagSection::Scan - Scan for the end of the header information /*{{{*/
237 // ---------------------------------------------------------------------
238 /* This looks for the first double new line in the data stream.
239 It also indexes the tags in the section. */
240 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
)
242 const char *End
= Start
+ MaxLength
;
243 Stop
= Section
= Start
;
244 memset(AlphaIndexes
,0,sizeof(AlphaIndexes
));
250 while (TagCount
+1 < sizeof(Indexes
)/sizeof(Indexes
[0]) && Stop
< End
)
252 TrimRecord(true,End
);
254 // Start a new index and add it to the hash
255 if (isspace(Stop
[0]) == 0)
257 Indexes
[TagCount
++] = Stop
- Section
;
258 AlphaIndexes
[AlphaHash(Stop
,End
)] = TagCount
;
261 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
266 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++);
268 // Double newline marks the end of the record
269 if (Stop
+1 < End
&& Stop
[1] == '\n')
271 Indexes
[TagCount
] = Stop
- Section
;
272 TrimRecord(false,End
);
282 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
283 // ---------------------------------------------------------------------
284 /* There should be exactly 2 newline at the end of the record, no more. */
285 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
)
287 if (BeforeRecord
== true)
289 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
292 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
293 // ---------------------------------------------------------------------
294 /* There should be exactly 1 newline at the end of the buffer, no more. */
295 void pkgTagSection::Trim()
297 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
300 // TagSection::Exists - return True if a tag exists /*{{{*/
301 bool pkgTagSection::Exists(const char* const Tag
)
304 return Find(Tag
, tmp
);
307 // TagSection::Find - Locate a tag /*{{{*/
308 // ---------------------------------------------------------------------
309 /* This searches the section for a tag that matches the given string. */
310 bool pkgTagSection::Find(const char *Tag
,unsigned int &Pos
) const
312 unsigned int Length
= strlen(Tag
);
313 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
318 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
322 St
= Section
+ Indexes
[I
];
323 if (strncasecmp(Tag
,St
,Length
) != 0)
326 // Make sure the colon is in the right place
327 const char *C
= St
+ Length
;
328 for (; isspace(*C
) != 0; C
++);
339 // TagSection::Find - Locate a tag /*{{{*/
340 // ---------------------------------------------------------------------
341 /* This searches the section for a tag that matches the given string. */
342 bool pkgTagSection::Find(const char *Tag
,const char *&Start
,
343 const char *&End
) const
345 unsigned int Length
= strlen(Tag
);
346 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
351 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
355 St
= Section
+ Indexes
[I
];
356 if (strncasecmp(Tag
,St
,Length
) != 0)
359 // Make sure the colon is in the right place
360 const char *C
= St
+ Length
;
361 for (; isspace(*C
) != 0; C
++);
365 // Strip off the gunk from the start end
367 End
= Section
+ Indexes
[I
+1];
369 return _error
->Error("Internal parsing error");
371 for (; (isspace(*Start
) != 0 || *Start
== ':') && Start
< End
; Start
++);
372 for (; isspace(End
[-1]) != 0 && End
> Start
; End
--);
381 // TagSection::FindS - Find a string /*{{{*/
382 // ---------------------------------------------------------------------
384 string
pkgTagSection::FindS(const char *Tag
) const
388 if (Find(Tag
,Start
,End
) == false)
390 return string(Start
,End
);
393 // TagSection::FindI - Find an integer /*{{{*/
394 // ---------------------------------------------------------------------
396 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
400 if (Find(Tag
,Start
,Stop
) == false)
403 // Copy it into a temp buffer so we can use strtol
405 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
407 strncpy(S
,Start
,Stop
-Start
);
411 signed long Result
= strtol(S
,&End
,10);
417 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
418 // ---------------------------------------------------------------------
420 unsigned long long pkgTagSection::FindULL(const char *Tag
, unsigned long long const &Default
) const
424 if (Find(Tag
,Start
,Stop
) == false)
427 // Copy it into a temp buffer so we can use strtoull
429 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
431 strncpy(S
,Start
,Stop
-Start
);
435 unsigned long long Result
= strtoull(S
,&End
,10);
441 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
442 // ---------------------------------------------------------------------
443 /* The bits marked in Flag are masked on/off in Flags */
444 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
445 unsigned long Flag
) const
449 if (Find(Tag
,Start
,Stop
) == false)
451 return FindFlag(Flags
, Flag
, Start
, Stop
);
453 bool const pkgTagSection::FindFlag(unsigned long &Flags
, unsigned long Flag
,
454 char const* Start
, char const* Stop
)
456 switch (StringToBool(string(Start
, Stop
)))
467 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
473 // TFRewrite - Rewrite a control record /*{{{*/
474 // ---------------------------------------------------------------------
475 /* This writes the control record to stdout rewriting it as necessary. The
476 override map item specificies the rewriting rules to follow. This also
477 takes the time to sort the feild list. */
479 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
481 static const char *iTFRewritePackageOrder
[] = {
489 "Original-Maintainer",
493 "Revision", // Obsolete
494 "Config-Version", // Obsolete
510 "MSDOS-Filename", // Obsolete
513 static const char *iTFRewriteSourceOrder
[] = {"Package",
520 "Original-Maintainer",
522 "Build-Depends-Indep",
524 "Build-Conflicts-Indep",
532 /* Two levels of initialization are used because gcc will set the symbol
533 size of an array to the length of the array, causing dynamic relinking
534 errors. Doing this makes the symbol size constant */
535 const char **TFRewritePackageOrder
= iTFRewritePackageOrder
;
536 const char **TFRewriteSourceOrder
= iTFRewriteSourceOrder
;
538 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
539 TFRewriteData
*Rewrite
)
541 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
542 for (unsigned I
= 0; I
!= 256; I
++)
545 // Set new tag up as necessary.
546 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
548 if (Rewrite
[J
].NewTag
== 0)
549 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
552 // Write all all of the tags, in order.
553 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
555 bool Rewritten
= false;
557 // See if this is a field that needs to be rewritten
558 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
560 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
563 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
565 if (isspace(Rewrite
[J
].Rewrite
[0]))
566 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
568 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
576 // See if it is in the fragment
578 if (Tags
.Find(Order
[I
],Pos
) == false)
582 if (Rewritten
== true)
585 /* Write out this element, taking a moment to rewrite the tag
586 in case of changes of case. */
589 Tags
.Get(Start
,Stop
,Pos
);
591 if (fputs(Order
[I
],Output
) < 0)
592 return _error
->Errno("fputs","IO Error to output");
593 Start
+= strlen(Order
[I
]);
594 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
595 return _error
->Errno("fwrite","IO Error to output");
596 if (Stop
[-1] != '\n')
597 fprintf(Output
,"\n");
600 // Now write all the old tags that were missed.
601 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
603 if ((Visited
[I
] & 1) == 1)
608 Tags
.Get(Start
,Stop
,I
);
609 const char *End
= Start
;
610 for (; End
< Stop
&& *End
!= ':'; End
++);
612 // See if this is a field that needs to be rewritten
613 bool Rewritten
= false;
614 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
616 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
619 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
621 if (isspace(Rewrite
[J
].Rewrite
[0]))
622 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
624 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
632 if (Rewritten
== true)
635 // Write out this element
636 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
637 return _error
->Errno("fwrite","IO Error to output");
638 if (Stop
[-1] != '\n')
639 fprintf(Output
,"\n");
642 // Now write all the rewrites that were missed
643 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
645 if ((Visited
[J
] & 2) == 2)
648 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
650 if (isspace(Rewrite
[J
].Rewrite
[0]))
651 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
653 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);