]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/configuration.cc
Added the remove command
[apt.git] / apt-pkg / contrib / configuration.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: configuration.cc,v 1.6 1998/10/02 04:39:49 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 sub directory in sub nodes
109 */
110 string 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
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::FindI - Find an integer value /*{{{*/
138 // ---------------------------------------------------------------------
139 /* */
140 int 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
151 return Res;
152 }
153 /*}}}*/
154 // Configuration::FindB - Find a boolean type /*{{{*/
155 // ---------------------------------------------------------------------
156 /* */
157 bool 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
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)
172 return false;
173
174 // Check for negatives
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)
179 return true;
180
181 return Default;
182 }
183
184 return Res;
185 }
186 /*}}}*/
187 // Configuration::Set - Set a value /*{{{*/
188 // ---------------------------------------------------------------------
189 /* */
190 void 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 /* */
201 void 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 /*}}}*/
211 // Configuration::Exists - Returns true if the Name exists /*{{{*/
212 // ---------------------------------------------------------------------
213 /* */
214 bool 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. */
227 bool 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 /*}}}*/