]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/configuration.cc
Sync
[apt.git] / apt-pkg / contrib / configuration.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: configuration.cc,v 1.8 1998/10/22 04:56:45 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
216 // Configuration::Item::FullTag - Return the fully scoped tag /*{{{*/
217 // ---------------------------------------------------------------------
218 /* */
219 string Configuration::Item::FullTag() const
220 {
221 if (Parent == 0 || Parent->Parent == 0)
222 return Tag;
223 return Parent->FullTag() + "::" + Tag;
224 }
225 /*}}}*/
226
227 // ReadConfigFile - Read a configuration file /*{{{*/
228 // ---------------------------------------------------------------------
229 /* The configuration format is very much like the named.conf format
230 used in bind8, in fact this routine can parse most named.conf files. */
231 bool ReadConfigFile(Configuration &Conf,string FName)
232 {
233 // Open the stream for reading
234 ifstream F(FName.c_str(),ios::in | ios::nocreate);
235 if (!F != 0)
236 return _error->Errno("ifstream::ifstream","Opening configuration file %s",FName.c_str());
237
238 char Buffer[300];
239 string LineBuffer;
240
241 // Parser state
242 string ParentTag;
243
244 int CurLine = 0;
245 bool InComment = false;
246 while (F.eof() == false)
247 {
248 F.getline(Buffer,sizeof(Buffer));
249 CurLine++;
250 _strtabexpand(Buffer,sizeof(Buffer));
251 _strstrip(Buffer);
252
253 // Multi line comment
254 if (InComment == true)
255 {
256 for (const char *I = Buffer; *I != 0; I++)
257 {
258 if (*I == '*' && I[1] == '/')
259 {
260 memmove(Buffer,I+2,strlen(I+2) + 1);
261 InComment = false;
262 break;
263 }
264 }
265 if (InComment == true)
266 continue;
267 }
268
269 // Discard single line comments
270 for (char *I = Buffer; *I != 0; I++)
271 {
272 if (*I == '/' && I[1] == '/')
273 {
274 *I = 0;
275 break;
276 }
277 }
278
279 // Look for multi line comments
280 for (char *I = Buffer; *I != 0; I++)
281 {
282 if (*I == '/' && I[1] == '*')
283 {
284 InComment = true;
285 for (char *J = Buffer; *J != 0; J++)
286 {
287 if (*J == '*' && J[1] == '/')
288 {
289 memmove(I,J+2,strlen(J+2) + 1);
290 InComment = false;
291 break;
292 }
293 }
294
295 if (InComment == true)
296 {
297 *I = 0;
298 break;
299 }
300 }
301 }
302
303 // Blank
304 if (Buffer[0] == 0)
305 continue;
306
307 // We now have a valid line fragment
308 for (char *I = Buffer; *I != 0;)
309 {
310 if (*I == '{' || *I == ';' || *I == '}')
311 {
312 // Put the last fragement into the buffer
313 char *Start = Buffer;
314 char *Stop = I;
315 for (; Start != I && isspace(*Start) != 0; Start++);
316 for (; Stop != Start && isspace(Stop[-1]) != 0; Stop--);
317 if (LineBuffer.empty() == false && Stop - Start != 0)
318 LineBuffer += ' ';
319 LineBuffer += string(Start,Stop - Start);
320
321 // Remove the fragment
322 char TermChar = *I;
323 memmove(Buffer,I + 1,strlen(I + 1) + 1);
324 I = Buffer;
325
326 // Move up a tag
327 if (TermChar == '}')
328 {
329 string::size_type Pos = ParentTag.rfind("::");
330 if (Pos == string::npos)
331 ParentTag = string();
332 else
333 ParentTag = string(ParentTag,0,Pos);
334 }
335
336 // Syntax Error
337 if (TermChar == '{' && LineBuffer.empty() == true)
338 return _error->Error("Syntax error %s:%u: Block starts with no name.",FName.c_str(),CurLine);
339
340 if (LineBuffer.empty() == true)
341 continue;
342
343 // Parse off the tag
344 string::size_type Pos = LineBuffer.find(' ');
345 if (Pos == string::npos)
346 {
347 if (TermChar == '{')
348 Pos = LineBuffer.length();
349 else
350 return _error->Error("Syntax error %s:%u: Tag with no value",FName.c_str(),CurLine);
351 }
352
353 string Tag = string(LineBuffer,0,Pos);
354
355 // Go down a level
356 if (TermChar == '{')
357 {
358 if (ParentTag.empty() == true)
359 ParentTag = Tag;
360 else
361 ParentTag += string("::") + Tag;
362 Tag = string();
363 }
364
365 // We dont have a value to set
366 if (Pos == LineBuffer.length())
367 {
368 LineBuffer = string();
369 continue;
370 }
371
372 // Parse off the word
373 string Word;
374 if (ParseCWord(LineBuffer.c_str()+Pos,Word) == false)
375 return _error->Error("Syntax error %s:%u: Malformed value",FName.c_str(),CurLine);
376
377 // Generate the item name
378 string Item;
379 if (ParentTag.empty() == true)
380 Item = Tag;
381 else
382 {
383 if (Tag.empty() == true)
384 Item = ParentTag;
385 else
386 Item = ParentTag + "::" + Tag;
387 }
388
389 // Set the item in the configuration class
390 Conf.Set(Item,Word);
391
392 // Empty the buffer
393 LineBuffer = string();
394 }
395 else
396 I++;
397 }
398
399 // Store the fragment
400 const char *Stripd = _strstrip(Buffer);
401 if (*Stripd != 0 && LineBuffer.empty() == false)
402 LineBuffer += " ";
403 LineBuffer += Stripd;
404 }
405
406 return true;
407 }
408 /*}}}*/