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