Release support
[ntk/apt.git] / apt-pkg / tagfile.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: tagfile.cc,v 1.19 1998/12/14 02:23:47 jgg Exp $
4 /* ######################################################################
5
6 Fast scanner for RFC-822 type header information
7
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.
10
11 ##################################################################### */
12 /*}}}*/
13 // Include Files /*{{{*/
14 #ifdef __GNUG__
15 #pragma implementation "apt-pkg/tagfile.h"
16 #endif
17
18 #include <apt-pkg/tagfile.h>
19 #include <apt-pkg/error.h>
20 #include <strutl.h>
21
22 #include <string>
23 #include <stdio.h>
24 /*}}}*/
25
26 // TagFile::pkgTagFile - Constructor /*{{{*/
27 // ---------------------------------------------------------------------
28 /* */
29 pkgTagFile::pkgTagFile(FileFd &Fd,unsigned long Size) : Fd(Fd), Size(Size)
30 {
31 Buffer = new char[Size];
32 Start = End = Buffer;
33 Left = Fd.Size();
34 iOffset = 0;
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. */
41 bool 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)
49 return _error->Error("Unable to parse package file %s",Fd.Name().c_str());
50 }
51 Start += Tag.size();
52 iOffset += Tag.size();
53
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 */
61 bool pkgTagFile::Fill()
62 {
63 unsigned long EndSize = End - Start;
64
65 memmove(Buffer,Start,EndSize);
66 Start = Buffer;
67 End = Buffer + EndSize;
68
69 if (Left == 0)
70 {
71 if (EndSize <= 3)
72 return false;
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
84 return true;
85 }
86
87 // See if only a bit of the file is left
88 if (Left < Size - (End - Buffer))
89 {
90 if (Fd.Read(End,Left) == false)
91 return false;
92
93 End += Left;
94 Left = 0;
95 }
96 else
97 {
98 if (Fd.Read(End,Size - (End - Buffer)) == false)
99 return false;
100
101 Left -= Size - (End - Buffer);
102 End = Buffer + Size;
103 }
104 return true;
105 }
106 /*}}}*/
107 // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
108 // ---------------------------------------------------------------------
109 /* This jumps to a pre-recorded file location and reads the record
110 that is there */
111 bool 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
119 if (Fill() == false)
120 return false;
121
122 if (Tag.Scan(Start,End - Start) == false)
123 return _error->Error("Unable to parse package file");
124 return true;
125 }
126 /*}}}*/
127 // TagSection::Scan - Scan for the end of the header information /*{{{*/
128 // ---------------------------------------------------------------------
129 /* This looks for the first double new line in the data stream. It also
130 indexes the tags in the section. This very simple hash function for the
131 first 3 letters gives very good performance on the debian package files */
132 bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
133 {
134 const char *End = Start + MaxLength;
135 Stop = Section = Start;
136 memset(AlphaIndexes,0,sizeof(AlphaIndexes));
137
138 if (Stop == 0)
139 return false;
140
141 TagCount = 0;
142 while (TagCount < sizeof(Indexes)/sizeof(Indexes[0]))
143 {
144 if (isspace(Stop[0]) == 0)
145 {
146 Indexes[TagCount++] = Stop - Section;
147 unsigned char A = tolower(Stop[0]) - 'a';
148 unsigned char B = tolower(Stop[1]) - 'a';
149 unsigned char C = tolower(Stop[3]) - 'a';
150 AlphaIndexes[((A + C/3)%26) + 26*((B + C/2)%26)] = TagCount;
151 }
152
153 Stop = (const char *)memchr(Stop,'\n',End - Stop);
154
155 if (Stop == 0)
156 return false;
157 for (; Stop[1] == '\r' && Stop < End; Stop++);
158
159 if (Stop[1] == '\n')
160 {
161 Indexes[TagCount] = Stop - Section;
162 for (; (Stop[0] == '\n' || Stop[0] == '\r') && Stop < End; Stop++);
163 return true;
164 }
165
166 Stop++;
167 }
168
169 return false;
170 }
171 /*}}}*/
172 // TagSection::Find - Locate a tag /*{{{*/
173 // ---------------------------------------------------------------------
174 /* This searches the section for a tag that matches the given string. */
175 bool pkgTagSection::Find(const char *Tag,const char *&Start,
176 const char *&End)
177 {
178 unsigned int Length = strlen(Tag);
179 unsigned char A = tolower(Tag[0]) - 'a';
180 unsigned char B = tolower(Tag[1]) - 'a';
181 unsigned char C = tolower(Tag[3]) - 'a';
182 unsigned int I = AlphaIndexes[((A + C/3)%26) + 26*((B + C/2)%26)];
183 if (I == 0)
184 return false;
185 I--;
186
187 for (unsigned int Counter = 0; Counter != TagCount; Counter++,
188 I = (I+1)%TagCount)
189 {
190 const char *St;
191 St = Section + Indexes[I];
192 if (strncasecmp(Tag,St,Length) != 0)
193 continue;
194
195 // Make sure the colon is in the right place
196 const char *C = St + Length;
197 for (; isspace(*C) != 0; C++);
198 if (*C != ':')
199 continue;
200
201 // Strip off the gunk from the start end
202 Start = C;
203 End = Section + Indexes[I+1];
204 for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
205 for (; isspace(End[-1]) != 0 && End > Start; End--);
206
207 return true;
208 }
209
210 Start = End = 0;
211 return false;
212 }
213 /*}}}*/
214 // TagSection::FindS - Find a string /*{{{*/
215 // ---------------------------------------------------------------------
216 /* */
217 string pkgTagSection::FindS(const char *Tag)
218 {
219 const char *Start;
220 const char *End;
221 if (Find(Tag,Start,End) == false)
222 return string();
223 return string(Start,End);
224 }
225 /*}}}*/
226 // TagSection::FindI - Find an integer /*{{{*/
227 // ---------------------------------------------------------------------
228 /* */
229 signed int pkgTagSection::FindI(const char *Tag,signed long Default)
230 {
231 const char *Start;
232 const char *Stop;
233 if (Find(Tag,Start,Stop) == false)
234 return Default;
235
236 // Copy it into a temp buffer so we can use strtol
237 char S[300];
238 if ((unsigned)(Stop - Start) >= sizeof(S))
239 return Default;
240 strncpy(S,Start,Stop-Start);
241 S[Stop - Start] = 0;
242
243 char *End;
244 signed long Result = strtol(S,&End,10);
245 if (S == End)
246 return Default;
247 return Result;
248 }
249 /*}}}*/
250 // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
251 // ---------------------------------------------------------------------
252 /* The bits marked in Flag are masked on/off in Flags */
253 bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
254 unsigned long Flag)
255 {
256 const char *Start;
257 const char *Stop;
258 if (Find(Tag,Start,Stop) == false)
259 return true;
260
261 switch (StringToBool(string(Start,Stop)))
262 {
263 case 0:
264 Flags &= ~Flag;
265 return true;
266
267 case 1:
268 Flags |= Flag;
269 return true;
270
271 default:
272 _error->Warning("Unknown flag value");
273 return true;
274 }
275 return true;
276 }
277 /*}}}*/
278
279