##################################################################### */
/*}}}*/
// Include files /*{{{*/
+#include <config.h>
+
#include <apt-pkg/configuration.h>
#include <apt-pkg/error.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/fileutl.h>
-#include <apti18n.h>
+#include <apt-pkg/macros.h>
+
+#include <ctype.h>
+#include <regex.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <algorithm>
+#include <string>
+#include <stack>
#include <vector>
#include <fstream>
-#include <iostream>
+
+#include <apti18n.h>
using namespace std;
/*}}}*/
}
Configuration::Configuration(const Item *Root) : Root((Item *)Root), ToFree(false)
{
-};
-
+}
/*}}}*/
// Configuration::~Configuration - Destructor /*{{{*/
// ---------------------------------------------------------------------
string Configuration::FindFile(const char *Name,const char *Default) const
{
const Item *RootItem = Lookup("RootDir");
- std::string rootDir = (RootItem == 0) ? "" : RootItem->Value;
- if(rootDir.size() > 0 && rootDir[rootDir.size() - 1] != '/')
- rootDir.push_back('/');
+ 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 rootDir;
- else
- return rootDir + Default;
+ if (Default != 0)
+ result.append(Default);
}
-
- string val = Itm->Value;
- while (Itm->Parent != 0 && Itm->Parent->Value.empty() == false)
- {
- // Absolute
- if (val.length() >= 1 && val[0] == '/')
- 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, "/");
+ else
+ {
+ string val = Itm->Value;
+ while (Itm->Parent != 0)
+ {
+ if (Itm->Parent->Value.empty() == true)
+ {
+ Itm = Itm->Parent;
+ continue;
+ }
- val.insert(0, Itm->Parent->Value);
- Itm = Itm->Parent;
- }
+ // 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, "/");
- return rootDir + val;
+ val.insert(0, Itm->Parent->Value);
+ Itm = Itm->Parent;
+ }
+ result.append(val);
+ }
+ return flNormalize(result);
}
/*}}}*/
// Configuration::FindDir - Find a directory name /*{{{*/
{
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<string> Configuration::FindVector(const char *Name) const
+vector<string> Configuration::FindVector(const char *Name, std::string const &Default, bool const Keys) const
{
vector<string> Vec;
const Item *Top = Lookup(Name);
if (Top == NULL)
- return Vec;
+ return VectorizeString(Default, ',');
+
+ if (Top->Value.empty() == false)
+ return VectorizeString(Top->Value, ',');
Item *I = Top->Child;
while(I != NULL)
{
- Vec.push_back(I->Value);
+ Vec.push_back(Keys ? I->Tag : I->Value);
I = I->Next;
}
+ if (Vec.empty() == true)
+ return VectorizeString(Default, ',');
+
return Vec;
}
/*}}}*/
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 /*{{{*/
// ---------------------------------------------------------------------
/* */
}
}
+}
+ /*}}}*/
+// 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::MoveSubTree(char const * const OldRootName, char const * const NewRootName)/*{{{*/
+{
+ // prevent NewRoot being a subtree of OldRoot
+ if (OldRootName == nullptr)
+ return;
+ if (NewRootName != nullptr)
+ {
+ if (strcmp(OldRootName, NewRootName) == 0)
+ return;
+ std::string const oldroot = std::string(OldRootName) + "::";
+ if (strcasestr(NewRootName, oldroot.c_str()) != NULL)
+ return;
+ }
+
+ Item * Top;
+ Item const * const OldRoot = Top = Lookup(OldRootName, false);
+ if (Top == nullptr)
+ return;
+ std::string NewRoot;
+ if (NewRootName != nullptr)
+ NewRoot.append(NewRootName).append("::");
+
+ Top->Value.clear();
+ Item * const 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)
+ {
+ Set(NewRoot + Top->FullTag(OldRoot), Top->Value);
+ Item const * const Tmp = Top;
+ Top = Top->Parent;
+ delete Tmp;
+
+ if (Top == Stop)
+ return;
+ }
+
+ Set(NewRoot + Top->FullTag(OldRoot), Top->Value);
+ Item const * const Tmp = Top;
+ if (Top != 0)
+ Top = Top->Next;
+ delete Tmp;
+ }
+}
+ /*}}}*/
// Configuration::Exists - Returns true if the Name exists /*{{{*/
// ---------------------------------------------------------------------
/* */
/* Dump the entire configuration space */
void Configuration::Dump(ostream& str)
{
- /* Write out all of the configuration directives by walking the
+ Dump(str, NULL, "%f \"%v\";\n", true);
+}
+void Configuration::Dump(ostream& str, char const * const root,
+ char const * const formatstr, bool const emptyValue)
+{
+ const Configuration::Item* Top = Tree(root);
+ if (Top == 0)
+ return;
+ const Configuration::Item* const Root = (root == NULL) ? NULL : Top;
+ std::vector<std::string> const format = VectorizeString(formatstr, '%');
+
+ /* Write out all of the configuration directives by walking the
configuration tree */
- const Configuration::Item *Top = Tree(0);
- for (; Top != 0;)
- {
- str << Top->FullTag() << " \"" << Top->Value << "\";" << endl;
-
+ do {
+ if (emptyValue == true || Top->Value.empty() == emptyValue)
+ {
+ std::vector<std::string>::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);
}
/*}}}*/
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.*/
+static void leaveCurrentScope(std::stack<std::string> &Stack, std::string &ParentTag)
+{
+ if (Stack.empty())
+ ParentTag.clear();
+ else
+ {
+ ParentTag = Stack.top();
+ Stack.pop();
+ }
+}
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);
- if (!F != 0)
+ ifstream F(FName.c_str(),ios::in);
+ if (F.fail() == true)
return _error->Errno("ifstream::ifstream",_("Opening configuration file %s"),FName.c_str());
string LineBuffer;
- string Stack[100];
- unsigned int StackPos = 0;
-
+ std::stack<std::string> Stack;
+
// Parser state
string ParentTag;
-
+
int CurLine = 0;
bool InComment = false;
while (F.eof() == false)
Start = I + 2;
InComment = false;
break;
- }
+ }
}
if (InComment == true)
continue;
}
-
+
// Discard single line comments
bool InQuote = false;
for (std::string::const_iterator I = Start;
I = J + 1;
InComment = false;
break;
- }
+ }
}
-
+
if (InComment == true)
break;
}
// Skip blank lines.
if (Fragment.empty())
continue;
-
+
// The line has actual content; interpret what it means.
InQuote = false;
Start = Fragment.begin();
{
if (*I == '"')
InQuote = !InQuote;
-
+
if (InQuote == false && (*I == '{' || *I == ';' || *I == '}'))
{
// 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 (; NonWhitespaceStart != I && isspace(*NonWhitespaceStart) != 0; ++NonWhitespaceStart)
;
- for (; NonWhitespaceStop != NonWhitespaceStart && isspace(NonWhitespaceStop[-1]) != 0; NonWhitespaceStop--)
+ for (; NonWhitespaceStop != NonWhitespaceStart && isspace(NonWhitespaceStop[-1]) != 0; --NonWhitespaceStop)
;
if (LineBuffer.empty() == false && NonWhitespaceStop - NonWhitespaceStart != 0)
LineBuffer += ' ';
// 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);
-
+
// No string on this line
if (LineBuffer.empty() == true)
{
if (TermChar == '}')
- {
- if (StackPos == 0)
- ParentTag = string();
- else
- ParentTag = Stack[--StackPos];
- }
+ leaveCurrentScope(Stack, ParentTag);
continue;
}
-
+
// Parse off the tag
string Tag;
const char *Pos = LineBuffer.c_str();
// Go down a level
if (TermChar == '{')
{
- if (StackPos <= 100)
- Stack[StackPos++] = ParentTag;
-
+ Stack.push(ParentTag);
+
/* Make sectional tags incorperate the section into the
tag string */
if (AsSectional == true && Word.empty() == false)
{
- Tag += "::" ;
- Tag += Word;
- Word = "";
+ Tag.append("::").append(Word);
+ Word.clear();
}
-
+
if (ParentTag.empty() == true)
ParentTag = Tag;
else
- ParentTag += string("::") + Tag;
- Tag = string();
+ ParentTag.append("::").append(Tag);
+ Tag.clear();
}
-
+
// Generate the item name
string Item;
if (ParentTag.empty() == true)
else
Item = ParentTag;
}
-
+
// Specials
if (Tag.length() >= 1 && Tag[0] == '#')
{
{
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.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];
- }
-
+ leaveCurrentScope(Stack, ParentTag);
}
}
// ReadConfigDir - Read a directory of config files /*{{{*/
// ---------------------------------------------------------------------
/* */
-bool ReadConfigDir(Configuration &Conf,const string &Dir,bool const &AsSectional,
- unsigned const &Depth)
+bool ReadConfigDir(Configuration &Conf,const string &Dir,
+ bool const &AsSectional, unsigned const &Depth)
{
- vector<string> const List = GetListOfFilesInDir(Dir, "", true);
+ vector<string> const List = GetListOfFilesInDir(Dir, "conf", true, true);
// Read the files
- for (vector<string>::const_iterator I = List.begin(); I != List.end(); I++)
+ for (vector<string>::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<std::string> const strings = _config->FindVector(Config);
+ for (std::vector<std::string>::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<regex_t *>::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<regex_t *>::const_iterator p = patterns.begin();
+ p != patterns.end(); ++p)
+ if (*p != NULL && regexec(*p, str, 0, 0, 0) == 0)
+ return true;
+
+ return false;
+}
+ /*}}}*/