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