Working acquire code
[ntk/apt.git] / apt-pkg / contrib / configuration.cc
CommitLineData
6c139d6e
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
bfd22fc0 3// $Id: configuration.cc,v 1.10 1998/11/05 07:21:43 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
bfd22fc0 295 bool InQuote = false;
08e8f724
AL
296 for (char *I = Buffer; *I != 0; I++)
297 {
bfd22fc0
AL
298 if (*I == '"')
299 InQuote = !InQuote;
300 if (InQuote == true)
301 continue;
302
08e8f724
AL
303 if (*I == '/' && I[1] == '/')
304 {
305 *I = 0;
306 break;
307 }
308 }
309
310 // Look for multi line comments
311 for (char *I = Buffer; *I != 0; I++)
312 {
bfd22fc0
AL
313 if (*I == '"')
314 InQuote = !InQuote;
315 if (InQuote == true)
316 continue;
317
08e8f724
AL
318 if (*I == '/' && I[1] == '*')
319 {
320 InComment = true;
321 for (char *J = Buffer; *J != 0; J++)
322 {
323 if (*J == '*' && J[1] == '/')
324 {
325 memmove(I,J+2,strlen(J+2) + 1);
326 InComment = false;
327 break;
328 }
329 }
330
331 if (InComment == true)
332 {
333 *I = 0;
334 break;
335 }
336 }
337 }
338
339 // Blank
340 if (Buffer[0] == 0)
341 continue;
342
343 // We now have a valid line fragment
344 for (char *I = Buffer; *I != 0;)
345 {
346 if (*I == '{' || *I == ';' || *I == '}')
347 {
348 // Put the last fragement into the buffer
349 char *Start = Buffer;
350 char *Stop = I;
351 for (; Start != I && isspace(*Start) != 0; Start++);
352 for (; Stop != Start && isspace(Stop[-1]) != 0; Stop--);
353 if (LineBuffer.empty() == false && Stop - Start != 0)
354 LineBuffer += ' ';
355 LineBuffer += string(Start,Stop - Start);
356
357 // Remove the fragment
358 char TermChar = *I;
359 memmove(Buffer,I + 1,strlen(I + 1) + 1);
360 I = Buffer;
361
362 // Move up a tag
363 if (TermChar == '}')
364 {
365 string::size_type Pos = ParentTag.rfind("::");
366 if (Pos == string::npos)
367 ParentTag = string();
368 else
369 ParentTag = string(ParentTag,0,Pos);
370 }
371
372 // Syntax Error
373 if (TermChar == '{' && LineBuffer.empty() == true)
374 return _error->Error("Syntax error %s:%u: Block starts with no name.",FName.c_str(),CurLine);
375
376 if (LineBuffer.empty() == true)
377 continue;
378
379 // Parse off the tag
380 string::size_type Pos = LineBuffer.find(' ');
381 if (Pos == string::npos)
382 {
383 if (TermChar == '{')
384 Pos = LineBuffer.length();
385 else
386 return _error->Error("Syntax error %s:%u: Tag with no value",FName.c_str(),CurLine);
387 }
388
389 string Tag = string(LineBuffer,0,Pos);
390
391 // Go down a level
392 if (TermChar == '{')
393 {
394 if (ParentTag.empty() == true)
395 ParentTag = Tag;
396 else
397 ParentTag += string("::") + Tag;
398 Tag = string();
399 }
400
401 // We dont have a value to set
402 if (Pos == LineBuffer.length())
403 {
404 LineBuffer = string();
405 continue;
406 }
407
408 // Parse off the word
409 string Word;
410 if (ParseCWord(LineBuffer.c_str()+Pos,Word) == false)
411 return _error->Error("Syntax error %s:%u: Malformed value",FName.c_str(),CurLine);
bfd22fc0 412
08e8f724
AL
413 // Generate the item name
414 string Item;
415 if (ParentTag.empty() == true)
416 Item = Tag;
417 else
418 {
419 if (Tag.empty() == true)
420 Item = ParentTag;
421 else
422 Item = ParentTag + "::" + Tag;
423 }
424
425 // Set the item in the configuration class
426 Conf.Set(Item,Word);
427
428 // Empty the buffer
429 LineBuffer = string();
430 }
431 else
432 I++;
433 }
434
435 // Store the fragment
436 const char *Stripd = _strstrip(Buffer);
437 if (*Stripd != 0 && LineBuffer.empty() == false)
438 LineBuffer += " ";
439 LineBuffer += Stripd;
440 }
441
442 return true;
443}
444 /*}}}*/