
The name of the object (i.e. the section name from the configuration file), is now stored in the configuration object for that object. That way, more contextual and hence morfe user friendly errors and warnings can be generated.
906 lines
18 KiB
C++
906 lines
18 KiB
C++
/*
|
|
* Copyright (c) 2018 MariaDB Corporation Ab
|
|
*
|
|
* Use of this software is governed by the Business Source License included
|
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
|
*
|
|
* Change Date: 2022-01-01
|
|
*
|
|
* On the date above, in accordance with the Business Source License, use
|
|
* of this software will be governed by version 2 or later of the General
|
|
* Public License.
|
|
*/
|
|
|
|
#include <maxscale/config2.hh>
|
|
#include "internal/config.hh"
|
|
|
|
using namespace std;
|
|
|
|
namespace
|
|
{
|
|
|
|
using namespace config;
|
|
|
|
// TODO: Do not duplicate information from config.cc.
|
|
|
|
const char* pzCore_filter_params[] = {
|
|
CN_TYPE,
|
|
CN_MODULE,
|
|
nullptr,
|
|
};
|
|
|
|
const char* pzCore_monitor_params[] = {
|
|
CN_TYPE,
|
|
CN_MODULE,
|
|
CN_USER,
|
|
CN_PASSWORD,
|
|
CN_SERVERS,
|
|
CN_MONITOR_INTERVAL,
|
|
CN_BACKEND_CONNECT_TIMEOUT,
|
|
CN_BACKEND_READ_TIMEOUT,
|
|
CN_BACKEND_WRITE_TIMEOUT,
|
|
CN_BACKEND_CONNECT_ATTEMPTS,
|
|
CN_JOURNAL_MAX_AGE,
|
|
CN_DISK_SPACE_THRESHOLD,
|
|
CN_DISK_SPACE_CHECK_INTERVAL,
|
|
CN_SCRIPT,
|
|
CN_SCRIPT_TIMEOUT,
|
|
CN_EVENTS,
|
|
nullptr,
|
|
};
|
|
|
|
const char* pzCore_router_params[] =
|
|
{
|
|
CN_AUTH_ALL_SERVERS,
|
|
CN_CONNECTION_TIMEOUT,
|
|
CN_ENABLE_ROOT_USER,
|
|
CN_LOCALHOST_MATCH_WILDCARD_HOST,
|
|
CN_LOG_AUTH_WARNINGS,
|
|
CN_MAX_CONNECTIONS,
|
|
CN_MAX_RETRY_INTERVAL,
|
|
CN_NET_WRITE_TIMEOUT,
|
|
CN_PASSWORD,
|
|
CN_RETAIN_LAST_STATEMENTS,
|
|
CN_RETRY_ON_FAILURE,
|
|
CN_ROUTER,
|
|
CN_SERVERS,
|
|
CN_SESSION_TRACK_TRX_STATE,
|
|
CN_STRIP_DB_ESC,
|
|
CN_TYPE,
|
|
CN_USER,
|
|
nullptr
|
|
};
|
|
|
|
bool is_core_param(Specification::Kind kind, const std::string& param)
|
|
{
|
|
bool rv = false;
|
|
|
|
const char** pzCore_params = nullptr;
|
|
|
|
switch (kind)
|
|
{
|
|
case Specification::FILTER:
|
|
pzCore_params = pzCore_filter_params;
|
|
break;
|
|
|
|
case Specification::MONITOR:
|
|
pzCore_params = pzCore_monitor_params;
|
|
break;
|
|
|
|
case Specification::ROUTER:
|
|
pzCore_params = pzCore_router_params;
|
|
break;
|
|
|
|
default:
|
|
mxb_assert(!true);
|
|
}
|
|
|
|
if (pzCore_params)
|
|
{
|
|
while (!rv && *pzCore_params)
|
|
{
|
|
const char* zCore_param = *pzCore_params;
|
|
|
|
rv = (param == zCore_param);
|
|
++pzCore_params;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
namespace config
|
|
{
|
|
|
|
/**
|
|
* class Specification
|
|
*/
|
|
Specification::Specification(const char* zModule, Kind kind)
|
|
: m_module(zModule)
|
|
, m_kind(kind)
|
|
{
|
|
}
|
|
|
|
Specification::~Specification()
|
|
{
|
|
}
|
|
|
|
const string& Specification::module() const
|
|
{
|
|
return m_module;
|
|
}
|
|
|
|
const Param* Specification::find_param(const string& name) const
|
|
{
|
|
auto it = m_params.find(name);
|
|
|
|
return it != m_params.end() ? it->second : nullptr;
|
|
}
|
|
|
|
ostream& Specification::document(ostream& out) const
|
|
{
|
|
for (const auto& entry : m_params)
|
|
{
|
|
out << entry.second->documentation() << endl;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
bool Specification::validate(const MXS_CONFIG_PARAMETER& params) const
|
|
{
|
|
bool valid = true;
|
|
|
|
set<string> provided;
|
|
|
|
for (const auto& param : params)
|
|
{
|
|
const auto& name = param.first;
|
|
const auto& value = param.second;
|
|
|
|
const Param* pParam = find_param(name.c_str());
|
|
|
|
if (pParam)
|
|
{
|
|
bool param_valid = true;
|
|
string message;
|
|
|
|
if (!pParam->validate(value.c_str(), &message))
|
|
{
|
|
param_valid = false;
|
|
}
|
|
|
|
if (!message.empty())
|
|
{
|
|
if (param_valid)
|
|
{
|
|
MXS_WARNING("%s: %s", name.c_str(), message.c_str());
|
|
}
|
|
else
|
|
{
|
|
MXS_ERROR("%s: %s", name.c_str(), message.c_str());
|
|
}
|
|
}
|
|
|
|
provided.insert(name);
|
|
}
|
|
else if (!is_core_param(m_kind, name))
|
|
{
|
|
MXS_WARNING("%s: The parameter '%s' is unrecognized.", m_module.c_str(), name.c_str());
|
|
valid = false;
|
|
}
|
|
}
|
|
|
|
for (const auto& entry : m_params)
|
|
{
|
|
const Param* pParam = entry.second;
|
|
|
|
if (pParam->is_mandatory() && (provided.find(pParam->name()) == provided.end()))
|
|
{
|
|
MXS_ERROR("%s: The mandatory parameter '%s' is not provided.",
|
|
m_module.c_str(), pParam->name().c_str());
|
|
valid = false;
|
|
}
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
bool Specification::configure(Configuration& configuration, const MXS_CONFIG_PARAMETER& params) const
|
|
{
|
|
mxb_assert(validate(params));
|
|
mxb_assert(size() == configuration.size());
|
|
|
|
bool configured = true;
|
|
|
|
for (const auto& param : params)
|
|
{
|
|
const auto& name = param.first;
|
|
|
|
if (!is_core_param(m_kind, name))
|
|
{
|
|
const auto& value = param.second;
|
|
|
|
const Param* pParam = find_param(name.c_str());
|
|
config::Type* pValue = configuration.find_value(name.c_str());
|
|
|
|
mxb_assert(pValue && pParam); // Should have been validated.
|
|
mxb_assert(&pValue->parameter() == pParam);
|
|
|
|
if (pParam && pValue)
|
|
{
|
|
if (!pParam->set(*pValue, value.c_str()))
|
|
{
|
|
mxb_assert(!true);
|
|
configured = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MXS_ERROR("%s: The parameter '%s' is unrecognized.", m_module.c_str(), name.c_str());
|
|
configured = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (configured)
|
|
{
|
|
configured = configuration.post_configure(params);
|
|
}
|
|
|
|
return configured;
|
|
}
|
|
|
|
void Specification::populate(MXS_MODULE& module) const
|
|
{
|
|
MXS_MODULE_PARAM* pModule_param = &module.parameters[0];
|
|
|
|
for (const auto& entry : m_params)
|
|
{
|
|
const Param* pParam = entry.second;
|
|
|
|
pParam->populate(*pModule_param);
|
|
++pModule_param;
|
|
}
|
|
}
|
|
|
|
size_t Specification::size() const
|
|
{
|
|
return m_params.size();
|
|
}
|
|
|
|
void Specification::insert(Param* pParam)
|
|
{
|
|
mxb_assert(m_params.find(pParam->name()) == m_params.end());
|
|
|
|
m_params.insert(make_pair(pParam->name(), pParam));
|
|
}
|
|
|
|
void Specification::remove(Param* pParam)
|
|
{
|
|
auto it = m_params.find(pParam->name());
|
|
mxb_assert(it != m_params.end());
|
|
|
|
m_params.erase(it);
|
|
}
|
|
|
|
|
|
/**
|
|
* class Param
|
|
*/
|
|
Param::Param(Specification* pSpecification,
|
|
const char* zName,
|
|
const char* zDescription,
|
|
Kind kind,
|
|
mxs_module_param_type legacy_type)
|
|
: m_specification(*pSpecification)
|
|
, m_name(zName)
|
|
, m_description(zDescription)
|
|
, m_kind(kind)
|
|
, m_legacy_type(legacy_type)
|
|
{
|
|
m_specification.insert(this);
|
|
}
|
|
|
|
Param::~Param()
|
|
{
|
|
m_specification.remove(this);
|
|
}
|
|
|
|
const string& Param::name() const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
const string& Param::description() const
|
|
{
|
|
return m_description;
|
|
}
|
|
|
|
std::string Param::documentation() const
|
|
{
|
|
std::stringstream ss;
|
|
|
|
ss << m_name << " (" << type() << ", ";
|
|
|
|
if (is_mandatory())
|
|
{
|
|
ss << "mandatory";
|
|
}
|
|
else
|
|
{
|
|
ss << "optional, default: " << default_to_string();
|
|
}
|
|
|
|
ss << "): " << m_description;
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
Param::Kind Param::kind() const
|
|
{
|
|
return m_kind;
|
|
}
|
|
|
|
bool Param::is_mandatory() const
|
|
{
|
|
return m_kind == MANDATORY;
|
|
}
|
|
|
|
bool Param::is_optional() const
|
|
{
|
|
return m_kind == OPTIONAL;
|
|
}
|
|
|
|
bool Param::has_default_value() const
|
|
{
|
|
return is_optional();
|
|
}
|
|
|
|
void Param::populate(MXS_MODULE_PARAM& param) const
|
|
{
|
|
param.type = m_legacy_type;
|
|
param.name = MXS_STRDUP_A(name().c_str());
|
|
|
|
if (has_default_value())
|
|
{
|
|
string s = default_to_string().c_str();
|
|
|
|
if ((s.length() >= 2) && (s.at(0) == '"') && (s.at(s.length() - 1) == '"'))
|
|
{
|
|
s = s.substr(1, s.length() - 2);
|
|
}
|
|
|
|
param.default_value = MXS_STRDUP_A(s.c_str());
|
|
}
|
|
|
|
if (is_mandatory())
|
|
{
|
|
param.options |= MXS_MODULE_OPT_REQUIRED;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* class Configuration
|
|
*/
|
|
Configuration::Configuration(const std::string& name, const config::Specification* pSpecification)
|
|
: m_name(name)
|
|
, m_specification(*pSpecification)
|
|
{
|
|
}
|
|
|
|
const std::string& Configuration::name() const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
const config::Specification& Configuration::specification() const
|
|
{
|
|
return m_specification;
|
|
}
|
|
|
|
Type* Configuration::find_value(const string& name)
|
|
{
|
|
auto it = m_values.find(name);
|
|
|
|
return it != m_values.end() ? it->second : nullptr;
|
|
}
|
|
|
|
const Type* Configuration::find_value(const string& name) const
|
|
{
|
|
return const_cast<Configuration*>(this)->find_value(name);
|
|
}
|
|
|
|
ostream& Configuration::persist(ostream& out) const
|
|
{
|
|
for (const auto& entry : m_values)
|
|
{
|
|
Type* pValue = entry.second;
|
|
pValue->persist(out) << "\n";
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
void Configuration::insert(Type* pValue)
|
|
{
|
|
mxb_assert(m_values.find(pValue->parameter().name()) == m_values.end());
|
|
|
|
m_values.insert(make_pair(pValue->parameter().name(), pValue));
|
|
}
|
|
|
|
void Configuration::remove(Type* pValue)
|
|
{
|
|
auto it = m_values.find(pValue->parameter().name());
|
|
|
|
mxb_assert(it != m_values.end());
|
|
m_values.erase(it);
|
|
}
|
|
|
|
bool Configuration::post_configure(const MXS_CONFIG_PARAMETER& params)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
size_t Configuration::size() const
|
|
{
|
|
return m_values.size();
|
|
}
|
|
|
|
/**
|
|
* class Type
|
|
*/
|
|
Type::Type(Configuration* pConfiguration, const config::Param* pParam)
|
|
: m_configuration(*pConfiguration)
|
|
, m_param(*pParam)
|
|
{
|
|
m_configuration.insert(this);
|
|
}
|
|
|
|
Type::~Type()
|
|
{
|
|
m_configuration.remove(this);
|
|
}
|
|
|
|
const config::Param& Type::parameter() const
|
|
{
|
|
return m_param;
|
|
}
|
|
|
|
ostream& Type::persist(ostream& out) const
|
|
{
|
|
out << m_param.name() << "=" << to_string();
|
|
return out;
|
|
}
|
|
|
|
bool Type::set(const string& value_as_string)
|
|
{
|
|
return m_param.set(*this, value_as_string);
|
|
}
|
|
|
|
/**
|
|
* ParamBool
|
|
*/
|
|
std::string ParamBool::type() const
|
|
{
|
|
return "boolean";
|
|
}
|
|
|
|
std::string ParamBool::default_to_string() const
|
|
{
|
|
return to_string(m_default_value);
|
|
}
|
|
|
|
bool ParamBool::validate(const std::string& value_as_string, std::string* pMessage) const
|
|
{
|
|
value_type value;
|
|
return from_string(value_as_string, &value, pMessage);
|
|
}
|
|
|
|
bool ParamBool::set(Type& value, const std::string& value_as_string) const
|
|
{
|
|
mxb_assert(&value.parameter() == this);
|
|
|
|
Bool& bool_value = static_cast<Bool&>(value);
|
|
|
|
value_type x;
|
|
bool valid = from_string(value_as_string, &x);
|
|
|
|
if (valid)
|
|
{
|
|
bool_value.set(x);
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
bool ParamBool::from_string(const string& value_as_string, value_type* pValue, string* pMessage) const
|
|
{
|
|
int rv = config_truth_value(value_as_string.c_str());
|
|
|
|
if (rv == 1)
|
|
{
|
|
*pValue = true;
|
|
}
|
|
else if (rv == 0)
|
|
{
|
|
*pValue = false;
|
|
}
|
|
else if (pMessage)
|
|
{
|
|
mxb_assert(rv == -1);
|
|
|
|
*pMessage = "Invalid boolean: ";
|
|
*pMessage += value_as_string;
|
|
}
|
|
|
|
return rv != -1;
|
|
}
|
|
|
|
string ParamBool::to_string(value_type value) const
|
|
{
|
|
return value ? "true" : "false";
|
|
}
|
|
|
|
/**
|
|
* ParamNumber
|
|
*/
|
|
std::string ParamNumber::default_to_string() const
|
|
{
|
|
return to_string(m_default_value);
|
|
}
|
|
|
|
bool ParamNumber::validate(const std::string& value_as_string, std::string* pMessage) const
|
|
{
|
|
value_type value;
|
|
return from_string(value_as_string, &value, pMessage);
|
|
}
|
|
|
|
bool ParamNumber::set(Type& value, const std::string& value_as_string) const
|
|
{
|
|
mxb_assert(&value.parameter() == this);
|
|
|
|
Number& number_value = static_cast<Number&>(value);
|
|
|
|
value_type x;
|
|
bool valid = from_string(value_as_string, &x);
|
|
|
|
if (valid)
|
|
{
|
|
number_value.set(x);
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
bool ParamNumber::from_string(const std::string& value_as_string,
|
|
value_type* pValue,
|
|
std::string* pMessage) const
|
|
{
|
|
const char* zValue = value_as_string.c_str();
|
|
char* zEnd;
|
|
long l = strtol(zValue, &zEnd, 10);
|
|
bool valid = (l >= m_min_value && l <= m_max_value && zEnd != zValue && *zEnd == 0);
|
|
|
|
if (valid)
|
|
{
|
|
*pValue = l;
|
|
}
|
|
else if (pMessage)
|
|
{
|
|
if (!(zEnd != zValue && *zEnd == 0))
|
|
{
|
|
*pMessage = "Invalid ";
|
|
}
|
|
else if (!(l >= m_min_value))
|
|
{
|
|
*pMessage = "Too small a ";
|
|
}
|
|
else
|
|
{
|
|
mxb_assert(!(l <= m_max_value));
|
|
*pMessage = "Too large a ";
|
|
}
|
|
|
|
*pMessage += type();
|
|
*pMessage += ": ";
|
|
*pMessage += value_as_string;
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
std::string ParamNumber::to_string(value_type value) const
|
|
{
|
|
return std::to_string(value);
|
|
}
|
|
|
|
/**
|
|
* ParamCount
|
|
*/
|
|
std::string ParamCount::type() const
|
|
{
|
|
return "count";
|
|
}
|
|
|
|
/**
|
|
* ParamInteger
|
|
*/
|
|
std::string ParamInteger::type() const
|
|
{
|
|
return "integer";
|
|
}
|
|
|
|
/**
|
|
* ParamPath
|
|
*/
|
|
std::string ParamPath::type() const
|
|
{
|
|
return "path";
|
|
}
|
|
|
|
std::string ParamPath::default_to_string() const
|
|
{
|
|
return to_string(m_default_value);
|
|
}
|
|
|
|
bool ParamPath::validate(const std::string& value_as_string, std::string* pMessage) const
|
|
{
|
|
value_type value;
|
|
return from_string(value_as_string, &value, pMessage);
|
|
}
|
|
|
|
bool ParamPath::set(Type& value, const std::string& value_as_string) const
|
|
{
|
|
mxb_assert(&value.parameter() == this);
|
|
|
|
Path& path_value = static_cast<Path&>(value);
|
|
|
|
value_type x;
|
|
bool valid = from_string(value_as_string, &x);
|
|
|
|
if (valid)
|
|
{
|
|
path_value.set(x);
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
bool ParamPath::from_string(const std::string& value_as_string,
|
|
value_type* pValue,
|
|
std::string* pMessage) const
|
|
{
|
|
MXS_MODULE_PARAM param {};
|
|
param.options = m_options;
|
|
|
|
bool valid = check_path_parameter(¶m, value_as_string.c_str());
|
|
|
|
if (valid)
|
|
{
|
|
*pValue = value_as_string;
|
|
}
|
|
else if (pMessage)
|
|
{
|
|
*pMessage = "Invalid path (does not exist, required permissions are not granted, ";
|
|
*pMessage += "or cannot be created): ";
|
|
*pMessage += value_as_string;
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
std::string ParamPath::to_string(const value_type& value) const
|
|
{
|
|
return value;
|
|
}
|
|
|
|
void ParamPath::populate(MXS_MODULE_PARAM& param) const
|
|
{
|
|
Param::populate(param);
|
|
|
|
param.options |= m_options;
|
|
}
|
|
|
|
/**
|
|
* ParamServer
|
|
*/
|
|
std::string ParamServer::type() const
|
|
{
|
|
return "server";
|
|
}
|
|
|
|
std::string ParamServer::default_to_string() const
|
|
{
|
|
return "";
|
|
}
|
|
|
|
bool ParamServer::validate(const std::string& value_as_string, std::string* pMessage) const
|
|
{
|
|
value_type value;
|
|
return from_string(value_as_string, &value, pMessage);
|
|
}
|
|
|
|
bool ParamServer::set(Type& value, const std::string& value_as_string) const
|
|
{
|
|
mxb_assert(&value.parameter() == this);
|
|
|
|
Server& server_value = static_cast<Server&>(value);
|
|
|
|
value_type x;
|
|
bool valid = from_string(value_as_string, &x);
|
|
|
|
if (valid)
|
|
{
|
|
server_value.set(x);
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
bool ParamServer::from_string(const std::string& value_as_string,
|
|
value_type* pValue,
|
|
std::string* pMessage) const
|
|
{
|
|
*pValue = SERVER::find_by_unique_name(value_as_string);
|
|
|
|
if (!*pValue && pMessage)
|
|
{
|
|
*pMessage = "Unknown server: ";
|
|
*pMessage += value_as_string;
|
|
}
|
|
|
|
return *pValue;
|
|
}
|
|
|
|
std::string ParamServer::to_string(value_type value) const
|
|
{
|
|
return value->name();
|
|
}
|
|
|
|
/**
|
|
* ParamSize
|
|
*/
|
|
std::string ParamSize::type() const
|
|
{
|
|
return "size";
|
|
}
|
|
|
|
std::string ParamSize::default_to_string() const
|
|
{
|
|
return to_string(m_default_value);
|
|
}
|
|
|
|
bool ParamSize::validate(const std::string& value_as_string, std::string* pMessage) const
|
|
{
|
|
value_type value;
|
|
return from_string(value_as_string, &value, pMessage);
|
|
}
|
|
|
|
bool ParamSize::set(Type& value, const std::string& value_as_string) const
|
|
{
|
|
mxb_assert(&value.parameter() == this);
|
|
|
|
Size& size_value = static_cast<Size&>(value);
|
|
|
|
value_type x;
|
|
bool valid = from_string(value_as_string, &x);
|
|
|
|
if (valid)
|
|
{
|
|
size_value.set(x);
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
bool ParamSize::from_string(const std::string& value_as_string,
|
|
value_type* pValue,
|
|
std::string* pMessage) const
|
|
{
|
|
bool valid = get_suffixed_size(value_as_string.c_str(), pValue);
|
|
|
|
if (!valid && pMessage)
|
|
{
|
|
*pMessage = "Invalid size: ";
|
|
*pMessage += value_as_string;
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
std::string ParamSize::to_string(value_type value) const
|
|
{
|
|
// TODO: Use largest possible unit.
|
|
return std::to_string(value);
|
|
}
|
|
|
|
/**
|
|
* ParamString
|
|
*/
|
|
std::string ParamString::type() const
|
|
{
|
|
return "string";
|
|
}
|
|
|
|
std::string ParamString::default_to_string() const
|
|
{
|
|
return to_string(m_default_value);
|
|
}
|
|
|
|
bool ParamString::validate(const std::string& value_as_string, std::string* pMessage) const
|
|
{
|
|
value_type value;
|
|
return from_string(value_as_string, &value, pMessage);
|
|
}
|
|
|
|
bool ParamString::set(Type& value, const std::string& value_as_string) const
|
|
{
|
|
mxb_assert(&value.parameter() == this);
|
|
|
|
String& string_value = static_cast<String&>(value);
|
|
|
|
value_type x;
|
|
bool valid = from_string(value_as_string, &x);
|
|
|
|
if (valid)
|
|
{
|
|
string_value.set(x);
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
bool ParamString::from_string(const std::string& value_as_string,
|
|
value_type* pValue,
|
|
std::string* pMessage) const
|
|
{
|
|
bool valid = true;
|
|
|
|
char b = value_as_string.empty() ? 0 : value_as_string.front();
|
|
char e = value_as_string.empty() ? 0 : value_as_string.back();
|
|
|
|
if (b != '"' && b != '\'')
|
|
{
|
|
if (pMessage)
|
|
{
|
|
*pMessage = "A string value should be enclosed in quotes: ";
|
|
*pMessage += value_as_string;
|
|
}
|
|
}
|
|
|
|
string s = value_as_string;
|
|
|
|
if (b == '"' || b == '\'')
|
|
{
|
|
valid = (b == e);
|
|
|
|
if (valid)
|
|
{
|
|
s = s.substr(1, s.length() - 2);
|
|
}
|
|
else if (pMessage)
|
|
{
|
|
*pMessage = "A quoted string must end with the same quote: ";
|
|
*pMessage += value_as_string;
|
|
}
|
|
}
|
|
|
|
if (valid)
|
|
{
|
|
*pValue = s;
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
std::string ParamString::to_string(value_type value) const
|
|
{
|
|
stringstream ss;
|
|
ss << "\"" << value << "\"";
|
|
return ss.str();
|
|
}
|
|
}
|