X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/5e14c452578c536077a994e3a3c1cc4829ba6637..3184b4cf2e8e2009ce62b8f66c666ae7da67e378:/apt-pkg/contrib/configuration.cc diff --git a/apt-pkg/contrib/configuration.cc b/apt-pkg/contrib/configuration.cc index 740aa8137..81cc87d15 100644 --- a/apt-pkg/contrib/configuration.cc +++ b/apt-pkg/contrib/configuration.cc @@ -15,9 +15,6 @@ ##################################################################### */ /*}}}*/ // Include files /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/configuration.h" -#endif #include #include #include @@ -25,14 +22,8 @@ #include #include -#include #include #include - -#include -#include -#include -#include using namespace std; /*}}}*/ @@ -88,7 +79,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; @@ -110,7 +101,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; @@ -121,7 +112,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; @@ -161,7 +152,7 @@ string Configuration::Find(const char *Name,const char *Default) const if (Itm == 0 || Itm->Value.empty() == true) { if (Default == 0) - return string(); + return ""; else return Default; } @@ -176,13 +167,18 @@ string Configuration::Find(const char *Name,const char *Default) const */ 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('/'); + const Item *Itm = Lookup(Name); if (Itm == 0 || Itm->Value.empty() == true) { if (Default == 0) - return string(); + return rootDir; else - return Default; + return rootDir + Default; } string val = Itm->Value; @@ -207,7 +203,7 @@ string Configuration::FindFile(const char *Name,const char *Default) const Itm = Itm->Parent; } - return val; + return rootDir + val; } /*}}}*/ // Configuration::FindDir - Find a directory name /*{{{*/ @@ -221,10 +217,29 @@ string Configuration::FindDir(const char *Name,const char *Default) const 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) const +int Configuration::FindI(const char *Name,int const &Default) const { const Item *Itm = Lookup(Name); if (Itm == 0 || Itm->Value.empty() == true) @@ -241,7 +256,7 @@ int Configuration::FindI(const char *Name,int Default) const // Configuration::FindB - Find a boolean type /*{{{*/ // --------------------------------------------------------------------- /* */ -bool Configuration::FindB(const char *Name,bool Default) const +bool Configuration::FindB(const char *Name,bool const &Default) const { const Item *Itm = Lookup(Name); if (Itm == 0 || Itm->Value.empty() == true) @@ -294,7 +309,7 @@ string Configuration::FindAny(const char *Name,const char *Default) const // Configuration::CndSet - Conditinal Set a value /*{{{*/ // --------------------------------------------------------------------- /* This will not overwrite */ -void Configuration::CndSet(const char *Name,string Value) +void Configuration::CndSet(const char *Name,const string &Value) { Item *Itm = Lookup(Name,true); if (Itm == 0) @@ -306,7 +321,7 @@ void Configuration::CndSet(const char *Name,string Value) // 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) @@ -317,7 +332,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) @@ -325,18 +340,59 @@ void Configuration::Set(const char *Name,int Value) char S[300]; snprintf(S,sizeof(S),"%i",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 an entire tree /*{{{*/ // --------------------------------------------------------------------- /* */ -void Configuration::Clear(string Name) +void Configuration::Clear(string const &Name) { Item *Top = Lookup(Name.c_str(),false); - if (Top == 0) + if (Top == 0) return; - - Top->Value = string(); + + Top->Value.clear(); Item *Stop = Top; Top = Top->Child; Stop->Child = 0; @@ -384,6 +440,7 @@ 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); @@ -394,7 +451,7 @@ bool Configuration::ExistsAny(const char *Name) const { _error->Warning(_("Unrecognized type abbreviation: '%c'"), key.end()[-3]); } - + } return Exists(Name); } /*}}}*/ @@ -444,15 +501,14 @@ string Configuration::Item::FullTag(const Item *Stop) const 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,string FName,bool AsSectional, - unsigned Depth) +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) return _error->Errno("ifstream::ifstream",_("Opening configuration file %s"),FName.c_str()); - - char Buffer[1024]; + string LineBuffer; string Stack[100]; unsigned int StackPos = 0; @@ -464,23 +520,64 @@ bool ReadConfigFile(Configuration &Conf,string FName,bool AsSectional, 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++; - // This should be made to work instead, but this is better than looping - if (F.fail() && !F.eof()) - return _error->Error(_("Line %d too long (max %d)"), CurLine, sizeof(Buffer)); - _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; } @@ -491,57 +588,68 @@ bool ReadConfigFile(Configuration &Conf,string FName,bool AsSectional, // 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 + // Look for multi line comments and build up the + // fragment. + Fragment.reserve(End - Start); 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] == '*') + 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 + // The line has actual content; interpret what it means. InQuote = false; - for (char *I = Buffer; *I != 0;) + Start = Fragment.begin(); + End = Fragment.end(); + for (std::string::const_iterator I = Start; + I != End; ++I) { if (*I == '"') InQuote = !InQuote; @@ -549,18 +657,21 @@ bool ReadConfigFile(Configuration &Conf,string FName,bool AsSectional, if (InQuote == false && (*I == '{' || *I == ';' || *I == '}')) { // Put the last fragment 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) + 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 + LineBuffer += string(NonWhitespaceStart, NonWhitespaceStop); + + // 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; - memmove(Buffer,I + 1,strlen(I + 1) + 1); - I = Buffer; + Start = I + 1; // Syntax Error if (TermChar == '{' && LineBuffer.empty() == true) @@ -583,7 +694,7 @@ bool ReadConfigFile(Configuration &Conf,string FName,bool AsSectional, 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); + return _error->Error(_("Syntax error %s:%u: Malformed tag"),FName.c_str(),CurLine); // Parse off the word string Word; @@ -662,6 +773,8 @@ bool ReadConfigFile(Configuration &Conf,string FName,bool AsSectional, 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 @@ -670,27 +783,44 @@ bool ReadConfigFile(Configuration &Conf,string FName,bool AsSectional, } // 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 = string(); + 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) @@ -701,39 +831,10 @@ bool ReadConfigFile(Configuration &Conf,string FName,bool AsSectional, // ReadConfigDir - Read a directory of config files /*{{{*/ // --------------------------------------------------------------------- /* */ -bool ReadConfigDir(Configuration &Conf,string Dir,bool AsSectional, - unsigned Depth) -{ - DIR *D = opendir(Dir.c_str()); - if (D == 0) - return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str()); - - vector List; - - for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D)) - { - if (Ent->d_name[0] == '.') - continue; - - // Skip bad file names ala run-parts - const char *C = Ent->d_name; - for (; *C != 0; C++) - if (isalpha(*C) == 0 && isdigit(*C) == 0 && *C != '_' && *C != '-') - break; - if (*C != 0) - continue; - - // Make sure it is a file and not something else - string File = flCombine(Dir,Ent->d_name); - struct stat St; - if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0) - continue; - - List.push_back(File); - } - closedir(D); - - sort(List.begin(),List.end()); +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++) @@ -742,3 +843,46 @@ bool ReadConfigDir(Configuration &Conf,string Dir,bool AsSectional, 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("Regex compilation error for '%s' in configuration option '%s'", + s->c_str(), Config); + } + } + +} + /*}}}*/ +// MatchAgainstConfig Destructor /*{{{*/ +Configuration::MatchAgainstConfig::~MatchAgainstConfig() +{ + for(std::vector::const_iterator p = patterns.begin(); + p != patterns.end(); ++p) + { + regfree(*p); + delete *p; + } +} + /*}}}*/ +// 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 (regexec(*p, str, 0, 0, 0) == 0) + return true; + + return false; +} + /*}}}*/