X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/7f25bdff3a0fa55ba897964d5c82475b45f3325a..2a49601f69e08f06fb2727d869d420daacdd09d5:/apt-pkg/contrib/configuration.cc?ds=inline diff --git a/apt-pkg/contrib/configuration.cc b/apt-pkg/contrib/configuration.cc index e03001f84..31cd9f8ad 100644 --- a/apt-pkg/contrib/configuration.cc +++ b/apt-pkg/contrib/configuration.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: configuration.cc,v 1.11 1999/01/18 06:20:07 jgg Exp $ +// $Id: configuration.cc,v 1.28 2004/04/30 04:00:15 mdz Exp $ /* ###################################################################### Configuration Class @@ -8,19 +8,27 @@ This class provides a configuration file and command line parser for a tree-oriented configuration environment. All runtime configuration is stored in here. + + This source is placed in the Public Domain, do with it what you will + It was originally written by Jason Gunthorpe . ##################################################################### */ /*}}}*/ // Include files /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/configuration.h" -#endif +#include + #include #include -#include +#include +#include + +#include +#include +#include -#include -#include +#include + +using namespace std; /*}}}*/ Configuration *_config = new Configuration; @@ -28,9 +36,45 @@ Configuration *_config = new Configuration; // Configuration::Configuration - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ -Configuration::Configuration() +Configuration::Configuration() : ToFree(true) { Root = new Item; +} +Configuration::Configuration(const Item *Root) : Root((Item *)Root), ToFree(false) +{ +}; + + /*}}}*/ +// Configuration::~Configuration - Destructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +Configuration::~Configuration() +{ + if (ToFree == false) + return; + + Item *Top = Root; + for (; Top != 0;) + { + if (Top->Child != 0) + { + Top = Top->Child; + continue; + } + + while (Top != 0 && Top->Next == 0) + { + Item *Parent = Top->Parent; + delete Top; + Top = Parent; + } + if (Top != 0) + { + Item *Next = Top->Next; + delete Top; + Top = Next; + } + } } /*}}}*/ // Configuration::Lookup - Lookup a single item /*{{{*/ @@ -38,7 +82,7 @@ Configuration::Configuration() /* This will lookup a single item by name below another item. It is a helper function for the main lookup function */ Configuration::Item *Configuration::Lookup(Item *Head,const char *S, - unsigned long Len,bool Create) + unsigned long const &Len,bool const &Create) { int Res = 1; Item *I = Head->Child; @@ -48,7 +92,7 @@ Configuration::Item *Configuration::Lookup(Item *Head,const char *S, if (Len != 0) { for (; I != 0; Last = &I->Next, I = I->Next) - if ((Res = stringcasecmp(I->Tag.begin(),I->Tag.end(),S,S + Len)) == 0) + if ((Res = stringcasecmp(I->Tag,S,S + Len)) == 0) break; } else @@ -60,7 +104,7 @@ Configuration::Item *Configuration::Lookup(Item *Head,const char *S, return 0; I = new Item; - I->Tag = string(S,Len); + I->Tag.assign(S,Len); I->Next = *Last; I->Parent = Head; *Last = I; @@ -71,7 +115,7 @@ Configuration::Item *Configuration::Lookup(Item *Head,const char *S, // --------------------------------------------------------------------- /* This performs a fully scoped lookup of a given name, possibly creating new items */ -Configuration::Item *Configuration::Lookup(const char *Name,bool Create) +Configuration::Item *Configuration::Lookup(const char *Name,bool const &Create) { if (Name == 0) return Root->Child; @@ -105,13 +149,13 @@ Configuration::Item *Configuration::Lookup(const char *Name,bool Create) // Configuration::Find - Find a value /*{{{*/ // --------------------------------------------------------------------- /* */ -string Configuration::Find(const char *Name,const char *Default) +string Configuration::Find(const char *Name,const char *Default) const { - Item *Itm = Lookup(Name,false); + const Item *Itm = Lookup(Name); if (Itm == 0 || Itm->Value.empty() == true) { if (Default == 0) - return string(); + return ""; else return Default; } @@ -124,50 +168,106 @@ string Configuration::Find(const char *Name,const char *Default) /* Directories are stored as the base dir in the Parent node and the sub directory in sub nodes with the final node being the end filename */ -string Configuration::FindFile(const char *Name,const char *Default) +string Configuration::FindFile(const char *Name,const char *Default) const { - Item *Itm = Lookup(Name,false); + const Item *RootItem = Lookup("RootDir"); + std::string result = (RootItem == 0) ? "" : RootItem->Value; + if(result.empty() == false && result[result.size() - 1] != '/') + result.push_back('/'); + + const Item *Itm = Lookup(Name); if (Itm == 0 || Itm->Value.empty() == true) { - if (Default == 0) - return string(); - else - return Default; + if (Default != 0) + result.append(Default); } - - // Absolute path - if (Itm->Value[0] == '/' || Itm->Parent == 0) - return Itm->Value; - - // ./ is also considered absolute as is anything with ~ in it - if (Itm->Value[0] != 0 && - ((Itm->Value[0] == '.' && Itm->Value[1] == '/') || - (Itm->Value[0] == '~' && Itm->Value[1] == '/'))) - return Itm->Value; - - if (Itm->Parent->Value.end()[-1] == '/') - return Itm->Parent->Value + Itm->Value; else - return Itm->Parent->Value + '/' + Itm->Value; + { + string val = Itm->Value; + while (Itm->Parent != 0) + { + if (Itm->Parent->Value.empty() == true) + { + Itm = Itm->Parent; + continue; + } + + // Absolute + if (val.length() >= 1 && val[0] == '/') + { + if (val.compare(0, 9, "/dev/null") == 0) + val.erase(9); + break; + } + + // ~/foo or ./foo + if (val.length() >= 2 && (val[0] == '~' || val[0] == '.') && val[1] == '/') + break; + + // ../foo + if (val.length() >= 3 && val[0] == '.' && val[1] == '.' && val[2] == '/') + break; + + if (Itm->Parent->Value.end()[-1] != '/') + val.insert(0, "/"); + + val.insert(0, Itm->Parent->Value); + Itm = Itm->Parent; + } + result.append(val); + } + + // do some normalisation by removing // and /./ from the path + size_t found = string::npos; + while ((found = result.find("/./")) != string::npos) + result.replace(found, 3, "/"); + while ((found = result.find("//")) != string::npos) + result.replace(found, 2, "/"); + + return result; } /*}}}*/ // Configuration::FindDir - Find a directory name /*{{{*/ // --------------------------------------------------------------------- /* This is like findfile execept the result is terminated in a / */ -string Configuration::FindDir(const char *Name,const char *Default) +string Configuration::FindDir(const char *Name,const char *Default) const { string Res = FindFile(Name,Default); if (Res.end()[-1] != '/') + { + size_t const found = Res.rfind("/dev/null"); + if (found != string::npos && found == Res.size() - 9) + return Res; // /dev/null returning return Res + '/'; + } return Res; } /*}}}*/ +// Configuration::FindVector - Find a vector of values /*{{{*/ +// --------------------------------------------------------------------- +/* Returns a vector of config values under the given item */ +vector Configuration::FindVector(const char *Name) const +{ + vector Vec; + const Item *Top = Lookup(Name); + if (Top == NULL) + return Vec; + + Item *I = Top->Child; + while(I != NULL) + { + Vec.push_back(I->Value); + I = I->Next; + } + return Vec; +} + /*}}}*/ // Configuration::FindI - Find an integer value /*{{{*/ // --------------------------------------------------------------------- /* */ -int Configuration::FindI(const char *Name,int Default) +int Configuration::FindI(const char *Name,int const &Default) const { - Item *Itm = Lookup(Name,false); + const Item *Itm = Lookup(Name); if (Itm == 0 || Itm->Value.empty() == true) return Default; @@ -182,19 +282,85 @@ int Configuration::FindI(const char *Name,int Default) // Configuration::FindB - Find a boolean type /*{{{*/ // --------------------------------------------------------------------- /* */ -bool Configuration::FindB(const char *Name,bool Default) +bool Configuration::FindB(const char *Name,bool const &Default) const { - Item *Itm = Lookup(Name,false); + const Item *Itm = Lookup(Name); if (Itm == 0 || Itm->Value.empty() == true) return Default; return StringToBool(Itm->Value,Default); } /*}}}*/ +// Configuration::FindAny - Find an arbitrary type /*{{{*/ +// --------------------------------------------------------------------- +/* a key suffix of /f, /d, /b or /i calls Find{File,Dir,B,I} */ +string Configuration::FindAny(const char *Name,const char *Default) const +{ + string key = Name; + char type = 0; + + if (key.size() > 2 && key.end()[-2] == '/') + { + type = key.end()[-1]; + key.resize(key.size() - 2); + } + + switch (type) + { + // file + case 'f': + return FindFile(key.c_str(), Default); + + // directory + case 'd': + return FindDir(key.c_str(), Default); + + // bool + case 'b': + return FindB(key, Default) ? "true" : "false"; + + // int + case 'i': + { + char buf[16]; + snprintf(buf, sizeof(buf)-1, "%d", FindI(key, Default ? atoi(Default) : 0 )); + return buf; + } + } + + // fallback + return Find(Name, Default); +} + /*}}}*/ +// Configuration::CndSet - Conditinal Set a value /*{{{*/ +// --------------------------------------------------------------------- +/* This will not overwrite */ +void Configuration::CndSet(const char *Name,const string &Value) +{ + Item *Itm = Lookup(Name,true); + if (Itm == 0) + return; + if (Itm->Value.empty() == true) + Itm->Value = Value; +} + /*}}}*/ +// Configuration::Set - Set an integer value /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void Configuration::CndSet(const char *Name,int const Value) +{ + Item *Itm = Lookup(Name,true); + if (Itm == 0 || Itm->Value.empty() == false) + return; + char S[300]; + snprintf(S,sizeof(S),"%i",Value); + Itm->Value = S; +} + /*}}}*/ // Configuration::Set - Set a value /*{{{*/ // --------------------------------------------------------------------- /* */ -void Configuration::Set(const char *Name,string Value) +void Configuration::Set(const char *Name,const string &Value) { Item *Itm = Lookup(Name,true); if (Itm == 0) @@ -205,7 +371,7 @@ void Configuration::Set(const char *Name,string Value) // Configuration::Set - Set an integer value /*{{{*/ // --------------------------------------------------------------------- /* */ -void Configuration::Set(const char *Name,int Value) +void Configuration::Set(const char *Name,int const &Value) { Item *Itm = Lookup(Name,true); if (Itm == 0) @@ -215,67 +381,244 @@ void Configuration::Set(const char *Name,int Value) Itm->Value = S; } /*}}}*/ +// Configuration::Clear - Clear an single value from a list /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void Configuration::Clear(string const &Name, int const &Value) +{ + char S[300]; + snprintf(S,sizeof(S),"%i",Value); + Clear(Name, S); +} + /*}}}*/ +// Configuration::Clear - Clear an single value from a list /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void Configuration::Clear(string const &Name, string const &Value) +{ + Item *Top = Lookup(Name.c_str(),false); + if (Top == 0 || Top->Child == 0) + return; + + Item *Tmp, *Prev, *I; + Prev = I = Top->Child; + + while(I != NULL) + { + if(I->Value == Value) + { + Tmp = I; + // was first element, point parent to new first element + if(Top->Child == Tmp) + Top->Child = I->Next; + I = I->Next; + Prev->Next = I; + delete Tmp; + } else { + Prev = I; + I = I->Next; + } + } + +} + /*}}}*/ +// Configuration::Clear - Clear everything /*{{{*/ +// --------------------------------------------------------------------- +void Configuration::Clear() +{ + const Configuration::Item *Top = Tree(0); + while( Top != 0 ) + { + Clear(Top->FullTag()); + Top = Top->Next; + } +} + /*}}}*/ +// Configuration::Clear - Clear an entire tree /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void Configuration::Clear(string const &Name) +{ + Item *Top = Lookup(Name.c_str(),false); + if (Top == 0) + return; + + Top->Value.clear(); + Item *Stop = Top; + Top = Top->Child; + Stop->Child = 0; + for (; Top != 0;) + { + if (Top->Child != 0) + { + Top = Top->Child; + continue; + } + + while (Top != 0 && Top->Next == 0) + { + Item *Tmp = Top; + Top = Top->Parent; + delete Tmp; + + if (Top == Stop) + return; + } + + Item *Tmp = Top; + if (Top != 0) + Top = Top->Next; + delete Tmp; + } +} + /*}}}*/ // Configuration::Exists - Returns true if the Name exists /*{{{*/ // --------------------------------------------------------------------- /* */ -bool Configuration::Exists(const char *Name) +bool Configuration::Exists(const char *Name) const { - Item *Itm = Lookup(Name,false); + const Item *Itm = Lookup(Name); if (Itm == 0) return false; return true; } /*}}}*/ +// Configuration::ExistsAny - Returns true if the Name, possibly /*{{{*/ +// --------------------------------------------------------------------- +/* qualified by /[fdbi] exists */ +bool Configuration::ExistsAny(const char *Name) const +{ + string key = Name; + + if (key.size() > 2 && key.end()[-2] == '/') + { + if (key.find_first_of("fdbi",key.size()-1) < key.size()) + { + key.resize(key.size() - 2); + if (Exists(key.c_str())) + return true; + } + else + { + _error->Warning(_("Unrecognized type abbreviation: '%c'"), key.end()[-3]); + } + } + return Exists(Name); +} + /*}}}*/ // Configuration::Dump - Dump the config /*{{{*/ // --------------------------------------------------------------------- /* Dump the entire configuration space */ -void Configuration::Dump() +void Configuration::Dump(ostream& str) +{ + Dump(str, NULL, "%f \"%v\";\n", true); +} +void Configuration::Dump(ostream& str, char const * const root, + char const * const formatstr, bool const emptyValue) { - /* Write out all of the configuration directives by walking the + const Configuration::Item* Top = Tree(root); + if (Top == 0) + return; + const Configuration::Item* const Root = (root == NULL) ? NULL : Top; + std::vector const format = VectorizeString(formatstr, '%'); + + /* Write out all of the configuration directives by walking the configuration tree */ - const Configuration::Item *Top = _config->Tree(0); - for (; Top != 0;) - { - clog << Top->FullTag() << " \"" << Top->Value << "\";" << endl; - + do { + if (emptyValue == true || Top->Value.empty() == emptyValue) + { + std::vector::const_iterator f = format.begin(); + str << *f; + for (++f; f != format.end(); ++f) + { + if (f->empty() == true) + { + ++f; + str << '%' << *f; + continue; + } + char const type = (*f)[0]; + if (type == 'f') + str << Top->FullTag(); + else if (type == 't') + str << Top->Tag; + else if (type == 'v') + str << Top->Value; + else if (type == 'F') + str << QuoteString(Top->FullTag(), "=\"\n"); + else if (type == 'T') + str << QuoteString(Top->Tag, "=\"\n"); + else if (type == 'V') + str << QuoteString(Top->Value, "=\"\n"); + else if (type == 'n') + str << "\n"; + else if (type == 'N') + str << "\t"; + else + str << '%' << type; + str << f->c_str() + 1; + } + } + if (Top->Child != 0) { Top = Top->Child; continue; } - + while (Top != 0 && Top->Next == 0) Top = Top->Parent; if (Top != 0) Top = Top->Next; - } + + if (Root != NULL) + { + const Configuration::Item* I = Top; + while(I != 0) + { + if (I == Root) + break; + else + I = I->Parent; + } + if (I == 0) + break; + } + } while (Top != 0); } /*}}}*/ // Configuration::Item::FullTag - Return the fully scoped tag /*{{{*/ // --------------------------------------------------------------------- -/* */ -string Configuration::Item::FullTag() const +/* Stop sets an optional max recursion depth if this item is being viewed as + part of a sub tree. */ +string Configuration::Item::FullTag(const Item *Stop) const { - if (Parent == 0 || Parent->Parent == 0) + if (Parent == 0 || Parent->Parent == 0 || Parent == Stop) return Tag; - return Parent->FullTag() + "::" + Tag; + return Parent->FullTag(Stop) + "::" + Tag; } /*}}}*/ // ReadConfigFile - Read a configuration file /*{{{*/ // --------------------------------------------------------------------- /* The configuration format is very much like the named.conf format - used in bind8, in fact this routine can parse most named.conf files. */ -bool ReadConfigFile(Configuration &Conf,string FName) -{ + used in bind8, in fact this routine can parse most named.conf files. + Sectional config files are like bind's named.conf where there are + sections like 'zone "foo.org" { .. };' This causes each section to be + added in with a tag like "zone::foo.org" instead of being split + tag/value. AsSectional enables Sectional parsing.*/ +bool ReadConfigFile(Configuration &Conf,const string &FName,bool const &AsSectional, + unsigned const &Depth) +{ // Open the stream for reading - ifstream F(FName.c_str(),ios::in | ios::nocreate); + ifstream F(FName.c_str(),ios::in); if (!F != 0) - return _error->Errno("ifstream::ifstream","Opening configuration file %s",FName.c_str()); - - char Buffer[300]; + return _error->Errno("ifstream::ifstream",_("Opening configuration file %s"),FName.c_str()); + string LineBuffer; + string Stack[100]; + unsigned int StackPos = 0; // Parser state string ParentTag; @@ -284,19 +627,64 @@ bool ReadConfigFile(Configuration &Conf,string FName) bool InComment = false; while (F.eof() == false) { - F.getline(Buffer,sizeof(Buffer)); + // The raw input line. + std::string Input; + // The input line with comments stripped. + std::string Fragment; + + // Grab the next line of F and place it in Input. + do + { + char *Buffer = new char[1024]; + + F.clear(); + F.getline(Buffer,sizeof(Buffer) / 2); + + Input += Buffer; + delete[] Buffer; + } + while (F.fail() && !F.eof()); + + // Expand tabs in the input line and remove leading and trailing + // whitespace. + { + const int BufferSize = Input.size() * 8 + 1; + char *Buffer = new char[BufferSize]; + try + { + memcpy(Buffer, Input.c_str(), Input.size() + 1); + + _strtabexpand(Buffer, BufferSize); + _strstrip(Buffer); + Input = Buffer; + } + catch(...) + { + delete[] Buffer; + throw; + } + delete[] Buffer; + } CurLine++; - _strtabexpand(Buffer,sizeof(Buffer)); - _strstrip(Buffer); + + // Now strip comments; if the whole line is contained in a + // comment, skip this line. + + // The first meaningful character in the current fragment; will + // be adjusted below as we remove bytes from the front. + std::string::const_iterator Start = Input.begin(); + // The last meaningful character in the current fragment. + std::string::const_iterator End = Input.end(); // Multi line comment if (InComment == true) { - for (const char *I = Buffer; *I != 0; I++) + for (std::string::const_iterator I = Start; + I != End; ++I) { - if (*I == '*' && I[1] == '/') + if (*I == '*' && I + 1 != End && I[1] == '/') { - memmove(Buffer,I+2,strlen(I+2) + 1); + Start = I + 2; InComment = false; break; } @@ -307,114 +695,151 @@ bool ReadConfigFile(Configuration &Conf,string FName) // Discard single line comments bool InQuote = false; - for (char *I = Buffer; *I != 0; I++) + for (std::string::const_iterator I = Start; + I != End; ++I) { if (*I == '"') InQuote = !InQuote; if (InQuote == true) continue; - - if (*I == '/' && I[1] == '/') - { - *I = 0; + + if ((*I == '/' && I + 1 != End && I[1] == '/') || + (*I == '#' && strcmp(string(I,I+6).c_str(),"#clear") != 0 && + strcmp(string(I,I+8).c_str(),"#include") != 0)) + { + End = I; break; } } - - // Look for multi line comments - for (char *I = Buffer; *I != 0; I++) + + // Look for multi line comments and build up the + // fragment. + Fragment.reserve(End - Start); + InQuote = false; + for (std::string::const_iterator I = Start; + I != End; ++I) { if (*I == '"') InQuote = !InQuote; if (InQuote == true) - continue; - - if (*I == '/' && I[1] == '*') + Fragment.push_back(*I); + else if (*I == '/' && I + 1 != End && I[1] == '*') { InComment = true; - for (char *J = Buffer; *J != 0; J++) + for (std::string::const_iterator J = I; + J != End; ++J) { - if (*J == '*' && J[1] == '/') + if (*J == '*' && J + 1 != End && J[1] == '/') { - memmove(I,J+2,strlen(J+2) + 1); + // Pretend we just finished walking over the + // comment, and don't add anything to the output + // fragment. + I = J + 1; InComment = false; break; } } if (InComment == true) - { - *I = 0; - break; - } + break; } + else + Fragment.push_back(*I); } - - // Blank - if (Buffer[0] == 0) + + // Skip blank lines. + if (Fragment.empty()) continue; - // We now have a valid line fragment - for (char *I = Buffer; *I != 0;) + // The line has actual content; interpret what it means. + InQuote = false; + Start = Fragment.begin(); + End = Fragment.end(); + for (std::string::const_iterator I = Start; + I != End; ++I) { - if (*I == '{' || *I == ';' || *I == '}') + if (*I == '"') + InQuote = !InQuote; + + if (InQuote == false && (*I == '{' || *I == ';' || *I == '}')) { - // Put the last fragement into the buffer - char *Start = Buffer; - char *Stop = I; - for (; Start != I && isspace(*Start) != 0; Start++); - for (; Stop != Start && isspace(Stop[-1]) != 0; Stop--); - if (LineBuffer.empty() == false && Stop - Start != 0) + // Put the last fragment into the buffer + std::string::const_iterator NonWhitespaceStart = Start; + std::string::const_iterator NonWhitespaceStop = I; + for (; NonWhitespaceStart != I && isspace(*NonWhitespaceStart) != 0; ++NonWhitespaceStart) + ; + for (; NonWhitespaceStop != NonWhitespaceStart && isspace(NonWhitespaceStop[-1]) != 0; --NonWhitespaceStop) + ; + if (LineBuffer.empty() == false && NonWhitespaceStop - NonWhitespaceStart != 0) LineBuffer += ' '; - LineBuffer += string(Start,Stop - Start); - - // Remove the fragment - char TermChar = *I; - memmove(Buffer,I + 1,strlen(I + 1) + 1); - I = Buffer; + LineBuffer += string(NonWhitespaceStart, NonWhitespaceStop); - // Move up a tag - if (TermChar == '}') - { - string::size_type Pos = ParentTag.rfind("::"); - if (Pos == string::npos) - ParentTag = string(); - else - ParentTag = string(ParentTag,0,Pos); - } + // Drop this from the input string, saving the character + // that terminated the construct we just closed. (i.e., a + // brace or a semicolon) + char TermChar = *I; + Start = I + 1; // Syntax Error if (TermChar == '{' && LineBuffer.empty() == true) - return _error->Error("Syntax error %s:%u: Block starts with no name.",FName.c_str(),CurLine); + return _error->Error(_("Syntax error %s:%u: Block starts with no name."),FName.c_str(),CurLine); + // No string on this line if (LineBuffer.empty() == true) + { + if (TermChar == '}') + { + if (StackPos == 0) + ParentTag = string(); + else + ParentTag = Stack[--StackPos]; + } continue; - + } + // Parse off the tag string Tag; const char *Pos = LineBuffer.c_str(); if (ParseQuoteWord(Pos,Tag) == false) - return _error->Error("Syntax error %s:%u: Malformed Tag",FName.c_str(),CurLine); - - // Go down a level - if (TermChar == '{') - { - if (ParentTag.empty() == true) - ParentTag = Tag; - else - ParentTag += string("::") + Tag; - Tag = string(); - } + return _error->Error(_("Syntax error %s:%u: Malformed tag"),FName.c_str(),CurLine); // Parse off the word string Word; - if (ParseCWord(Pos,Word) == false) + bool NoWord = false; + if (ParseCWord(Pos,Word) == false && + ParseQuoteWord(Pos,Word) == false) { if (TermChar != '{') { Word = Tag; Tag = ""; - } + } + else + NoWord = true; + } + if (strlen(Pos) != 0) + return _error->Error(_("Syntax error %s:%u: Extra junk after value"),FName.c_str(),CurLine); + + // Go down a level + if (TermChar == '{') + { + if (StackPos <= 100) + Stack[StackPos++] = ParentTag; + + /* Make sectional tags incorperate the section into the + tag string */ + if (AsSectional == true && Word.empty() == false) + { + Tag += "::" ; + Tag += Word; + Word = ""; + } + + if (ParentTag.empty() == true) + ParentTag = Tag; + else + ParentTag += string("::") + Tag; + Tag = string(); } // Generate the item name @@ -429,23 +854,151 @@ bool ReadConfigFile(Configuration &Conf,string FName) Item = ParentTag; } - // Set the item in the configuration class - Conf.Set(Item,Word); - + // Specials + if (Tag.length() >= 1 && Tag[0] == '#') + { + if (ParentTag.empty() == false) + return _error->Error(_("Syntax error %s:%u: Directives can only be done at the top level"),FName.c_str(),CurLine); + Tag.erase(Tag.begin()); + if (Tag == "clear") + Conf.Clear(Word); + else if (Tag == "include") + { + if (Depth > 10) + return _error->Error(_("Syntax error %s:%u: Too many nested includes"),FName.c_str(),CurLine); + if (Word.length() > 2 && Word.end()[-1] == '/') + { + if (ReadConfigDir(Conf,Word,AsSectional,Depth+1) == false) + return _error->Error(_("Syntax error %s:%u: Included from here"),FName.c_str(),CurLine); + } + else + { + if (ReadConfigFile(Conf,Word,AsSectional,Depth+1) == false) + return _error->Error(_("Syntax error %s:%u: Included from here"),FName.c_str(),CurLine); + } + } + else + return _error->Error(_("Syntax error %s:%u: Unsupported directive '%s'"),FName.c_str(),CurLine,Tag.c_str()); + } + else if (Tag.empty() == true && NoWord == false && Word == "#clear") + return _error->Error(_("Syntax error %s:%u: clear directive requires an option tree as argument"),FName.c_str(),CurLine); + else + { + // Set the item in the configuration class + if (NoWord == false) + Conf.Set(Item,Word); + } + // Empty the buffer - LineBuffer = string(); + LineBuffer.clear(); + + // Move up a tag, but only if there is no bit to parse + if (TermChar == '}') + { + if (StackPos == 0) + ParentTag.clear(); + else + ParentTag = Stack[--StackPos]; + } + } - else - I++; } - // Store the fragment - const char *Stripd = _strstrip(Buffer); - if (*Stripd != 0 && LineBuffer.empty() == false) - LineBuffer += " "; - LineBuffer += Stripd; + // Store the remaining text, if any, in the current line buffer. + + // NB: could change this to use string-based operations; I'm + // using strstrip now to ensure backwards compatibility. + // -- dburrows 2008-04-01 + { + char *Buffer = new char[End - Start + 1]; + try + { + std::copy(Start, End, Buffer); + Buffer[End - Start] = '\0'; + + const char *Stripd = _strstrip(Buffer); + if (*Stripd != 0 && LineBuffer.empty() == false) + LineBuffer += " "; + LineBuffer += Stripd; + } + catch(...) + { + delete[] Buffer; + throw; + } + delete[] Buffer; + } } - + + if (LineBuffer.empty() == false) + return _error->Error(_("Syntax error %s:%u: Extra junk at end of file"),FName.c_str(),CurLine); return true; } /*}}}*/ +// ReadConfigDir - Read a directory of config files /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool ReadConfigDir(Configuration &Conf,const string &Dir, + bool const &AsSectional, unsigned const &Depth) +{ + vector const List = GetListOfFilesInDir(Dir, "conf", true, true); + + // Read the files + for (vector::const_iterator I = List.begin(); I != List.end(); ++I) + if (ReadConfigFile(Conf,*I,AsSectional,Depth) == false) + return false; + return true; +} + /*}}}*/ +// MatchAgainstConfig Constructor /*{{{*/ +Configuration::MatchAgainstConfig::MatchAgainstConfig(char const * Config) +{ + std::vector const strings = _config->FindVector(Config); + for (std::vector::const_iterator s = strings.begin(); + s != strings.end(); ++s) + { + regex_t *p = new regex_t; + if (regcomp(p, s->c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB) == 0) + patterns.push_back(p); + else + { + regfree(p); + delete p; + _error->Warning("Invalid regular expression '%s' in configuration " + "option '%s' will be ignored.", + s->c_str(), Config); + continue; + } + } + if (strings.empty() == true) + patterns.push_back(NULL); +} + /*}}}*/ +// MatchAgainstConfig Destructor /*{{{*/ +Configuration::MatchAgainstConfig::~MatchAgainstConfig() +{ + clearPatterns(); +} +void Configuration::MatchAgainstConfig::clearPatterns() +{ + for(std::vector::const_iterator p = patterns.begin(); + p != patterns.end(); ++p) + { + if (*p == NULL) continue; + regfree(*p); + delete *p; + } + patterns.clear(); +} + /*}}}*/ +// MatchAgainstConfig::Match - returns true if a pattern matches /*{{{*/ +bool Configuration::MatchAgainstConfig::Match(char const * str) const +{ + for(std::vector::const_iterator p = patterns.begin(); + p != patterns.end(); ++p) + if (*p != NULL && regexec(*p, str, 0, 0, 0) == 0) + return true; + + return false; +} + /*}}}*/