Dsync merge
[ntk/apt.git] / apt-pkg / contrib / configuration.cc
CommitLineData
6c139d6e
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7f25bdff 3// $Id: configuration.cc,v 1.11 1999/01/18 06:20:07 jgg Exp $
6c139d6e
AL
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__
094a497d 16#pragma implementation "apt-pkg/configuration.h"
6c139d6e 17#endif
094a497d 18#include <apt-pkg/configuration.h>
08e8f724 19#include <apt-pkg/error.h>
6c139d6e
AL
20#include <strutl.h>
21
22#include <stdio.h>
08e8f724 23#include <fstream.h>
6c139d6e 24 /*}}}*/
9c14e3d6
AL
25
26Configuration *_config = new Configuration;
6c139d6e
AL
27
28// Configuration::Configuration - Constructor /*{{{*/
29// ---------------------------------------------------------------------
30/* */
31Configuration::Configuration()
32{
33 Root = new Item;
34}
35 /*}}}*/
0a8a80e5 36// Configuration::Lookup - Lookup a single item /*{{{*/
6c139d6e
AL
37// ---------------------------------------------------------------------
38/* This will lookup a single item by name below another item. It is a
39 helper function for the main lookup function */
40Configuration::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;
9c14e3d6 46
7f25bdff
AL
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
6c139d6e
AL
57 if (Res == 0)
58 return I;
59 if (Create == false)
60 return 0;
61
62 I = new Item;
9c14e3d6 63 I->Tag = string(S,Len);
6c139d6e 64 I->Next = *Last;
9c14e3d6 65 I->Parent = Head;
6c139d6e
AL
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 */
74Configuration::Item *Configuration::Lookup(const char *Name,bool Create)
75{
0a8a80e5
AL
76 if (Name == 0)
77 return Root->Child;
78
6c139d6e
AL
79 const char *Start = Name;
80 const char *End = Start + strlen(Name);
81 const char *TagEnd = Name;
82 Item *Itm = Root;
7f25bdff 83 for (; End - TagEnd >= 2; TagEnd++)
6c139d6e
AL
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
7f25bdff
AL
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
6c139d6e 101 Itm = Lookup(Itm,Start,End - Start,Create);
6c139d6e
AL
102 return Itm;
103}
104 /*}}}*/
105// Configuration::Find - Find a value /*{{{*/
106// ---------------------------------------------------------------------
107/* */
108string Configuration::Find(const char *Name,const char *Default)
109{
110 Item *Itm = Lookup(Name,false);
111 if (Itm == 0 || Itm->Value.empty() == true)
9c14e3d6
AL
112 {
113 if (Default == 0)
114 return string();
115 else
116 return Default;
117 }
118
6c139d6e
AL
119 return Itm->Value;
120}
121 /*}}}*/
3b5421b4 122// Configuration::FindFile - Find a Filename /*{{{*/
9c14e3d6
AL
123// ---------------------------------------------------------------------
124/* Directories are stored as the base dir in the Parent node and the
3b5421b4 125 sub directory in sub nodes with the final node being the end filename
9c14e3d6 126 */
3b5421b4 127string Configuration::FindFile(const char *Name,const char *Default)
9c14e3d6
AL
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
0a8e3465 138 // Absolute path
9c14e3d6
AL
139 if (Itm->Value[0] == '/' || Itm->Parent == 0)
140 return Itm->Value;
0a8e3465
AL
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
9c14e3d6
AL
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 /*}}}*/
3b5421b4
AL
154// Configuration::FindDir - Find a directory name /*{{{*/
155// ---------------------------------------------------------------------
156/* This is like findfile execept the result is terminated in a / */
157string 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 /*}}}*/
6c139d6e
AL
165// Configuration::FindI - Find an integer value /*{{{*/
166// ---------------------------------------------------------------------
167/* */
168int 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
08e8f724
AL
179 return Res;
180}
181 /*}}}*/
182// Configuration::FindB - Find a boolean type /*{{{*/
183// ---------------------------------------------------------------------
184/* */
185bool 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
3b5421b4 191 return StringToBool(Itm->Value,Default);
6c139d6e
AL
192}
193 /*}}}*/
194// Configuration::Set - Set a value /*{{{*/
195// ---------------------------------------------------------------------
196/* */
197void 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/* */
208void 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 /*}}}*/
08e8f724
AL
218// Configuration::Exists - Returns true if the Name exists /*{{{*/
219// ---------------------------------------------------------------------
220/* */
221bool Configuration::Exists(const char *Name)
222{
223 Item *Itm = Lookup(Name,false);
224 if (Itm == 0)
225 return false;
226 return true;
227}
228 /*}}}*/
93bf083d
AL
229// Configuration::Dump - Dump the config /*{{{*/
230// ---------------------------------------------------------------------
231/* Dump the entire configuration space */
232void 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 /*}}}*/
08e8f724 254
0a8a80e5
AL
255// Configuration::Item::FullTag - Return the fully scoped tag /*{{{*/
256// ---------------------------------------------------------------------
257/* */
258string Configuration::Item::FullTag() const
259{
260 if (Parent == 0 || Parent->Parent == 0)
261 return Tag;
262 return Parent->FullTag() + "::" + Tag;
263}
264 /*}}}*/
265
08e8f724
AL
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. */
270bool 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
bfd22fc0 309 bool InQuote = false;
08e8f724
AL
310 for (char *I = Buffer; *I != 0; I++)
311 {
bfd22fc0
AL
312 if (*I == '"')
313 InQuote = !InQuote;
314 if (InQuote == true)
315 continue;
316
08e8f724
AL
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 {
bfd22fc0
AL
327 if (*I == '"')
328 InQuote = !InQuote;
329 if (InQuote == true)
330 continue;
331
08e8f724
AL
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
7f25bdff
AL
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);
08e8f724 398
08e8f724
AL
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
08e8f724
AL
409 // Parse off the word
410 string Word;
7f25bdff
AL
411 if (ParseCWord(Pos,Word) == false)
412 {
413 if (TermChar != '{')
414 {
415 Word = Tag;
416 Tag = "";
417 }
418 }
bfd22fc0 419
08e8f724
AL
420 // Generate the item name
421 string Item;
422 if (ParentTag.empty() == true)
423 Item = Tag;
424 else
425 {
7f25bdff 426 if (TermChar != '{' || Tag.empty() == false)
08e8f724 427 Item = ParentTag + "::" + Tag;
7f25bdff
AL
428 else
429 Item = ParentTag;
08e8f724
AL
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 /*}}}*/