Sync
[ntk/apt.git] / apt-pkg / contrib / configuration.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: configuration.cc,v 1.4 1998/09/22 05:30:26 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 const char *Start = Name;
70 const char *End = Start + strlen(Name);
71 const char *TagEnd = Name;
72 Item *Itm = Root;
73 for (; End - TagEnd > 2; TagEnd++)
74 {
75 if (TagEnd[0] == ':' && TagEnd[1] == ':')
76 {
77 Itm = Lookup(Itm,Start,TagEnd - Start,Create);
78 if (Itm == 0)
79 return 0;
80 TagEnd = Start = TagEnd + 2;
81 }
82 }
83
84 Itm = Lookup(Itm,Start,End - Start,Create);
85 return Itm;
86 }
87 /*}}}*/
88 // Configuration::Find - Find a value /*{{{*/
89 // ---------------------------------------------------------------------
90 /* */
91 string Configuration::Find(const char *Name,const char *Default)
92 {
93 Item *Itm = Lookup(Name,false);
94 if (Itm == 0 || Itm->Value.empty() == true)
95 {
96 if (Default == 0)
97 return string();
98 else
99 return Default;
100 }
101
102 return Itm->Value;
103 }
104 /*}}}*/
105 // Configuration::FindDir - Find a directory /*{{{*/
106 // ---------------------------------------------------------------------
107 /* Directories are stored as the base dir in the Parent node and the
108 */
109 string Configuration::FindDir(const char *Name,const char *Default = 0)
110 {
111 Item *Itm = Lookup(Name,false);
112 if (Itm == 0 || Itm->Value.empty() == true)
113 {
114 if (Default == 0)
115 return string();
116 else
117 return Default;
118 }
119
120 if (Itm->Value[0] == '/' || Itm->Parent == 0)
121 return Itm->Value;
122 if (Itm->Parent->Value.end()[-1] == '/')
123 return Itm->Parent->Value + Itm->Value;
124 else
125 return Itm->Parent->Value + '/' + Itm->Value;
126 }
127 /*}}}*/
128 // Configuration::FindI - Find an integer value /*{{{*/
129 // ---------------------------------------------------------------------
130 /* */
131 int Configuration::FindI(const char *Name,int Default)
132 {
133 Item *Itm = Lookup(Name,false);
134 if (Itm == 0 || Itm->Value.empty() == true)
135 return Default;
136
137 char *End;
138 int Res = strtol(Itm->Value.c_str(),&End,0);
139 if (End == Itm->Value.c_str())
140 return Default;
141
142 return Res;
143 }
144 /*}}}*/
145 // Configuration::FindB - Find a boolean type /*{{{*/
146 // ---------------------------------------------------------------------
147 /* */
148 bool Configuration::FindB(const char *Name,bool Default)
149 {
150 Item *Itm = Lookup(Name,false);
151 if (Itm == 0 || Itm->Value.empty() == true)
152 return Default;
153
154 char *End;
155 int Res = strtol(Itm->Value.c_str(),&End,0);
156 if (End == Itm->Value.c_str() || Res < 0 || Res > 1)
157 {
158 // Check for positives
159 if (strcasecmp(Itm->Value,"no") == 0 ||
160 strcasecmp(Itm->Value,"false") == 0 ||
161 strcasecmp(Itm->Value,"without") == 0 ||
162 strcasecmp(Itm->Value,"disable") == 0)
163 return false;
164
165 // Check for negatives
166 if (strcasecmp(Itm->Value,"no") == 0 ||
167 strcasecmp(Itm->Value,"false") == 0 ||
168 strcasecmp(Itm->Value,"without") == 0 ||
169 strcasecmp(Itm->Value,"disable") == 0)
170 return false;
171
172 return Default;
173 }
174
175 return Res;
176 }
177 /*}}}*/
178 // Configuration::Set - Set a value /*{{{*/
179 // ---------------------------------------------------------------------
180 /* */
181 void Configuration::Set(const char *Name,string Value)
182 {
183 Item *Itm = Lookup(Name,true);
184 if (Itm == 0)
185 return;
186 Itm->Value = Value;
187 }
188 /*}}}*/
189 // Configuration::Set - Set an integer value /*{{{*/
190 // ---------------------------------------------------------------------
191 /* */
192 void Configuration::Set(const char *Name,int Value)
193 {
194 Item *Itm = Lookup(Name,true);
195 if (Itm == 0)
196 return;
197 char S[300];
198 snprintf(S,sizeof(S),"%i",Value);
199 Itm->Value = S;
200 }
201 /*}}}*/
202 // Configuration::Exists - Returns true if the Name exists /*{{{*/
203 // ---------------------------------------------------------------------
204 /* */
205 bool Configuration::Exists(const char *Name)
206 {
207 Item *Itm = Lookup(Name,false);
208 if (Itm == 0)
209 return false;
210 return true;
211 }
212 /*}}}*/
213
214 // ReadConfigFile - Read a configuration file /*{{{*/
215 // ---------------------------------------------------------------------
216 /* The configuration format is very much like the named.conf format
217 used in bind8, in fact this routine can parse most named.conf files. */
218 bool ReadConfigFile(Configuration &Conf,string FName)
219 {
220 // Open the stream for reading
221 ifstream F(FName.c_str(),ios::in | ios::nocreate);
222 if (!F != 0)
223 return _error->Errno("ifstream::ifstream","Opening configuration file %s",FName.c_str());
224
225 char Buffer[300];
226 string LineBuffer;
227
228 // Parser state
229 string ParentTag;
230
231 int CurLine = 0;
232 bool InComment = false;
233 while (F.eof() == false)
234 {
235 F.getline(Buffer,sizeof(Buffer));
236 CurLine++;
237 _strtabexpand(Buffer,sizeof(Buffer));
238 _strstrip(Buffer);
239
240 // Multi line comment
241 if (InComment == true)
242 {
243 for (const char *I = Buffer; *I != 0; I++)
244 {
245 if (*I == '*' && I[1] == '/')
246 {
247 memmove(Buffer,I+2,strlen(I+2) + 1);
248 InComment = false;
249 break;
250 }
251 }
252 if (InComment == true)
253 continue;
254 }
255
256 // Discard single line comments
257 for (char *I = Buffer; *I != 0; I++)
258 {
259 if (*I == '/' && I[1] == '/')
260 {
261 *I = 0;
262 break;
263 }
264 }
265
266 // Look for multi line comments
267 for (char *I = Buffer; *I != 0; I++)
268 {
269 if (*I == '/' && I[1] == '*')
270 {
271 InComment = true;
272 for (char *J = Buffer; *J != 0; J++)
273 {
274 if (*J == '*' && J[1] == '/')
275 {
276 memmove(I,J+2,strlen(J+2) + 1);
277 InComment = false;
278 break;
279 }
280 }
281
282 if (InComment == true)
283 {
284 *I = 0;
285 break;
286 }
287 }
288 }
289
290 // Blank
291 if (Buffer[0] == 0)
292 continue;
293
294 // We now have a valid line fragment
295 for (char *I = Buffer; *I != 0;)
296 {
297 if (*I == '{' || *I == ';' || *I == '}')
298 {
299 // Put the last fragement into the buffer
300 char *Start = Buffer;
301 char *Stop = I;
302 for (; Start != I && isspace(*Start) != 0; Start++);
303 for (; Stop != Start && isspace(Stop[-1]) != 0; Stop--);
304 if (LineBuffer.empty() == false && Stop - Start != 0)
305 LineBuffer += ' ';
306 LineBuffer += string(Start,Stop - Start);
307
308 // Remove the fragment
309 char TermChar = *I;
310 memmove(Buffer,I + 1,strlen(I + 1) + 1);
311 I = Buffer;
312
313 // Move up a tag
314 if (TermChar == '}')
315 {
316 string::size_type Pos = ParentTag.rfind("::");
317 if (Pos == string::npos)
318 ParentTag = string();
319 else
320 ParentTag = string(ParentTag,0,Pos);
321 }
322
323 // Syntax Error
324 if (TermChar == '{' && LineBuffer.empty() == true)
325 return _error->Error("Syntax error %s:%u: Block starts with no name.",FName.c_str(),CurLine);
326
327 if (LineBuffer.empty() == true)
328 continue;
329
330 // Parse off the tag
331 string::size_type Pos = LineBuffer.find(' ');
332 if (Pos == string::npos)
333 {
334 if (TermChar == '{')
335 Pos = LineBuffer.length();
336 else
337 return _error->Error("Syntax error %s:%u: Tag with no value",FName.c_str(),CurLine);
338 }
339
340 string Tag = string(LineBuffer,0,Pos);
341
342 // Go down a level
343 if (TermChar == '{')
344 {
345 if (ParentTag.empty() == true)
346 ParentTag = Tag;
347 else
348 ParentTag += string("::") + Tag;
349 Tag = string();
350 }
351
352 // We dont have a value to set
353 if (Pos == LineBuffer.length())
354 {
355 LineBuffer = string();
356 continue;
357 }
358
359 // Parse off the word
360 string Word;
361 if (ParseCWord(LineBuffer.c_str()+Pos,Word) == false)
362 return _error->Error("Syntax error %s:%u: Malformed value",FName.c_str(),CurLine);
363
364 // Generate the item name
365 string Item;
366 if (ParentTag.empty() == true)
367 Item = Tag;
368 else
369 {
370 if (Tag.empty() == true)
371 Item = ParentTag;
372 else
373 Item = ParentTag + "::" + Tag;
374 }
375
376 // Set the item in the configuration class
377 Conf.Set(Item,Word);
378
379 // Empty the buffer
380 LineBuffer = string();
381 }
382 else
383 I++;
384 }
385
386 // Store the fragment
387 const char *Stripd = _strstrip(Buffer);
388 if (*Stripd != 0 && LineBuffer.empty() == false)
389 LineBuffer += " ";
390 LineBuffer += Stripd;
391 }
392
393 return true;
394 }
395 /*}}}*/