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