]>
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>
32 class pkgTagFilePrivate
35 pkgTagFilePrivate(FileFd
*pFd
, unsigned long long Size
) : Fd(*pFd
), Buffer(NULL
),
36 Start(NULL
), End(NULL
),
37 Done(false), iOffset(0),
46 unsigned long long iOffset
;
47 unsigned long long Size
;
50 // TagFile::pkgTagFile - Constructor /*{{{*/
51 // ---------------------------------------------------------------------
53 pkgTagFile::pkgTagFile(FileFd
*pFd
,unsigned long long Size
)
59 void pkgTagFile::Init(FileFd
*pFd
,unsigned long long Size
)
61 /* The size is increased by 4 because if we start with the Size of the
62 filename we need to try to read 1 char more to see an EOF faster, 1
63 char the end-pointer can be on and maybe 2 newlines need to be added
64 to the end of the file -> 4 extra chars */
71 d
= new pkgTagFilePrivate(pFd
, Size
);
73 if (d
->Fd
.IsOpen() == false)
74 d
->Start
= d
->End
= d
->Buffer
= 0;
76 d
->Buffer
= (char*)malloc(sizeof(char) * Size
);
78 if (d
->Buffer
== NULL
)
83 d
->Start
= d
->End
= d
->Buffer
;
89 // TagFile::~pkgTagFile - Destructor /*{{{*/
90 // ---------------------------------------------------------------------
92 pkgTagFile::~pkgTagFile()
98 // TagFile::Offset - Return the current offset in the buffer /*{{{*/
99 APT_PURE
unsigned long pkgTagFile::Offset()
104 // TagFile::Resize - Resize the internal buffer /*{{{*/
105 // ---------------------------------------------------------------------
106 /* Resize the internal buffer (double it in size). Fail if a maximum size
109 bool pkgTagFile::Resize()
111 // fail is the buffer grows too big
112 if(d
->Size
> 1024*1024+1)
115 return Resize(d
->Size
* 2);
117 bool pkgTagFile::Resize(unsigned long long const newSize
)
119 unsigned long long const EndSize
= d
->End
- d
->Start
;
121 // get new buffer and use it
122 char* newBuffer
= (char*)realloc(d
->Buffer
, sizeof(char) * newSize
);
123 if (newBuffer
== NULL
)
125 d
->Buffer
= newBuffer
;
128 // update the start/end pointers to the new buffer
129 d
->Start
= d
->Buffer
;
130 d
->End
= d
->Start
+ EndSize
;
134 // TagFile::Step - Advance to the next section /*{{{*/
135 // ---------------------------------------------------------------------
136 /* If the Section Scanner fails we refill the buffer and try again.
137 * If that fails too, double the buffer size and try again until a
138 * maximum buffer is reached.
140 bool pkgTagFile::Step(pkgTagSection
&Tag
)
142 while (Tag
.Scan(d
->Start
,d
->End
- d
->Start
) == false)
147 if(Tag
.Scan(d
->Start
,d
->End
- d
->Start
))
150 if (Resize() == false)
151 return _error
->Error(_("Unable to parse package file %s (1)"),
152 d
->Fd
.Name().c_str());
154 d
->Start
+= Tag
.size();
155 d
->iOffset
+= Tag
.size();
161 // TagFile::Fill - Top up the buffer /*{{{*/
162 // ---------------------------------------------------------------------
163 /* This takes the bit at the end of the buffer and puts it at the start
164 then fills the rest from the file */
165 bool pkgTagFile::Fill()
167 unsigned long long EndSize
= d
->End
- d
->Start
;
168 unsigned long long Actual
= 0;
170 memmove(d
->Buffer
,d
->Start
,EndSize
);
171 d
->Start
= d
->Buffer
;
172 d
->End
= d
->Buffer
+ EndSize
;
174 if (d
->Done
== false)
176 // See if only a bit of the file is left
177 unsigned long long const dataSize
= d
->Size
- ((d
->End
- d
->Buffer
) + 1);
178 if (d
->Fd
.Read(d
->End
, dataSize
, &Actual
) == false)
180 if (Actual
!= dataSize
)
187 if (EndSize
<= 3 && Actual
== 0)
189 if (d
->Size
- (d
->End
- d
->Buffer
) < 4)
192 // Append a double new line if one does not exist
193 unsigned int LineCount
= 0;
194 for (const char *E
= d
->End
- 1; E
- d
->End
< 6 && (*E
== '\n' || *E
== '\r'); E
--)
199 if ((unsigned)(d
->End
- d
->Buffer
) >= d
->Size
)
201 for (; LineCount
< 2; LineCount
++)
211 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
212 // ---------------------------------------------------------------------
213 /* This jumps to a pre-recorded file location and reads the record
215 bool pkgTagFile::Jump(pkgTagSection
&Tag
,unsigned long long Offset
)
217 // We are within a buffer space of the next hit..
218 if (Offset
>= d
->iOffset
&& d
->iOffset
+ (d
->End
- d
->Start
) > Offset
)
220 unsigned long long Dist
= Offset
- d
->iOffset
;
223 // if we have seen the end, don't ask for more
225 return Tag
.Scan(d
->Start
, d
->End
- d
->Start
);
230 // Reposition and reload..
233 if (d
->Fd
.Seek(Offset
) == false)
235 d
->End
= d
->Start
= d
->Buffer
;
240 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
) == true)
243 // This appends a double new line (for the real eof handling)
247 if (Tag
.Scan(d
->Start
, d
->End
- d
->Start
) == false)
248 return _error
->Error(_("Unable to parse package file %s (2)"),d
->Fd
.Name().c_str());
253 // pkgTagSection::pkgTagSection - Constructor /*{{{*/
254 // ---------------------------------------------------------------------
256 pkgTagSection::pkgTagSection()
257 : Section(0), TagCount(0), d(NULL
), Stop(0)
259 memset(&Indexes
, 0, sizeof(Indexes
));
260 memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
));
263 // TagSection::Scan - Scan for the end of the header information /*{{{*/
264 // ---------------------------------------------------------------------
265 /* This looks for the first double new line in the data stream.
266 It also indexes the tags in the section. */
267 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
)
269 const char *End
= Start
+ MaxLength
;
270 Stop
= Section
= Start
;
271 memset(AlphaIndexes
,0,sizeof(AlphaIndexes
));
277 while (TagCount
+1 < sizeof(Indexes
)/sizeof(Indexes
[0]) && Stop
< End
)
279 TrimRecord(true,End
);
281 // this can happen when TrimRecord trims away the entire Record
282 // (e.g. because it just contains comments)
286 // Start a new index and add it to the hash
287 if (isspace(Stop
[0]) == 0)
289 Indexes
[TagCount
++] = Stop
- Section
;
290 AlphaIndexes
[AlphaHash(Stop
,End
)] = TagCount
;
293 Stop
= (const char *)memchr(Stop
,'\n',End
- Stop
);
298 for (; Stop
+1 < End
&& Stop
[1] == '\r'; Stop
++)
302 // Double newline marks the end of the record
303 if (Stop
+1 < End
&& Stop
[1] == '\n')
305 Indexes
[TagCount
] = Stop
- Section
;
306 TrimRecord(false,End
);
316 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
317 // ---------------------------------------------------------------------
318 /* There should be exactly 2 newline at the end of the record, no more. */
319 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
)
321 if (BeforeRecord
== true)
323 for (; Stop
< End
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++);
326 // TagSection::Trim - Trim off any trailing garbage /*{{{*/
327 // ---------------------------------------------------------------------
328 /* There should be exactly 1 newline at the end of the buffer, no more. */
329 void pkgTagSection::Trim()
331 for (; Stop
> Section
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--);
334 // TagSection::Exists - return True if a tag exists /*{{{*/
335 bool pkgTagSection::Exists(const char* const Tag
)
338 return Find(Tag
, tmp
);
341 // TagSection::Find - Locate a tag /*{{{*/
342 // ---------------------------------------------------------------------
343 /* This searches the section for a tag that matches the given string. */
344 bool pkgTagSection::Find(const char *Tag
,unsigned int &Pos
) const
346 unsigned int Length
= strlen(Tag
);
347 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
352 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
356 St
= Section
+ Indexes
[I
];
357 if (strncasecmp(Tag
,St
,Length
) != 0)
360 // Make sure the colon is in the right place
361 const char *C
= St
+ Length
;
362 for (; isspace(*C
) != 0; C
++);
373 // TagSection::Find - Locate a tag /*{{{*/
374 // ---------------------------------------------------------------------
375 /* This searches the section for a tag that matches the given string. */
376 bool pkgTagSection::Find(const char *Tag
,const char *&Start
,
377 const char *&End
) const
379 unsigned int Length
= strlen(Tag
);
380 unsigned int I
= AlphaIndexes
[AlphaHash(Tag
)];
385 for (unsigned int Counter
= 0; Counter
!= TagCount
; Counter
++,
389 St
= Section
+ Indexes
[I
];
390 if (strncasecmp(Tag
,St
,Length
) != 0)
393 // Make sure the colon is in the right place
394 const char *C
= St
+ Length
;
395 for (; isspace(*C
) != 0; C
++);
399 // Strip off the gunk from the start end
401 End
= Section
+ Indexes
[I
+1];
403 return _error
->Error("Internal parsing error");
405 for (; (isspace(*Start
) != 0 || *Start
== ':') && Start
< End
; Start
++);
406 for (; isspace(End
[-1]) != 0 && End
> Start
; End
--);
415 // TagSection::FindS - Find a string /*{{{*/
416 // ---------------------------------------------------------------------
418 string
pkgTagSection::FindS(const char *Tag
) const
422 if (Find(Tag
,Start
,End
) == false)
424 return string(Start
,End
);
427 // TagSection::FindI - Find an integer /*{{{*/
428 // ---------------------------------------------------------------------
430 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const
434 if (Find(Tag
,Start
,Stop
) == false)
437 // Copy it into a temp buffer so we can use strtol
439 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
441 strncpy(S
,Start
,Stop
-Start
);
445 signed long Result
= strtol(S
,&End
,10);
451 // TagSection::FindULL - Find an unsigned long long integer /*{{{*/
452 // ---------------------------------------------------------------------
454 unsigned long long pkgTagSection::FindULL(const char *Tag
, unsigned long long const &Default
) const
458 if (Find(Tag
,Start
,Stop
) == false)
461 // Copy it into a temp buffer so we can use strtoull
463 if ((unsigned)(Stop
- Start
) >= sizeof(S
))
465 strncpy(S
,Start
,Stop
-Start
);
469 unsigned long long Result
= strtoull(S
,&End
,10);
475 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
476 // ---------------------------------------------------------------------
477 /* The bits marked in Flag are masked on/off in Flags */
478 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
,
479 unsigned long Flag
) const
483 if (Find(Tag
,Start
,Stop
) == false)
485 return FindFlag(Flags
, Flag
, Start
, Stop
);
487 bool pkgTagSection::FindFlag(unsigned long &Flags
, unsigned long Flag
,
488 char const* Start
, char const* Stop
)
490 switch (StringToBool(string(Start
, Stop
)))
501 _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str());
507 // TFRewrite - Rewrite a control record /*{{{*/
508 // ---------------------------------------------------------------------
509 /* This writes the control record to stdout rewriting it as necessary. The
510 override map item specificies the rewriting rules to follow. This also
511 takes the time to sort the feild list. */
513 /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
515 static const char *iTFRewritePackageOrder
[] = {
523 "Original-Maintainer",
527 "Revision", // Obsolete
528 "Config-Version", // Obsolete
544 "MSDOS-Filename", // Obsolete
547 static const char *iTFRewriteSourceOrder
[] = {"Package",
554 "Original-Maintainer",
556 "Build-Depends-Indep",
558 "Build-Conflicts-Indep",
566 /* Two levels of initialization are used because gcc will set the symbol
567 size of an array to the length of the array, causing dynamic relinking
568 errors. Doing this makes the symbol size constant */
569 const char **TFRewritePackageOrder
= iTFRewritePackageOrder
;
570 const char **TFRewriteSourceOrder
= iTFRewriteSourceOrder
;
572 bool TFRewrite(FILE *Output
,pkgTagSection
const &Tags
,const char *Order
[],
573 TFRewriteData
*Rewrite
)
575 unsigned char Visited
[256]; // Bit 1 is Order, Bit 2 is Rewrite
576 for (unsigned I
= 0; I
!= 256; I
++)
579 // Set new tag up as necessary.
580 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
582 if (Rewrite
[J
].NewTag
== 0)
583 Rewrite
[J
].NewTag
= Rewrite
[J
].Tag
;
586 // Write all all of the tags, in order.
589 for (unsigned int I
= 0; Order
[I
] != 0; I
++)
591 bool Rewritten
= false;
593 // See if this is a field that needs to be rewritten
594 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
596 if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0)
599 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
601 if (isspace(Rewrite
[J
].Rewrite
[0]))
602 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
604 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
611 // See if it is in the fragment
613 if (Tags
.Find(Order
[I
],Pos
) == false)
617 if (Rewritten
== true)
620 /* Write out this element, taking a moment to rewrite the tag
621 in case of changes of case. */
624 Tags
.Get(Start
,Stop
,Pos
);
626 if (fputs(Order
[I
],Output
) < 0)
627 return _error
->Errno("fputs","IO Error to output");
628 Start
+= strlen(Order
[I
]);
629 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
630 return _error
->Errno("fwrite","IO Error to output");
631 if (Stop
[-1] != '\n')
632 fprintf(Output
,"\n");
636 // Now write all the old tags that were missed.
637 for (unsigned int I
= 0; I
!= Tags
.Count(); I
++)
639 if ((Visited
[I
] & 1) == 1)
644 Tags
.Get(Start
,Stop
,I
);
645 const char *End
= Start
;
646 for (; End
< Stop
&& *End
!= ':'; End
++);
648 // See if this is a field that needs to be rewritten
649 bool Rewritten
= false;
650 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
652 if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0)
655 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
657 if (isspace(Rewrite
[J
].Rewrite
[0]))
658 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
660 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
668 if (Rewritten
== true)
671 // Write out this element
672 if (fwrite(Start
,Stop
- Start
,1,Output
) != 1)
673 return _error
->Errno("fwrite","IO Error to output");
674 if (Stop
[-1] != '\n')
675 fprintf(Output
,"\n");
678 // Now write all the rewrites that were missed
679 for (unsigned int J
= 0; Rewrite
!= 0 && Rewrite
[J
].Tag
!= 0; J
++)
681 if ((Visited
[J
] & 2) == 2)
684 if (Rewrite
[J
].Rewrite
!= 0 && Rewrite
[J
].Rewrite
[0] != 0)
686 if (isspace(Rewrite
[J
].Rewrite
[0]))
687 fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);
689 fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
);