]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/configuration.cc
Reorderd error handling
[apt.git] / apt-pkg / contrib / configuration.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: configuration.cc,v 1.13 1999/07/02 23:17:00 jgg Exp $
4 /* ######################################################################
5
6 Configuration Class
7
8 This class provides a configuration file and command line parser
9 for a tree-oriented configuration environment. All runtime configuration
10 is stored in here.
11
12 ##################################################################### */
13 /*}}}*/
14 // Include files /*{{{*/
15 #ifdef __GNUG__
16 #pragma implementation "apt-pkg/configuration.h"
17 #endif
18 #include <apt-pkg/configuration.h>
19 #include <apt-pkg/error.h>
20 #include <apt-pkg/strutl.h>
21
22 #include <stdio.h>
23 #include <fstream.h>
24 /*}}}*/
25
26 Configuration *_config = new Configuration;
27
28 // Configuration::Configuration - Constructor /*{{{*/
29 // ---------------------------------------------------------------------
30 /* */
31 Configuration::Configuration()
32 {
33 Root = new Item;
34 }
35 /*}}}*/
36 // Configuration::Lookup - Lookup a single item /*{{{*/
37 // ---------------------------------------------------------------------
38 /* This will lookup a single item by name below another item. It is a
39 helper function for the main lookup function */
40 Configuration::Item *Configuration::Lookup(Item *Head,const char *S,
41 unsigned long Len,bool Create)
42 {
43 int Res = 1;
44 Item *I = Head->Child;
45 Item **Last = &Head->Child;
46
47 // Empty strings match nothing. They are used for lists.
48 if (Len != 0)
49 {
50 for (; I != 0; Last = &I->Next, I = I->Next)
51 if ((Res = stringcasecmp(I->Tag.begin(),I->Tag.end(),S,S + Len)) == 0)
52 break;
53 }
54 else
55 for (; I != 0; Last = &I->Next, I = I->Next);
56
57 if (Res == 0)
58 return I;
59 if (Create == false)
60 return 0;
61
62 I = new Item;
63 I->Tag = string(S,Len);
64 I->Next = *Last;
65 I->Parent = Head;
66 *Last = I;
67 return I;
68 }
69 /*}}}*/
70 // Configuration::Lookup - Lookup a fully scoped item /*{{{*/
71 // ---------------------------------------------------------------------
72 /* This performs a fully scoped lookup of a given name, possibly creating
73 new items */
74 Configuration::Item *Configuration::Lookup(const char *Name,bool Create)
75 {
76 if (Name == 0)
77 return Root->Child;
78
79 const char *Start = Name;
80 const char *End = Start + strlen(Name);
81 const char *TagEnd = Name;
82 Item *Itm = Root;
83 for (; End - TagEnd >= 2; TagEnd++)
84 {
85 if (TagEnd[0] == ':' && TagEnd[1] == ':')
86 {
87 Itm = Lookup(Itm,Start,TagEnd - Start,Create);
88 if (Itm == 0)
89 return 0;
90 TagEnd = Start = TagEnd + 2;
91 }
92 }
93
94 // This must be a trailing ::, we create unique items in a list
95 if (End - Start == 0)
96 {
97 if (Create == false)
98 return 0;
99 }
100
101 Itm = Lookup(Itm,Start,End - Start,Create);
102 return Itm;
103 }
104 /*}}}*/
105 // Configuration::Find - Find a value /*{{{*/
106 // ---------------------------------------------------------------------
107 /* */
108 string Configuration::Find(const char *Name,const char *Default)
109 {
110 Item *Itm = Lookup(Name,false);
111 if (Itm == 0 || Itm->Value.empty() == true)
112 {
113 if (Default == 0)
114 return string();
115 else
116 return Default;
117 }
118
119 return Itm->Value;
120 }
121 /*}}}*/
122 // Configuration::FindFile - Find a Filename /*{{{*/
123 // ---------------------------------------------------------------------
124 /* Directories are stored as the base dir in the Parent node and the
125 sub directory in sub nodes with the final node being the end filename
126 */
127 string Configuration::FindFile(const char *Name,const char *Default)
128 {
129 Item *Itm = Lookup(Name,false);
130 if (Itm == 0 || Itm->Value.empty() == true)
131 {
132 if (Default == 0)
133 return string();
134 else
135 return Default;
136 }
137
138 // Absolute path
139 if (Itm->Value[0] == '/' || Itm->Parent == 0)
140 return Itm->Value;
141
142 // ./ is also considered absolute as is anything with ~ in it
143 if (Itm->Value[0] != 0 &&
144 ((Itm->Value[0] == '.' && Itm->Value[1] == '/') ||
145 (Itm->Value[0] == '~' && Itm->Value[1] == '/')))
146 return Itm->Value;
147
148 if (Itm->Parent->Value.end()[-1] == '/')
149 return Itm->Parent->Value + Itm->Value;
150 else
151 return Itm->Parent->Value + '/' + Itm->Value;
152 }
153 /*}}}*/
154 // Configuration::FindDir - Find a directory name /*{{{*/
155 // ---------------------------------------------------------------------
156 /* This is like findfile execept the result is terminated in a / */
157 string Configuration::FindDir(const char *Name,const char *Default)
158 {
159 string Res = FindFile(Name,Default);
160 if (Res.end()[-1] != '/')
161 return Res + '/';
162 return Res;
163 }
164 /*}}}*/
165 // Configuration::FindI - Find an integer value /*{{{*/
166 // ---------------------------------------------------------------------
167 /* */
168 int Configuration::FindI(const char *Name,int Default)
169 {
170 Item *Itm = Lookup(Name,false);
171 if (Itm == 0 || Itm->Value.empty() == true)
172 return Default;
173
174 char *End;
175 int Res = strtol(Itm->Value.c_str(),&End,0);
176 if (End == Itm->Value.c_str())
177 return Default;
178
179 return Res;
180 }
181 /*}}}*/
182 // Configuration::FindB - Find a boolean type /*{{{*/
183 // ---------------------------------------------------------------------
184 /* */
185 bool Configuration::FindB(const char *Name,bool Default)
186 {
187 Item *Itm = Lookup(Name,false);
188 if (Itm == 0 || Itm->Value.empty() == true)
189 return Default;
190
191 return StringToBool(Itm->Value,Default);
192 }
193 /*}}}*/
194 // Configuration::Set - Set a value /*{{{*/
195 // ---------------------------------------------------------------------
196 /* */
197 void Configuration::Set(const char *Name,string Value)
198 {
199 Item *Itm = Lookup(Name,true);
200 if (Itm == 0)
201 return;
202 Itm->Value = Value;
203 }
204 /*}}}*/
205 // Configuration::Set - Set an integer value /*{{{*/
206 // ---------------------------------------------------------------------
207 /* */
208 void Configuration::Set(const char *Name,int Value)
209 {
210 Item *Itm = Lookup(Name,true);
211 if (Itm == 0)
212 return;
213 char S[300];
214 snprintf(S,sizeof(S),"%i",Value);
215 Itm->Value = S;
216 }
217 /*}}}*/
218 // Configuration::Exists - Returns true if the Name exists /*{{{*/
219 // ---------------------------------------------------------------------
220 /* */
221 bool Configuration::Exists(const char *Name)
222 {
223 Item *Itm = Lookup(Name,false);
224 if (Itm == 0)
225 return false;
226 return true;
227 }
228 /*}}}*/
229 // Configuration::Dump - Dump the config /*{{{*/
230 // ---------------------------------------------------------------------
231 /* Dump the entire configuration space */
232 void Configuration::Dump()
233 {
234 /* Write out all of the configuration directives by walking the
235 configuration tree */
236 const Configuration::Item *Top = _config->Tree(0);
237 for (; Top != 0;)
238 {
239 clog << Top->FullTag() << " \"" << Top->Value << "\";" << endl;
240
241 if (Top->Child != 0)
242 {
243 Top = Top->Child;
244 continue;
245 }
246
247 while (Top != 0 && Top->Next == 0)
248 Top = Top->Parent;
249 if (Top != 0)
250 Top = Top->Next;
251 }
252 }
253 /*}}}*/
254
255 // Configuration::Item::FullTag - Return the fully scoped tag /*{{{*/
256 // ---------------------------------------------------------------------
257 /* */
258 string Configuration::Item::FullTag() const
259 {
260 if (Parent == 0 || Parent->Parent == 0)
261 return Tag;
262 return Parent->FullTag() + "::" + Tag;
263 }
264 /*}}}*/
265
266 // ReadConfigFile - Read a configuration file /*{{{*/
267 // ---------------------------------------------------------------------
268 /* The configuration format is very much like the named.conf format
269 used in bind8, in fact this routine can parse most named.conf files. */
270 bool ReadConfigFile(Configuration &Conf,string FName)
271 {
272 // Open the stream for reading
273 ifstream F(FName.c_str(),ios::in | ios::nocreate);
274 if (!F != 0)
275 return _error->Errno("ifstream::ifstream","Opening configuration file %s",FName.c_str());
276
277 char Buffer[300];
278 string LineBuffer;
279 string Stack[100];
280 unsigned int StackPos = 0;
281
282 // Parser state
283 string ParentTag;
284
285 int CurLine = 0;
286 bool InComment = false;
287 while (F.eof() == false)
288 {
289 F.getline(Buffer,sizeof(Buffer));
290 CurLine++;
291 _strtabexpand(Buffer,sizeof(Buffer));
292 _strstrip(Buffer);
293
294 // Multi line comment
295 if (InComment == true)
296 {
297 for (const char *I = Buffer; *I != 0; I++)
298 {
299 if (*I == '*' && I[1] == '/')
300 {
301 memmove(Buffer,I+2,strlen(I+2) + 1);
302 InComment = false;
303 break;
304 }
305 }
306 if (InComment == true)
307 continue;
308 }
309
310 // Discard single line comments
311 bool InQuote = false;
312 for (char *I = Buffer; *I != 0; I++)
313 {
314 if (*I == '"')
315 InQuote = !InQuote;
316 if (InQuote == true)
317 continue;
318
319 if (*I == '/' && I[1] == '/')
320 {
321 *I = 0;
322 break;
323 }
324 }
325
326 // Look for multi line comments
327 for (char *I = Buffer; *I != 0; I++)
328 {
329 if (*I == '"')
330 InQuote = !InQuote;
331 if (InQuote == true)
332 continue;
333
334 if (*I == '/' && I[1] == '*')
335 {
336 InComment = true;
337 for (char *J = Buffer; *J != 0; J++)
338 {
339 if (*J == '*' && J[1] == '/')
340 {
341 memmove(I,J+2,strlen(J+2) + 1);
342 InComment = false;
343 break;
344 }
345 }
346
347 if (InComment == true)
348 {
349 *I = 0;
350 break;
351 }
352 }
353 }
354
355 // Blank
356 if (Buffer[0] == 0)
357 continue;
358
359 // We now have a valid line fragment
360 for (char *I = Buffer; *I != 0;)
361 {
362 if (*I == '{' || *I == ';' || *I == '}')
363 {
364 // Put the last fragement into the buffer
365 char *Start = Buffer;
366 char *Stop = I;
367 for (; Start != I && isspace(*Start) != 0; Start++);
368 for (; Stop != Start && isspace(Stop[-1]) != 0; Stop--);
369 if (LineBuffer.empty() == false && Stop - Start != 0)
370 LineBuffer += ' ';
371 LineBuffer += string(Start,Stop - Start);
372
373 // Remove the fragment
374 char TermChar = *I;
375 memmove(Buffer,I + 1,strlen(I + 1) + 1);
376 I = Buffer;
377
378 // Move up a tag
379 if (TermChar == '}')
380 {
381 if (StackPos == 0)
382 ParentTag = string();
383 else
384 ParentTag = Stack[--StackPos];
385 }
386
387 // Syntax Error
388 if (TermChar == '{' && LineBuffer.empty() == true)
389 return _error->Error("Syntax error %s:%u: Block starts with no name.",FName.c_str(),CurLine);
390
391 if (LineBuffer.empty() == true)
392 continue;
393
394 // Parse off the tag
395 string Tag;
396 const char *Pos = LineBuffer.c_str();
397 if (ParseQuoteWord(Pos,Tag) == false)
398 return _error->Error("Syntax error %s:%u: Malformed Tag",FName.c_str(),CurLine);
399
400 // Go down a level
401 if (TermChar == '{')
402 {
403 if (StackPos <= 100)
404 Stack[StackPos++] = ParentTag;
405 if (ParentTag.empty() == true)
406 ParentTag = Tag;
407 else
408 ParentTag += string("::") + Tag;
409 Tag = string();
410 }
411
412 // Parse off the word
413 string Word;
414 if (ParseCWord(Pos,Word) == false)
415 {
416 if (TermChar != '{')
417 {
418 Word = Tag;
419 Tag = "";
420 }
421 }
422
423 // Generate the item name
424 string Item;
425 if (ParentTag.empty() == true)
426 Item = Tag;
427 else
428 {
429 if (TermChar != '{' || Tag.empty() == false)
430 Item = ParentTag + "::" + Tag;
431 else
432 Item = ParentTag;
433 }
434
435 // Set the item in the configuration class
436 Conf.Set(Item,Word);
437
438 // Empty the buffer
439 LineBuffer = string();
440 }
441 else
442 I++;
443 }
444
445 // Store the fragment
446 const char *Stripd = _strstrip(Buffer);
447 if (*Stripd != 0 && LineBuffer.empty() == false)
448 LineBuffer += " ";
449 LineBuffer += Stripd;
450 }
451
452 return true;
453 }
454 /*}}}*/