Multiprotocol connect
[ntk/apt.git] / apt-pkg / tagfile.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
06bba740 3// $Id: tagfile.cc,v 1.24 1999/02/22 03:30:06 jgg 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 /*{{{*/
6c139d6e 14#ifdef __GNUG__
094a497d 15#pragma implementation "apt-pkg/tagfile.h"
6c139d6e
AL
16#endif
17
094a497d
AL
18#include <apt-pkg/tagfile.h>
19#include <apt-pkg/error.h>
cdcc6d34 20#include <apt-pkg/strutl.h>
578bfd0a
AL
21
22#include <string>
23#include <stdio.h>
24 /*}}}*/
25
26// TagFile::pkgTagFile - Constructor /*{{{*/
27// ---------------------------------------------------------------------
28/* */
8e06abb2 29pkgTagFile::pkgTagFile(FileFd &Fd,unsigned long Size) : Fd(Fd), Size(Size)
578bfd0a 30{
ad00ae81
AL
31 Buffer = new char[Size];
32 Start = End = Buffer;
578bfd0a 33 Left = Fd.Size();
dcb79bae 34 iOffset = 0;
578bfd0a
AL
35 Fill();
36}
37 /*}}}*/
38// TagFile::Step - Advance to the next section /*{{{*/
39// ---------------------------------------------------------------------
40/* If the Section Scanner fails we refill the buffer and try again. */
41bool pkgTagFile::Step(pkgTagSection &Tag)
42{
43 if (Tag.Scan(Start,End - Start) == false)
44 {
45 if (Fill() == false)
46 return false;
47
48 if (Tag.Scan(Start,End - Start) == false)
138d4b3d 49 return _error->Error("Unable to parse package file %s (1)",Fd.Name().c_str());
578bfd0a 50 }
dcb79bae
AL
51 Start += Tag.size();
52 iOffset += Tag.size();
53
578bfd0a
AL
54 return true;
55}
56 /*}}}*/
57// TagFile::Fill - Top up the buffer /*{{{*/
58// ---------------------------------------------------------------------
59/* This takes the bit at the end of the buffer and puts it at the start
60 then fills the rest from the file */
61bool pkgTagFile::Fill()
62{
ad00ae81 63 unsigned long EndSize = End - Start;
578bfd0a 64
c7b5ce1c
AL
65 memmove(Buffer,Start,EndSize);
66 Start = Buffer;
67 End = Buffer + EndSize;
68
578bfd0a
AL
69 if (Left == 0)
70 {
c7b5ce1c 71 if (EndSize <= 3)
578bfd0a 72 return false;
c7b5ce1c
AL
73 if (Size - (End - Buffer) < 4)
74 return true;
75
76 // Append a double new line if one does not exist
77 unsigned int LineCount = 0;
78 for (const char *E = End - 1; E - End < 6 && (*E == '\n' || *E == '\r'); E--)
79 if (*E == '\n')
80 LineCount++;
81 for (; LineCount < 2; LineCount++)
82 *End++ = '\n';
83
578bfd0a
AL
84 return true;
85 }
86
c88edf1d
AL
87 // See if only a bit of the file is left
88 if (Left < Size - (End - Buffer))
578bfd0a 89 {
ad00ae81 90 if (Fd.Read(End,Left) == false)
578bfd0a 91 return false;
c88edf1d 92
ad00ae81 93 End += Left;
578bfd0a
AL
94 Left = 0;
95 }
96 else
97 {
ad00ae81 98 if (Fd.Read(End,Size - (End - Buffer)) == false)
578bfd0a 99 return false;
c88edf1d 100
ad00ae81
AL
101 Left -= Size - (End - Buffer);
102 End = Buffer + Size;
578bfd0a
AL
103 }
104 return true;
105}
106 /*}}}*/
ad00ae81
AL
107// TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
108// ---------------------------------------------------------------------
03e39e59
AL
109/* This jumps to a pre-recorded file location and reads the record
110 that is there */
ad00ae81
AL
111bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long Offset)
112{
113 iOffset = Offset;
114 Left = Fd.Size() - Offset;
115 if (Fd.Seek(Offset) == false)
116 return false;
117 End = Start = Buffer;
118
138d4b3d
AL
119 if (Fill() == false)
120 return false;
121
122 if (Tag.Scan(Start,End - Start) == true)
123 return true;
124
125 // This appends a double new line (for the real eof handling)
ad00ae81
AL
126 if (Fill() == false)
127 return false;
128
129 if (Tag.Scan(Start,End - Start) == false)
06bba740
AL
130 {
131 cout << string(Start,End) << endl;
138d4b3d 132 return _error->Error("Unable to parse package file %s (2)",Fd.Name().c_str());
06bba740
AL
133 }
134
ad00ae81
AL
135 return true;
136}
137 /*}}}*/
578bfd0a
AL
138// TagSection::Scan - Scan for the end of the header information /*{{{*/
139// ---------------------------------------------------------------------
140/* This looks for the first double new line in the data stream. It also
c1a22377
AL
141 indexes the tags in the section. This very simple hash function for the
142 first 3 letters gives very good performance on the debian package files */
578bfd0a
AL
143bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
144{
145 const char *End = Start + MaxLength;
146 Stop = Section = Start;
c1a22377 147 memset(AlphaIndexes,0,sizeof(AlphaIndexes));
c7b5ce1c
AL
148
149 if (Stop == 0)
150 return false;
578bfd0a
AL
151
152 TagCount = 0;
f3bcc383 153 while (TagCount < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
578bfd0a 154 {
90d64280 155 // Start a new index and add it to the hash
c1a22377
AL
156 if (isspace(Stop[0]) == 0)
157 {
158 Indexes[TagCount++] = Stop - Section;
159 unsigned char A = tolower(Stop[0]) - 'a';
160 unsigned char B = tolower(Stop[1]) - 'a';
161 unsigned char C = tolower(Stop[3]) - 'a';
162 AlphaIndexes[((A + C/3)%26) + 26*((B + C/2)%26)] = TagCount;
163 }
0a8e3465 164
c1a22377 165 Stop = (const char *)memchr(Stop,'\n',End - Stop);
0a8e3465 166
c1a22377
AL
167 if (Stop == 0)
168 return false;
138d4b3d 169
90d64280 170 for (; Stop[1] == '\r' && Stop+1 < End; Stop++);
c1a22377 171
f3bcc383
AL
172 // Double newline marks the end of the record
173 if (Stop+1 < End && Stop[1] == '\n')
578bfd0a 174 {
578bfd0a 175 Indexes[TagCount] = Stop - Section;
0a8e3465 176 for (; (Stop[0] == '\n' || Stop[0] == '\r') && Stop < End; Stop++);
578bfd0a 177 return true;
578bfd0a
AL
178 }
179
c1a22377
AL
180 Stop++;
181 }
138d4b3d 182
578bfd0a
AL
183 return false;
184}
185 /*}}}*/
186// TagSection::Find - Locate a tag /*{{{*/
187// ---------------------------------------------------------------------
188/* This searches the section for a tag that matches the given string. */
189bool pkgTagSection::Find(const char *Tag,const char *&Start,
190 const char *&End)
191{
192 unsigned int Length = strlen(Tag);
c1a22377
AL
193 unsigned char A = tolower(Tag[0]) - 'a';
194 unsigned char B = tolower(Tag[1]) - 'a';
195 unsigned char C = tolower(Tag[3]) - 'a';
196 unsigned int I = AlphaIndexes[((A + C/3)%26) + 26*((B + C/2)%26)];
197 if (I == 0)
198 return false;
199 I--;
200
201 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
202 I = (I+1)%TagCount)
578bfd0a 203 {
c1a22377
AL
204 const char *St;
205 St = Section + Indexes[I];
206 if (strncasecmp(Tag,St,Length) != 0)
578bfd0a
AL
207 continue;
208
209 // Make sure the colon is in the right place
c1a22377 210 const char *C = St + Length;
578bfd0a
AL
211 for (; isspace(*C) != 0; C++);
212 if (*C != ':')
213 continue;
214
215 // Strip off the gunk from the start end
216 Start = C;
217 End = Section + Indexes[I+1];
06bba740
AL
218 if (Start >= End)
219 return _error->Error("Internal parsing error");
220
578bfd0a
AL
221 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
222 for (; isspace(End[-1]) != 0 && End > Start; End--);
06bba740 223
578bfd0a
AL
224 return true;
225 }
c1a22377 226
578bfd0a
AL
227 Start = End = 0;
228 return false;
229}
230 /*}}}*/
0e66b144 231// TagSection::FindS - Find a string /*{{{*/
a05599f1
AL
232// ---------------------------------------------------------------------
233/* */
234string pkgTagSection::FindS(const char *Tag)
235{
236 const char *Start;
237 const char *End;
238 if (Find(Tag,Start,End) == false)
239 return string();
240 return string(Start,End);
241}
242 /*}}}*/
243// TagSection::FindI - Find an integer /*{{{*/
244// ---------------------------------------------------------------------
245/* */
b0b4efb9 246signed int pkgTagSection::FindI(const char *Tag,signed long Default)
a05599f1
AL
247{
248 const char *Start;
b0b4efb9
AL
249 const char *Stop;
250 if (Find(Tag,Start,Stop) == false)
251 return Default;
252
253 // Copy it into a temp buffer so we can use strtol
254 char S[300];
255 if ((unsigned)(Stop - Start) >= sizeof(S))
256 return Default;
257 strncpy(S,Start,Stop-Start);
258 S[Stop - Start] = 0;
259
260 char *End;
261 signed long Result = strtol(S,&End,10);
262 if (S == End)
263 return Default;
264 return Result;
265}
266 /*}}}*/
267// TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
268// ---------------------------------------------------------------------
269/* The bits marked in Flag are masked on/off in Flags */
270bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
271 unsigned long Flag)
272{
273 const char *Start;
274 const char *Stop;
275 if (Find(Tag,Start,Stop) == false)
276 return true;
a05599f1 277
b0b4efb9
AL
278 switch (StringToBool(string(Start,Stop)))
279 {
280 case 0:
281 Flags &= ~Flag;
282 return true;
283
284 case 1:
285 Flags |= Flag;
286 return true;
287
288 default:
289 _error->Warning("Unknown flag value");
290 return true;
291 }
292 return true;
a05599f1
AL
293}
294 /*}}}*/