]>
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 // TagSection::Scan - Scan for the end of the header information /*{{{*/
222 // ---------------------------------------------------------------------
223 /* This looks for the first double new line in the data stream.
224 It also indexes the tags in the section. */
225 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
)
227 const char *End
= Start
+ MaxLength
;
228 Stop
= Section
= Start
;
229 memset(AlphaIndexes
,0,sizeof(AlphaIndexes
));
235 while (TagCount
+1 < sizeof(Indexes
)/sizeof(Indexes
[0]) && Stop
< End
)
237 TrimRecord(true,End
);
239 // Start a new index and add it to the hash
240 if (isspace(Stop
[0]) == 0)
242 Indexes
[TagCount
++] = Stop
- Section
;
243 AlphaIndexes
[AlphaHash(Stop
,End
)] = TagCount
;
246 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
251 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++);
253 // Double newline marks the end of the record
254 if (Stop
+1 < End
&& Stop
[1] == '\n')
256 Indexes
[TagCount
] = Stop
- Section
;
257 TrimRecord(false,End
);
267 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
268 // ---------------------------------------------------------------------
269 /* There should be exactly 2 newline at the end of the record, no more. */
270 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
)
272 if (BeforeRecord
== true)
274 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
277 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
278 // ---------------------------------------------------------------------
279 /* There should be exactly 1 newline at the end of the buffer, no more. */
280 void pkgTagSection::Trim()
282 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
285 // TagSection::Find - Locate a tag /*{{{*/
286 // ---------------------------------------------------------------------
287 /* This searches the section for a tag that matches the given string. */
288 bool pkgTagSection::Find(const char *Tag
,unsigned &Pos
) const
290 unsigned int Length
= strlen(Tag
);
291 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
296 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
300 St
= Section
+ Indexes
[I
];
301 if (strncasecmp(Tag
,St
,Length
) != 0)
304 // Make sure the colon is in the right place
305 const char *C
= St
+ Length
;
306 for (; isspace(*C
) != 0; C
++);
317 // TagSection::Find - Locate a tag /*{{{*/
318 // ---------------------------------------------------------------------
319 /* This searches the section for a tag that matches the given string. */
320 bool pkgTagSection::Find(const char *Tag
,const char *&Start
,
321 const char *&End
) const
323 unsigned int Length
= strlen(Tag
);
324 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
329 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
333 St
= Section
+ Indexes
[I
];
334 if (strncasecmp(Tag
,St
,Length
) != 0)
337 // Make sure the colon is in the right place
338 const char *C
= St
+ Length
;
339 for (; isspace(*C
) != 0; C
++);
343 // Strip off the gunk from the start end
345 End
= Section
+ Indexes
[I
+1];
347 return _error
->Error("Internal parsing error");
349 for (; (isspace(*Start
) != 0 || *Start
== ':') && Start
< End
; Start
++);
350 for (; isspace(End
[-1]) != 0 && End
> Start
; End
--);
359 // TagSection::FindS - Find a string /*{{{*/
360 // ---------------------------------------------------------------------
362 string
pkgTagSection::FindS(const char *Tag
) const
366 if (Find(Tag
,Start
,End
) == false)
368 return string(Start
,End
);
371 // TagSection::FindI - Find an integer /*{{{*/
372 // ---------------------------------------------------------------------
374 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
378 if (Find(Tag
,Start
,Stop
) == false)
381 // Copy it into a temp buffer so we can use strtol
383 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
385 strncpy(S
,Start
,Stop
-Start
);
389 signed long Result
= strtol(S
,&End
,10);
395 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
396 // ---------------------------------------------------------------------
398 unsigned long long pkgTagSection::FindULL(const char *Tag
, unsigned long long const &Default
) const
402 if (Find(Tag
,Start
,Stop
) == false)
405 // Copy it into a temp buffer so we can use strtoull
407 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
409 strncpy(S
,Start
,Stop
-Start
);
413 unsigned long long Result
= strtoull(S
,&End
,10);
419 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
420 // ---------------------------------------------------------------------
421 /* The bits marked in Flag are masked on/off in Flags */
422 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
423 unsigned long Flag
) const
427 if (Find(Tag
,Start
,Stop
) == false)
429 return FindFlag(Flags
, Flag
, Start
, Stop
);
431 bool const pkgTagSection::FindFlag(unsigned long &Flags
, unsigned long Flag
,
432 char const* Start
, char const* Stop
)
434 switch (StringToBool(string(Start
, Stop
)))
445 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
451 // TFRewrite - Rewrite a control record /*{{{*/
452 // ---------------------------------------------------------------------
453 /* This writes the control record to stdout rewriting it as necessary. The
454 override map item specificies the rewriting rules to follow. This also
455 takes the time to sort the feild list. */
457 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
459 static const char *iTFRewritePackageOrder
[] = {
467 "Original-Maintainer",
471 "Revision", // Obsolete
472 "Config-Version", // Obsolete
488 "MSDOS-Filename", // Obsolete
491 static const char *iTFRewriteSourceOrder
[] = {"Package",
498 "Original-Maintainer",
500 "Build-Depends-Indep",
502 "Build-Conflicts-Indep",
510 /* Two levels of initialization are used because gcc will set the symbol
511 size of an array to the length of the array, causing dynamic relinking
512 errors. Doing this makes the symbol size constant */
513 const char **TFRewritePackageOrder
= iTFRewritePackageOrder
;
514 const char **TFRewriteSourceOrder
= iTFRewriteSourceOrder
;
516 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
517 TFRewriteData
*Rewrite
)
519 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
520 for (unsigned I
= 0; I
!= 256; I
++)
523 // Set new tag up as necessary.
524 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
526 if (Rewrite
[J
].NewTag
== 0)
527 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
530 // Write all all of the tags, in order.
531 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
533 bool Rewritten
= false;
535 // See if this is a field that needs to be rewritten
536 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
538 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
541 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
543 if (isspace(Rewrite
[J
].Rewrite
[0]))
544 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
546 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
554 // See if it is in the fragment
556 if (Tags
.Find(Order
[I
],Pos
) == false)
560 if (Rewritten
== true)
563 /* Write out this element, taking a moment to rewrite the tag
564 in case of changes of case. */
567 Tags
.Get(Start
,Stop
,Pos
);
569 if (fputs(Order
[I
],Output
) < 0)
570 return _error
->Errno("fputs","IO Error to output");
571 Start
+= strlen(Order
[I
]);
572 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
573 return _error
->Errno("fwrite","IO Error to output");
574 if (Stop
[-1] != '\n')
575 fprintf(Output
,"\n");
578 // Now write all the old tags that were missed.
579 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
581 if ((Visited
[I
] & 1) == 1)
586 Tags
.Get(Start
,Stop
,I
);
587 const char *End
= Start
;
588 for (; End
< Stop
&& *End
!= ':'; End
++);
590 // See if this is a field that needs to be rewritten
591 bool Rewritten
= false;
592 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
594 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
597 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
599 if (isspace(Rewrite
[J
].Rewrite
[0]))
600 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
602 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
610 if (Rewritten
== true)
613 // Write out this element
614 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
615 return _error
->Errno("fwrite","IO Error to output");
616 if (Stop
[-1] != '\n')
617 fprintf(Output
,"\n");
620 // Now write all the rewrites that were missed
621 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
623 if ((Visited
[J
] & 2) == 2)
626 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
628 if (isspace(Rewrite
[J
].Rewrite
[0]))
629 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
631 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);