diff options
author | David Kalnischkies <david@kalnischkies.de> | 2016-12-31 18:24:12 +0100 |
---|---|---|
committer | David Kalnischkies <david@kalnischkies.de> | 2016-12-31 18:24:12 +0100 |
commit | ae73a2944a89e0d2406a2aab4a4c082e1e9da3f9 (patch) | |
tree | 5aad3ec8d399efb68b6a83133e7747e20c540616 /apt-pkg/contrib | |
parent | 4e18c2cee6da39982cc463cafbf27eab5561099f (diff) |
allow warning generation for non-whitelisted options
The idea is simple: Each¹ Find*( call starts with a call check if the
given option (with the requested type) exists in the whitelist. The
whitelist is specified via our configure-index file so that we have
a better chance at keeping it current. the whitelist is loaded via a
special (undocumented for now) configuration stanza and if none is
loaded the empty whitelist will make it so that no warnings are shown.
Much needs to be done still, but that is as good a time as any to take a
snapshot of the current state and release it into the wild given that it
found some bugs already and has no practical effect on users.
¹ not all in this iteration, but many
Diffstat (limited to 'apt-pkg/contrib')
-rw-r--r-- | apt-pkg/contrib/configuration.cc | 161 |
1 files changed, 160 insertions, 1 deletions
diff --git a/apt-pkg/contrib/configuration.cc b/apt-pkg/contrib/configuration.cc index 9007bf9ec..13d678539 100644 --- a/apt-pkg/contrib/configuration.cc +++ b/apt-pkg/contrib/configuration.cc @@ -31,10 +31,13 @@ #include <string.h> #include <algorithm> +#include <iterator> #include <string> #include <stack> #include <vector> #include <fstream> +#include <sstream> +#include <unordered_map> #include <apti18n.h> @@ -43,6 +46,150 @@ using namespace std; Configuration *_config = new Configuration; +/* TODO: This config verification shouldn't be using a static variable + but a Cnf-member – but that would need ABI breaks and stuff and for now + that really is an apt-dev-only tool, so it isn't that bad that it is + unusable and allaround a bit strange */ +enum class APT_HIDDEN ConfigType { UNDEFINED, INT, BOOL, STRING, STRING_OR_BOOL, STRING_OR_LIST, FILE, DIR, LIST, PROGRAM_PATH = FILE }; +APT_HIDDEN std::unordered_map<std::string, ConfigType> apt_known_config {}; +static std::string getConfigTypeString(ConfigType const type) /*{{{*/ +{ + switch (type) + { + case ConfigType::UNDEFINED: return "UNDEFINED"; + case ConfigType::INT: return "INT"; + case ConfigType::BOOL: return "BOOL"; + case ConfigType::STRING: return "STRING"; + case ConfigType::STRING_OR_BOOL: return "STRING_OR_BOOL"; + case ConfigType::FILE: return "FILE"; + case ConfigType::DIR: return "DIR"; + case ConfigType::LIST: return "LIST"; + case ConfigType::STRING_OR_LIST: return "STRING_OR_LIST"; + } + return "UNKNOWN"; +} + /*}}}*/ +static ConfigType getConfigType(std::string const &type) /*{{{*/ +{ + if (type == "<INT>") + return ConfigType::INT; + else if (type == "<BOOL>") + return ConfigType::BOOL; + else if (type == "<STRING>") + return ConfigType::STRING; + else if (type == "<STRING_OR_BOOL>") + return ConfigType::STRING_OR_BOOL; + else if (type == "<FILE>") + return ConfigType::FILE; + else if (type == "<DIR>") + return ConfigType::DIR; + else if (type == "<LIST>") + return ConfigType::LIST; + else if (type == "<STRING_OR_LIST>") + return ConfigType::STRING_OR_LIST; + else if (type == "<PROGRAM_PATH>") + return ConfigType::PROGRAM_PATH; + return ConfigType::UNDEFINED; +} + /*}}}*/ +static void checkFindConfigOptionType(std::string name, ConfigType const type)/*{{{*/ +{ + if (apt_known_config.empty()) + return; + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + auto known = apt_known_config.find(name); + if (known == apt_known_config.cend()) + { + auto const rcolon = name.rfind(':'); + if (rcolon != std::string::npos) + { + known = apt_known_config.find(name.substr(0, rcolon) + ":*"); + if (known == apt_known_config.cend()) + { + auto const parts = StringSplit(name, "::"); + size_t psize = parts.size(); + if (psize > 1) + { + for (size_t max = psize; max != 1; --max) + { + std::ostringstream os; + std::copy(parts.begin(), parts.begin() + max, std::ostream_iterator<std::string>(os, "::")); + os << "**"; + known = apt_known_config.find(os.str()); + if (known != apt_known_config.cend() && known->second == ConfigType::UNDEFINED) + return; + } + for (size_t max = psize - 1; max != 1; --max) + { + std::ostringstream os; + std::copy(parts.begin(), parts.begin() + max - 1, std::ostream_iterator<std::string>(os, "::")); + os << "*::"; + std::copy(parts.begin() + max + 1, parts.end() - 1, std::ostream_iterator<std::string>(os, "::")); + os << *(parts.end() - 1); + known = apt_known_config.find(os.str()); + if (known != apt_known_config.cend()) + break; + } + } + } + } + } + if (known == apt_known_config.cend()) + _error->Warning("Using unknown config option »%s« of type %s", + name.c_str(), getConfigTypeString(type).c_str()); + else if (known->second != type) + { + if (known->second == ConfigType::DIR && type == ConfigType::FILE) + ; // implementation detail + else if (type == ConfigType::STRING && (known->second == ConfigType::FILE || known->second == ConfigType::DIR)) + ; // TODO: that might be an error or not, we will figure this out later + else if (known->second == ConfigType::STRING_OR_BOOL && (type == ConfigType::BOOL || type == ConfigType::STRING)) + ; + else if (known->second == ConfigType::STRING_OR_LIST && (type == ConfigType::LIST || type == ConfigType::STRING)) + ; + else + _error->Warning("Using config option »%s« of type %s as a type %s", + name.c_str(), getConfigTypeString(known->second).c_str(), getConfigTypeString(type).c_str()); + } +} + /*}}}*/ +static bool LoadConfigurationIndex(std::string const &filename) /*{{{*/ +{ + apt_known_config.clear(); + if (filename.empty()) + return true; + Configuration Idx; + if (ReadConfigFile(Idx, filename) == false) + return false; + + Configuration::Item const * Top = Idx.Tree(nullptr); + if (unlikely(Top == nullptr)) + return false; + + do { + if (Top->Value.empty() == false) + { + std::string fulltag = Top->FullTag(); + std::transform(fulltag.begin(), fulltag.end(), fulltag.begin(), ::tolower); + apt_known_config.emplace(std::move(fulltag), getConfigType(Top->Value)); + } + + if (Top->Child != nullptr) + { + Top = Top->Child; + continue; + } + + while (Top != nullptr && Top->Next == nullptr) + Top = Top->Parent; + if (Top != nullptr) + Top = Top->Next; + } while (Top != nullptr); + + return true; +} + /*}}}*/ + // Configuration::Configuration - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -160,6 +307,7 @@ Configuration::Item *Configuration::Lookup(const char *Name,bool const &Create) /* */ string Configuration::Find(const char *Name,const char *Default) const { + checkFindConfigOptionType(Name, ConfigType::STRING); const Item *Itm = Lookup(Name); if (Itm == 0 || Itm->Value.empty() == true) { @@ -179,6 +327,7 @@ string Configuration::Find(const char *Name,const char *Default) const */ string Configuration::FindFile(const char *Name,const char *Default) const { + checkFindConfigOptionType(Name, ConfigType::FILE); const Item *RootItem = Lookup("RootDir"); std::string result = (RootItem == 0) ? "" : RootItem->Value; if(result.empty() == false && result[result.size() - 1] != '/') @@ -233,6 +382,7 @@ string Configuration::FindFile(const char *Name,const char *Default) const /* This is like findfile execept the result is terminated in a / */ string Configuration::FindDir(const char *Name,const char *Default) const { + checkFindConfigOptionType(Name, ConfigType::DIR); string Res = FindFile(Name,Default); if (Res.end()[-1] != '/') { @@ -249,6 +399,7 @@ string Configuration::FindDir(const char *Name,const char *Default) const /* Returns a vector of config values under the given item */ vector<string> Configuration::FindVector(const char *Name, std::string const &Default, bool const Keys) const { + checkFindConfigOptionType(Name, ConfigType::LIST); vector<string> Vec; const Item *Top = Lookup(Name); if (Top == NULL) @@ -274,6 +425,7 @@ vector<string> Configuration::FindVector(const char *Name, std::string const &De /* */ int Configuration::FindI(const char *Name,int const &Default) const { + checkFindConfigOptionType(Name, ConfigType::INT); const Item *Itm = Lookup(Name); if (Itm == 0 || Itm->Value.empty() == true) return Default; @@ -291,6 +443,7 @@ int Configuration::FindI(const char *Name,int const &Default) const /* */ bool Configuration::FindB(const char *Name,bool const &Default) const { + checkFindConfigOptionType(Name, ConfigType::BOOL); const Item *Itm = Lookup(Name); if (Itm == 0 || Itm->Value.empty() == true) return Default; @@ -774,7 +927,8 @@ bool ReadConfigFile(Configuration &Conf,const string &FName,bool const &AsSectio 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)) + strcmp(string(I,I+8).c_str(),"#include") != 0 && + strcmp(string(I,I+strlen("#x-apt-configure-index")).c_str(), "#x-apt-configure-index") != 0)) { End = I; break; @@ -939,6 +1093,11 @@ bool ReadConfigFile(Configuration &Conf,const string &FName,bool const &AsSectio return _error->Error(_("Syntax error %s:%u: Included from here"),FName.c_str(),CurLine); } } + else if (Tag == "x-apt-configure-index") + { + if (LoadConfigurationIndex(Word) == false) + return _error->Warning("Loading the configure index %s in file %s:%u failed!", Word.c_str(), FName.c_str(), CurLine); + } else return _error->Error(_("Syntax error %s:%u: Unsupported directive '%s'"),FName.c_str(),CurLine,Tag.c_str()); } |