/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Author : Maxim Mamontov */ #include "config.h" #include "stg/common.h" #include #include #include // strncasecmp using STG::Config; namespace { struct ParserError : public std::runtime_error { ParserError(size_t pos, const std::string& message) : runtime_error("Parsing error at position " + x2str(pos) + ". " + message), position(pos), error(message) {} virtual ~ParserError() throw() {} size_t position; std::string error; }; size_t skipSpaces(const std::string& value, size_t start) { while (start < value.length() && std::isspace(value[start])) ++start; return start; } size_t checkChar(const std::string& value, size_t start, char ch) { if (start >= value.length()) throw ParserError(start, "Unexpected end of string. Expected '" + std::string(1, ch) + "'."); if (value[start] != ch) throw ParserError(start, "Expected '" + std::string(1, ch) + "', got '" + std::string(1, value[start]) + "'."); return start + 1; } std::pair readString(const std::string& value, size_t start) { std::string dest; while (start < value.length() && !std::isspace(value[start]) && value[start] != ',' && value[start] != '(' && value[start] != ')') dest.push_back(value[start++]); if (dest.empty()) { if (start == value.length()) throw ParserError(start, "Unexpected end of string. Expected string."); else throw ParserError(start, "Unexpected whitespace. Expected string."); } return std::make_pair(start, dest); } Config::Pairs toPairs(const std::vector& values) { if (values.empty()) return Config::Pairs(); std::string value(values[0]); Config::Pairs res; size_t start = 0; while (start < value.size()) { Config::Pair pair; start = skipSpaces(value, start); if (!res.empty()) { start = checkChar(value, start, ','); start = skipSpaces(value, start); } size_t pairStart = start; start = checkChar(value, start, '('); const std::pair key = readString(value, start); start = key.first; pair.first = key.second; start = skipSpaces(value, start); start = checkChar(value, start, ','); start = skipSpaces(value, start); const std::pair val = readString(value, start); start = val.first; pair.second = val.second; start = skipSpaces(value, start); start = checkChar(value, start, ')'); if (res.find(pair.first) != res.end()) throw ParserError(pairStart, "Duplicate field."); res.insert(pair); } return res; } bool toBool(const std::vector& values) { if (values.empty()) return false; std::string value(values[0]); return strncasecmp(value.c_str(), "yes", 3) == 0; } std::string toString(const std::vector& values) { if (values.empty()) return ""; return values[0]; } template T toInt(const std::vector& values) { if (values.empty()) return 0; T res = 0; if (str2x(values[0], res) == 0) return res; return 0; } Config::Pairs parseVector(const std::string& paramName, const MODULE_SETTINGS& params) { for (size_t i = 0; i < params.moduleParams.size(); ++i) if (params.moduleParams[i].param == paramName) return toPairs(params.moduleParams[i].value); return Config::Pairs(); } bool parseBool(const std::string& paramName, const MODULE_SETTINGS& params) { for (size_t i = 0; i < params.moduleParams.size(); ++i) if (params.moduleParams[i].param == paramName) return toBool(params.moduleParams[i].value); return false; } std::string parseString(const std::string& paramName, const MODULE_SETTINGS& params) { for (size_t i = 0; i < params.moduleParams.size(); ++i) if (params.moduleParams[i].param == paramName) return toString(params.moduleParams[i].value); return ""; } template T parseInt(const std::string& paramName, const MODULE_SETTINGS& params) { for (size_t i = 0; i < params.moduleParams.size(); ++i) if (params.moduleParams[i].param == paramName) return toInt(params.moduleParams[i].value); return 0; } std::string parseAddress(const std::string& address) { size_t pos = address.find_first_of(':'); if (pos == std::string::npos) throw ParserError(0, "Connection type is not specified. Should be either 'unix' or 'tcp'."); return address.substr(pos + 1); } Config::Type parseConnectionType(const std::string& address) { size_t pos = address.find_first_of(':'); if (pos == std::string::npos) throw ParserError(0, "Connection type is not specified. Should be either 'unix' or 'tcp'."); std::string type = ToLower(address.substr(0, pos)); if (type == "unix") return Config::UNIX; else if (type == "tcp") return Config::TCP; throw ParserError(0, "Invalid connection type. Should be either 'unix' or 'tcp', got '" + type + "'"); } } // namespace anonymous Config::Config(const MODULE_SETTINGS& settings) : match(parseVector("match", settings)), modify(parseVector("modify", settings)), reply(parseVector("reply", settings)), verbose(parseBool("verbose", settings)), address(parseString("bind_address", settings)), bindAddress(parseAddress(address)), connectionType(parseConnectionType(address)), portStr(parseString("port", settings)), port(parseInt("port", settings)), key(parseString("key", settings)) { }