1 // -*- mode: cpp; mode: fold -*-
3 // $Id: configuration.cc,v 1.5 1999/11/17 05:59:29 jgg Exp $
4 /* ######################################################################
8 This class provides a configuration file and command line parser
9 for a tree-oriented configuration environment. All runtime configuration
12 ##################################################################### */
14 // Include files /*{{{*/
16 #pragma implementation "dsync/configuration.h"
18 #include <dsync/configuration.h>
19 #include <dsync/error.h>
20 #include <dsync/strutl.h>
28 Configuration *_config = new Configuration;
30 // Configuration::Configuration - Constructor /*{{{*/
31 // ---------------------------------------------------------------------
33 Configuration::Configuration()
38 // Configuration::Lookup - Lookup a single item /*{{{*/
39 // ---------------------------------------------------------------------
40 /* This will lookup a single item by name below another item. It is a
41 helper function for the main lookup function */
42 Configuration::Item *Configuration::Lookup(Item *Head,const char *S,
43 unsigned long Len,bool Create)
46 Item *I = Head->Child;
47 Item **Last = &Head->Child;
49 // Empty strings match nothing. They are used for lists.
52 for (; I != 0; Last = &I->Next, I = I->Next)
53 if ((Res = stringcasecmp(I->Tag.c_str(), I->Tag.c_str() + strlen(I->Tag.c_str()),S,S + Len)) == 0)
57 for (; I != 0; Last = &I->Next, I = I->Next);
65 I->Tag = string(S,Len);
72 // Configuration::Lookup - Lookup a fully scoped item /*{{{*/
73 // ---------------------------------------------------------------------
74 /* This performs a fully scoped lookup of a given name, possibly creating
76 Configuration::Item *Configuration::Lookup(const char *Name,bool Create)
81 const char *Start = Name;
82 const char *End = Start + strlen(Name);
83 const char *TagEnd = Name;
85 for (; End - TagEnd >= 2; TagEnd++)
87 if (TagEnd[0] == ':' && TagEnd[1] == ':')
89 Itm = Lookup(Itm,Start,TagEnd - Start,Create);
92 TagEnd = Start = TagEnd + 2;
96 // This must be a trailing ::, we create unique items in a list
103 Itm = Lookup(Itm,Start,End - Start,Create);
107 // Configuration::Find - Find a value /*{{{*/
108 // ---------------------------------------------------------------------
110 string Configuration::Find(const char *Name,const char *Default)
112 Item *Itm = Lookup(Name,false);
113 if (Itm == 0 || Itm->Value.empty() == true)
124 // Configuration::FindFile - Find a Filename /*{{{*/
125 // ---------------------------------------------------------------------
126 /* Directories are stored as the base dir in the Parent node and the
127 sub directory in sub nodes with the final node being the end filename
129 string Configuration::FindFile(const char *Name,const char *Default)
131 Item *Itm = Lookup(Name,false);
132 if (Itm == 0 || Itm->Value.empty() == true)
141 if (Itm->Value[0] == '/' || Itm->Parent == 0)
144 // ./ is also considered absolute as is anything with ~ in it
145 if (Itm->Value[0] != 0 &&
146 ((Itm->Value[0] == '.' && Itm->Value[1] == '/') ||
147 (Itm->Value[0] == '~' && Itm->Value[1] == '/')))
150 if (Itm->Parent->Value.end()[-1] == '/')
151 return Itm->Parent->Value + Itm->Value;
153 return Itm->Parent->Value + '/' + Itm->Value;
156 // Configuration::FindDir - Find a directory name /*{{{*/
157 // ---------------------------------------------------------------------
158 /* This is like findfile execept the result is terminated in a / */
159 string Configuration::FindDir(const char *Name,const char *Default)
161 string Res = FindFile(Name,Default);
162 if (Res.end()[-1] != '/')
167 // Configuration::FindI - Find an integer value /*{{{*/
168 // ---------------------------------------------------------------------
170 int Configuration::FindI(const char *Name,int Default)
172 Item *Itm = Lookup(Name,false);
173 if (Itm == 0 || Itm->Value.empty() == true)
177 int Res = strtol(Itm->Value.c_str(),&End,0);
178 if (End == Itm->Value.c_str())
184 // Configuration::FindB - Find a boolean type /*{{{*/
185 // ---------------------------------------------------------------------
187 bool Configuration::FindB(const char *Name,bool Default)
189 Item *Itm = Lookup(Name,false);
190 if (Itm == 0 || Itm->Value.empty() == true)
193 return StringToBool(Itm->Value,Default);
196 // Configuration::Set - Set a value /*{{{*/
197 // ---------------------------------------------------------------------
199 void Configuration::Set(const char *Name,string Value)
201 Item *Itm = Lookup(Name,true);
207 // Configuration::Set - Set an integer value /*{{{*/
208 // ---------------------------------------------------------------------
210 void Configuration::Set(const char *Name,int Value)
212 Item *Itm = Lookup(Name,true);
216 snprintf(S,sizeof(S),"%i",Value);
220 // Configuration::Exists - Returns true if the Name exists /*{{{*/
221 // ---------------------------------------------------------------------
223 bool Configuration::Exists(const char *Name)
225 Item *Itm = Lookup(Name,false);
231 // Configuration::Dump - Dump the config /*{{{*/
232 // ---------------------------------------------------------------------
233 /* Dump the entire configuration space */
234 void Configuration::Dump()
236 /* Write out all of the configuration directives by walking the
237 configuration tree */
238 const Configuration::Item *Top = _config->Tree(0);
241 clog << Top->FullTag() << " \"" << Top->Value << "\";" << endl;
249 while (Top != 0 && Top->Next == 0)
257 // Configuration::Item::FullTag - Return the fully scoped tag /*{{{*/
258 // ---------------------------------------------------------------------
260 string Configuration::Item::FullTag() const
262 if (Parent == 0 || Parent->Parent == 0)
264 return Parent->FullTag() + "::" + Tag;
268 // ReadConfigFile - Read a configuration file /*{{{*/
269 // ---------------------------------------------------------------------
270 /* The configuration format is very much like the named.conf format
271 used in bind8, in fact this routine can parse most named.conf files. */
272 bool ReadConfigFile(Configuration &Conf,string FName)
274 // Open the stream for reading
275 ifstream F(FName.c_str(),ios::in);
277 return _error->Errno("ifstream::ifstream","Opening configuration file %s",FName.c_str());
282 unsigned int StackPos = 0;
288 bool InComment = false;
289 while (F.eof() == false)
291 F.getline(Buffer,sizeof(Buffer));
293 _strtabexpand(Buffer,sizeof(Buffer));
296 // Multi line comment
297 if (InComment == true)
299 for (const char *I = Buffer; *I != 0; I++)
301 if (*I == '*' && I[1] == '/')
303 memmove(Buffer,I+2,strlen(I+2) + 1);
308 if (InComment == true)
312 // Discard single line comments
313 bool InQuote = false;
314 for (char *I = Buffer; *I != 0; I++)
321 if (*I == '/' && I[1] == '/')
328 // Look for multi line comments
329 for (char *I = Buffer; *I != 0; I++)
336 if (*I == '/' && I[1] == '*')
339 for (char *J = Buffer; *J != 0; J++)
341 if (*J == '*' && J[1] == '/')
343 memmove(I,J+2,strlen(J+2) + 1);
349 if (InComment == true)
361 // We now have a valid line fragment
362 for (char *I = Buffer; *I != 0;)
364 if (*I == '{' || *I == ';' || *I == '}')
366 // Put the last fragement into the buffer
367 char *Start = Buffer;
369 for (; Start != I && isspace(*Start) != 0; Start++);
370 for (; Stop != Start && isspace(Stop[-1]) != 0; Stop--);
371 if (LineBuffer.empty() == false && Stop - Start != 0)
373 LineBuffer += string(Start,Stop - Start);
375 // Remove the fragment
377 memmove(Buffer,I + 1,strlen(I + 1) + 1);
384 ParentTag = string();
386 ParentTag = Stack[--StackPos];
390 if (TermChar == '{' && LineBuffer.empty() == true)
391 return _error->Error("Syntax error %s:%u: Block starts with no name.",FName.c_str(),CurLine);
393 if (LineBuffer.empty() == true)
398 const char *Pos = LineBuffer.c_str();
399 if (ParseQuoteWord(Pos,Tag) == false)
400 return _error->Error("Syntax error %s:%u: Malformed Tag",FName.c_str(),CurLine);
406 Stack[StackPos++] = ParentTag;
407 if (ParentTag.empty() == true)
410 ParentTag += string("::") + Tag;
414 // Parse off the word
416 if (ParseCWord(Pos,Word) == false)
425 // Generate the item name
427 if (ParentTag.empty() == true)
431 if (TermChar != '{' || Tag.empty() == false)
432 Item = ParentTag + "::" + Tag;
437 // Set the item in the configuration class
441 LineBuffer = string();
447 // Store the fragment
448 const char *Stripd = _strstrip(Buffer);
449 if (*Stripd != 0 && LineBuffer.empty() == false)
451 LineBuffer += Stripd;