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