]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/configuration.cc
Dsync merge
[apt.git] / apt-pkg / contrib / configuration.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: configuration.cc,v 1.11 1999/01/18 06:20:07 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 <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
280 // Parser state
281 string ParentTag;
282
283 int CurLine = 0;
284 bool InComment = false;
285 while (F.eof() == false)
286 {
287 F.getline(Buffer,sizeof(Buffer));
288 CurLine++;
289 _strtabexpand(Buffer,sizeof(Buffer));
290 _strstrip(Buffer);
291
292 // Multi line comment
293 if (InComment == true)
294 {
295 for (const char *I = Buffer; *I != 0; I++)
296 {
297 if (*I == '*' && I[1] == '/')
298 {
299 memmove(Buffer,I+2,strlen(I+2) + 1);
300 InComment = false;
301 break;
302 }
303 }
304 if (InComment == true)
305 continue;
306 }
307
308 // Discard single line comments
309 bool InQuote = false;
310 for (char *I = Buffer; *I != 0; I++)
311 {
312 if (*I == '"')
313 InQuote = !InQuote;
314 if (InQuote == true)
315 continue;
316
317 if (*I == '/' && I[1] == '/')
318 {
319 *I = 0;
320 break;
321 }
322 }
323
324 // Look for multi line comments
325 for (char *I = Buffer; *I != 0; I++)
326 {
327 if (*I == '"')
328 InQuote = !InQuote;
329 if (InQuote == true)
330 continue;
331
332 if (*I == '/' && I[1] == '*')
333 {
334 InComment = true;
335 for (char *J = Buffer; *J != 0; J++)
336 {
337 if (*J == '*' && J[1] == '/')
338 {
339 memmove(I,J+2,strlen(J+2) + 1);
340 InComment = false;
341 break;
342 }
343 }
344
345 if (InComment == true)
346 {
347 *I = 0;
348 break;
349 }
350 }
351 }
352
353 // Blank
354 if (Buffer[0] == 0)
355 continue;
356
357 // We now have a valid line fragment
358 for (char *I = Buffer; *I != 0;)
359 {
360 if (*I == '{' || *I == ';' || *I == '}')
361 {
362 // Put the last fragement into the buffer
363 char *Start = Buffer;
364 char *Stop = I;
365 for (; Start != I && isspace(*Start) != 0; Start++);
366 for (; Stop != Start && isspace(Stop[-1]) != 0; Stop--);
367 if (LineBuffer.empty() == false && Stop - Start != 0)
368 LineBuffer += ' ';
369 LineBuffer += string(Start,Stop - Start);
370
371 // Remove the fragment
372 char TermChar = *I;
373 memmove(Buffer,I + 1,strlen(I + 1) + 1);
374 I = Buffer;
375
376 // Move up a tag
377 if (TermChar == '}')
378 {
379 string::size_type Pos = ParentTag.rfind("::");
380 if (Pos == string::npos)
381 ParentTag = string();
382 else
383 ParentTag = string(ParentTag,0,Pos);
384 }
385
386 // Syntax Error
387 if (TermChar == '{' && LineBuffer.empty() == true)
388 return _error->Error("Syntax error %s:%u: Block starts with no name.",FName.c_str(),CurLine);
389
390 if (LineBuffer.empty() == true)
391 continue;
392
393 // Parse off the tag
394 string Tag;
395 const char *Pos = LineBuffer.c_str();
396 if (ParseQuoteWord(Pos,Tag) == false)
397 return _error->Error("Syntax error %s:%u: Malformed Tag",FName.c_str(),CurLine);
398
399 // Go down a level
400 if (TermChar == '{')
401 {
402 if (ParentTag.empty() == true)
403 ParentTag = Tag;
404 else
405 ParentTag += string("::") + Tag;
406 Tag = string();
407 }
408
409 // Parse off the word
410 string Word;
411 if (ParseCWord(Pos,Word) == false)
412 {
413 if (TermChar != '{')
414 {
415 Word = Tag;
416 Tag = "";
417 }
418 }
419
420 // Generate the item name
421 string Item;
422 if (ParentTag.empty() == true)
423 Item = Tag;
424 else
425 {
426 if (TermChar != '{' || Tag.empty() == false)
427 Item = ParentTag + "::" + Tag;
428 else
429 Item = ParentTag;
430 }
431
432 // Set the item in the configuration class
433 Conf.Set(Item,Word);
434
435 // Empty the buffer
436 LineBuffer = string();
437 }
438 else
439 I++;
440 }
441
442 // Store the fragment
443 const char *Stripd = _strstrip(Buffer);
444 if (*Stripd != 0 && LineBuffer.empty() == false)
445 LineBuffer += " ";
446 LineBuffer += Stripd;
447 }
448
449 return true;
450 }
451 /*}}}*/