]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/configuration.cc
HTTP bugs
[apt.git] / apt-pkg / contrib / configuration.cc
CommitLineData
6c139d6e
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
93bf083d 3// $Id: configuration.cc,v 1.9 1998/10/30 07:53:42 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 /*}}}*/
0a8a80e5 36// Configuration::Lookup - Lookup a single item /*{{{*/
6c139d6e
AL
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{
0a8a80e5
AL
69 if (Name == 0)
70 return Root->Child;
71
6c139d6e
AL
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);
6c139d6e
AL
88 return Itm;
89}
90 /*}}}*/
91// Configuration::Find - Find a value /*{{{*/
92// ---------------------------------------------------------------------
93/* */
94string Configuration::Find(const char *Name,const char *Default)
95{
96 Item *Itm = Lookup(Name,false);
97 if (Itm == 0 || Itm->Value.empty() == true)
9c14e3d6
AL
98 {
99 if (Default == 0)
100 return string();
101 else
102 return Default;
103 }
104
6c139d6e
AL
105 return Itm->Value;
106}
107 /*}}}*/
3b5421b4 108// Configuration::FindFile - Find a Filename /*{{{*/
9c14e3d6
AL
109// ---------------------------------------------------------------------
110/* Directories are stored as the base dir in the Parent node and the
3b5421b4 111 sub directory in sub nodes with the final node being the end filename
9c14e3d6 112 */
3b5421b4 113string Configuration::FindFile(const char *Name,const char *Default)
9c14e3d6
AL
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
0a8e3465 124 // Absolute path
9c14e3d6
AL
125 if (Itm->Value[0] == '/' || Itm->Parent == 0)
126 return Itm->Value;
0a8e3465
AL
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
9c14e3d6
AL
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 /*}}}*/
3b5421b4
AL
140// Configuration::FindDir - Find a directory name /*{{{*/
141// ---------------------------------------------------------------------
142/* This is like findfile execept the result is terminated in a / */
143string 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 /*}}}*/
6c139d6e
AL
151// Configuration::FindI - Find an integer value /*{{{*/
152// ---------------------------------------------------------------------
153/* */
154int 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
08e8f724
AL
165 return Res;
166}
167 /*}}}*/
168// Configuration::FindB - Find a boolean type /*{{{*/
169// ---------------------------------------------------------------------
170/* */
171bool 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
3b5421b4 177 return StringToBool(Itm->Value,Default);
6c139d6e
AL
178}
179 /*}}}*/
180// Configuration::Set - Set a value /*{{{*/
181// ---------------------------------------------------------------------
182/* */
183void 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/* */
194void 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 /*}}}*/
08e8f724
AL
204// Configuration::Exists - Returns true if the Name exists /*{{{*/
205// ---------------------------------------------------------------------
206/* */
207bool Configuration::Exists(const char *Name)
208{
209 Item *Itm = Lookup(Name,false);
210 if (Itm == 0)
211 return false;
212 return true;
213}
214 /*}}}*/
93bf083d
AL
215// Configuration::Dump - Dump the config /*{{{*/
216// ---------------------------------------------------------------------
217/* Dump the entire configuration space */
218void Configuration::Dump()
219{
220 /* Write out all of the configuration directives by walking the
221 configuration tree */
222 const Configuration::Item *Top = _config->Tree(0);
223 for (; Top != 0;)
224 {
225 clog << Top->FullTag() << " \"" << Top->Value << "\";" << endl;
226
227 if (Top->Child != 0)
228 {
229 Top = Top->Child;
230 continue;
231 }
232
233 while (Top != 0 && Top->Next == 0)
234 Top = Top->Parent;
235 if (Top != 0)
236 Top = Top->Next;
237 }
238}
239 /*}}}*/
08e8f724 240
0a8a80e5
AL
241// Configuration::Item::FullTag - Return the fully scoped tag /*{{{*/
242// ---------------------------------------------------------------------
243/* */
244string Configuration::Item::FullTag() const
245{
246 if (Parent == 0 || Parent->Parent == 0)
247 return Tag;
248 return Parent->FullTag() + "::" + Tag;
249}
250 /*}}}*/
251
08e8f724
AL
252// ReadConfigFile - Read a configuration file /*{{{*/
253// ---------------------------------------------------------------------
254/* The configuration format is very much like the named.conf format
255 used in bind8, in fact this routine can parse most named.conf files. */
256bool ReadConfigFile(Configuration &Conf,string FName)
257{
258 // Open the stream for reading
259 ifstream F(FName.c_str(),ios::in | ios::nocreate);
260 if (!F != 0)
261 return _error->Errno("ifstream::ifstream","Opening configuration file %s",FName.c_str());
262
263 char Buffer[300];
264 string LineBuffer;
265
266 // Parser state
267 string ParentTag;
268
269 int CurLine = 0;
270 bool InComment = false;
271 while (F.eof() == false)
272 {
273 F.getline(Buffer,sizeof(Buffer));
274 CurLine++;
275 _strtabexpand(Buffer,sizeof(Buffer));
276 _strstrip(Buffer);
277
278 // Multi line comment
279 if (InComment == true)
280 {
281 for (const char *I = Buffer; *I != 0; I++)
282 {
283 if (*I == '*' && I[1] == '/')
284 {
285 memmove(Buffer,I+2,strlen(I+2) + 1);
286 InComment = false;
287 break;
288 }
289 }
290 if (InComment == true)
291 continue;
292 }
293
294 // Discard single line comments
295 for (char *I = Buffer; *I != 0; I++)
296 {
297 if (*I == '/' && I[1] == '/')
298 {
299 *I = 0;
300 break;
301 }
302 }
303
304 // Look for multi line comments
305 for (char *I = Buffer; *I != 0; I++)
306 {
307 if (*I == '/' && I[1] == '*')
308 {
309 InComment = true;
310 for (char *J = Buffer; *J != 0; J++)
311 {
312 if (*J == '*' && J[1] == '/')
313 {
314 memmove(I,J+2,strlen(J+2) + 1);
315 InComment = false;
316 break;
317 }
318 }
319
320 if (InComment == true)
321 {
322 *I = 0;
323 break;
324 }
325 }
326 }
327
328 // Blank
329 if (Buffer[0] == 0)
330 continue;
331
332 // We now have a valid line fragment
333 for (char *I = Buffer; *I != 0;)
334 {
335 if (*I == '{' || *I == ';' || *I == '}')
336 {
337 // Put the last fragement into the buffer
338 char *Start = Buffer;
339 char *Stop = I;
340 for (; Start != I && isspace(*Start) != 0; Start++);
341 for (; Stop != Start && isspace(Stop[-1]) != 0; Stop--);
342 if (LineBuffer.empty() == false && Stop - Start != 0)
343 LineBuffer += ' ';
344 LineBuffer += string(Start,Stop - Start);
345
346 // Remove the fragment
347 char TermChar = *I;
348 memmove(Buffer,I + 1,strlen(I + 1) + 1);
349 I = Buffer;
350
351 // Move up a tag
352 if (TermChar == '}')
353 {
354 string::size_type Pos = ParentTag.rfind("::");
355 if (Pos == string::npos)
356 ParentTag = string();
357 else
358 ParentTag = string(ParentTag,0,Pos);
359 }
360
361 // Syntax Error
362 if (TermChar == '{' && LineBuffer.empty() == true)
363 return _error->Error("Syntax error %s:%u: Block starts with no name.",FName.c_str(),CurLine);
364
365 if (LineBuffer.empty() == true)
366 continue;
367
368 // Parse off the tag
369 string::size_type Pos = LineBuffer.find(' ');
370 if (Pos == string::npos)
371 {
372 if (TermChar == '{')
373 Pos = LineBuffer.length();
374 else
375 return _error->Error("Syntax error %s:%u: Tag with no value",FName.c_str(),CurLine);
376 }
377
378 string Tag = string(LineBuffer,0,Pos);
379
380 // Go down a level
381 if (TermChar == '{')
382 {
383 if (ParentTag.empty() == true)
384 ParentTag = Tag;
385 else
386 ParentTag += string("::") + Tag;
387 Tag = string();
388 }
389
390 // We dont have a value to set
391 if (Pos == LineBuffer.length())
392 {
393 LineBuffer = string();
394 continue;
395 }
396
397 // Parse off the word
398 string Word;
399 if (ParseCWord(LineBuffer.c_str()+Pos,Word) == false)
400 return _error->Error("Syntax error %s:%u: Malformed value",FName.c_str(),CurLine);
401
402 // Generate the item name
403 string Item;
404 if (ParentTag.empty() == true)
405 Item = Tag;
406 else
407 {
408 if (Tag.empty() == true)
409 Item = ParentTag;
410 else
411 Item = ParentTag + "::" + Tag;
412 }
413
414 // Set the item in the configuration class
415 Conf.Set(Item,Word);
416
417 // Empty the buffer
418 LineBuffer = string();
419 }
420 else
421 I++;
422 }
423
424 // Store the fragment
425 const char *Stripd = _strstrip(Buffer);
426 if (*Stripd != 0 && LineBuffer.empty() == false)
427 LineBuffer += " ";
428 LineBuffer += Stripd;
429 }
430
431 return true;
432}
433 /*}}}*/