]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/configuration.cc
C9x types
[apt.git] / apt-pkg / contrib / configuration.cc
CommitLineData
6c139d6e
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
a4e87467 3// $Id: configuration.cc,v 1.13 1999/07/02 23:17:00 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>
cdcc6d34 20#include <apt-pkg/strutl.h>
6c139d6e
AL
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;
a4e87467
AL
279 string Stack[100];
280 unsigned int StackPos = 0;
08e8f724
AL
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
bfd22fc0 311 bool InQuote = false;
08e8f724
AL
312 for (char *I = Buffer; *I != 0; I++)
313 {
bfd22fc0
AL
314 if (*I == '"')
315 InQuote = !InQuote;
316 if (InQuote == true)
317 continue;
318
08e8f724
AL
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 {
bfd22fc0
AL
329 if (*I == '"')
330 InQuote = !InQuote;
331 if (InQuote == true)
332 continue;
333
08e8f724
AL
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 {
a4e87467 381 if (StackPos == 0)
08e8f724
AL
382 ParentTag = string();
383 else
a4e87467 384 ParentTag = Stack[--StackPos];
08e8f724
AL
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
7f25bdff
AL
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);
08e8f724 399
08e8f724
AL
400 // Go down a level
401 if (TermChar == '{')
402 {
a4e87467
AL
403 if (StackPos <= 100)
404 Stack[StackPos++] = ParentTag;
08e8f724
AL
405 if (ParentTag.empty() == true)
406 ParentTag = Tag;
407 else
408 ParentTag += string("::") + Tag;
409 Tag = string();
410 }
411
08e8f724
AL
412 // Parse off the word
413 string Word;
7f25bdff
AL
414 if (ParseCWord(Pos,Word) == false)
415 {
416 if (TermChar != '{')
417 {
418 Word = Tag;
419 Tag = "";
420 }
421 }
bfd22fc0 422
08e8f724
AL
423 // Generate the item name
424 string Item;
425 if (ParentTag.empty() == true)
426 Item = Tag;
427 else
428 {
7f25bdff 429 if (TermChar != '{' || Tag.empty() == false)
08e8f724 430 Item = ParentTag + "::" + Tag;
7f25bdff
AL
431 else
432 Item = ParentTag;
08e8f724
AL
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 /*}}}*/