mirror of
https://github.com/anope/anope.git
synced 2026-07-05 00:33:12 +02:00
Started framework for new config reader, based on hottpd by w00t, copied over from my own fork that was in progress.
git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@1409 5417fbe8-f217-4b02-8779-1006273d7864
This commit is contained in:
@@ -0,0 +1,378 @@
|
||||
#ifndef _CONFIGREADER_H_
|
||||
#define _CONFIGREADER_H_
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
/** A configuration key and value pair
|
||||
*/
|
||||
typedef std::pair<std::string, std::string> KeyVal;
|
||||
|
||||
/** A list of related configuration keys and values
|
||||
*/
|
||||
typedef std::vector<KeyVal> KeyValList;
|
||||
|
||||
/** An entire config file, built up of KeyValLists
|
||||
*/
|
||||
typedef std::multimap<std::string, KeyValList> ConfigDataHash;
|
||||
|
||||
// Required forward definitions
|
||||
class ServerConfig;
|
||||
|
||||
/** Types of data in the core config
|
||||
*/
|
||||
enum ConfigDataType {
|
||||
DT_NOTHING, // No data
|
||||
DT_INTEGER, // Integer
|
||||
DT_UINTEGER, // Unsigned Integer
|
||||
DT_CHARPTR, // Char pointer
|
||||
DT_STRING, // std::string
|
||||
DT_BOOLEAN, // Boolean
|
||||
DT_HOSTNAME, // Hostname syntax
|
||||
DT_NOSPACES, // No spaces
|
||||
DT_IPADDRESS, // IP address (v4, v6)
|
||||
DT_TIME, // Time value
|
||||
DT_NORELOAD = 32, // Item can't be reloaded after startup
|
||||
DT_ALLOW_WILD = 64, // Allow wildcards/CIDR in DT_IPADDRESS
|
||||
DT_ALLOW_NEWLINE = 128 // New line characters allowed in DT_CHARPTR
|
||||
};
|
||||
|
||||
/** Holds a config value, either string, integer or boolean.
|
||||
* Callback functions receive one or more of these, either on
|
||||
* their own as a reference, or in a reference to a deque of them.
|
||||
* The callback function can then alter the values of the ValueItem
|
||||
* classes to validate the settings.
|
||||
*/
|
||||
class ValueItem
|
||||
{
|
||||
/** Actual data */
|
||||
std::string v;
|
||||
public:
|
||||
/** Initialize with an int */
|
||||
ValueItem(int);
|
||||
/** Initialize with a bool */
|
||||
ValueItem(bool);
|
||||
/** Initialize with a char pointer */
|
||||
ValueItem(const char *);
|
||||
/** Initialize with an std::string */
|
||||
ValueItem(const std::string &);
|
||||
/** Change value to a char pointer */
|
||||
//void Set(char *);
|
||||
/** Change value to a const char pointer */
|
||||
void Set(const char *);
|
||||
/** Change value to an std::string */
|
||||
void Set(const std::string &);
|
||||
/** Change value to an int */
|
||||
void Set(int);
|
||||
/** Get value as an int */
|
||||
int GetInteger();
|
||||
/** Get value as a string */
|
||||
char *GetString();
|
||||
/** Get value as a bool */
|
||||
bool GetBool();
|
||||
};
|
||||
|
||||
/** The base class of the container 'ValueContainer'
|
||||
* used internally by the core to hold core values.
|
||||
*/
|
||||
class ValueContainerBase
|
||||
{
|
||||
public:
|
||||
/** Constructor */
|
||||
ValueContainerBase() { }
|
||||
/** Destructor */
|
||||
virtual ~ValueContainerBase() { }
|
||||
};
|
||||
|
||||
/** ValueContainer is used to contain pointers to different
|
||||
* core values such as the server name, maximum number of
|
||||
* clients etc.
|
||||
* It is specialized to hold a data type, then pointed at
|
||||
* a value in the ServerConfig class. When the value has been
|
||||
* read and validated, the Set method is called to write the
|
||||
* value safely in a type-safe manner.
|
||||
*/
|
||||
template<typename T> class ValueContainer : public ValueContainerBase
|
||||
{
|
||||
/** Contained item */
|
||||
T val;
|
||||
public:
|
||||
/** Initialize with nothing */
|
||||
ValueContainer() : ValueContainerBase(), val(NULL) { }
|
||||
/** Initialize with a value of type T */
|
||||
ValueContainer(T Val) : ValueContainerBase(), val(Val) { }
|
||||
/** Initialize with a copy */
|
||||
ValueContainer(const ValueContainer &Val) : ValueContainerBase(), val(Val.val) { }
|
||||
ValueContainer &operator=(const ValueContainer &Val)
|
||||
{
|
||||
val = Val.val;
|
||||
return *this;
|
||||
}
|
||||
/** Change value to type T of size s */
|
||||
void Set(const T newval, size_t s)
|
||||
{
|
||||
memcpy(val, newval, s);
|
||||
}
|
||||
};
|
||||
|
||||
/** This a specific version of ValueContainer to handle std::string specially
|
||||
*/
|
||||
template<> class ValueContainer<std::string *> : public ValueContainerBase
|
||||
{
|
||||
/** Contained item */
|
||||
std::string *val;
|
||||
public:
|
||||
/** Initialize with nothing */
|
||||
ValueContainer() : ValueContainerBase(), val(NULL) { }
|
||||
/** Initialize with an std::string */
|
||||
ValueContainer(std::string *Val) : ValueContainerBase(), val(Val) { }
|
||||
/** Initialize with a copy */
|
||||
ValueContainer(const ValueContainer &Val) : ValueContainerBase(), val(Val.val) { }
|
||||
ValueContainer &operator=(const ValueContainer &Val)
|
||||
{
|
||||
val = Val.val;
|
||||
return *this;
|
||||
}
|
||||
/** Change value to given std::string */
|
||||
void Set(const std::string &newval)
|
||||
{
|
||||
*val = newval;
|
||||
}
|
||||
/** Change value to given char pointer */
|
||||
void Set(const char *newval)
|
||||
{
|
||||
*val = newval;
|
||||
}
|
||||
};
|
||||
|
||||
/** A specialization of ValueContainer to hold a pointer to a bool
|
||||
*/
|
||||
typedef ValueContainer<bool *> ValueContainerBool;
|
||||
|
||||
/** A specialization of ValueContainer to hold a pointer to
|
||||
* an unsigned int
|
||||
*/
|
||||
typedef ValueContainer<unsigned *> ValueContainerUInt;
|
||||
|
||||
/** A specialization of ValueContainer to hold a pointer to
|
||||
* a char array.
|
||||
*/
|
||||
typedef ValueContainer<char *> ValueContainerChar;
|
||||
|
||||
/** A specialization of ValueContainer to hold a pointer to
|
||||
* an int
|
||||
*/
|
||||
typedef ValueContainer<int *> ValueContainerInt;
|
||||
|
||||
/** A specialization of ValueContainer to hold a pointer to
|
||||
* a time_t
|
||||
*/
|
||||
typedef ValueContainer<time_t *> ValueContainerTime;
|
||||
|
||||
/** A specialization of ValueContainer to hold a pointer to
|
||||
* an std::string
|
||||
*/
|
||||
typedef ValueContainer<std::string *> ValueContainerString;
|
||||
|
||||
/** A set of ValueItems used by multi-value validator functions
|
||||
*/
|
||||
typedef std::deque<ValueItem> ValueList;
|
||||
|
||||
/** A callback for validating a single value
|
||||
*/
|
||||
typedef bool (*Validator)(ServerConfig *, const char *, const char *, ValueItem &);
|
||||
/** A callback for validating multiple value entries
|
||||
*/
|
||||
typedef bool (*MultiValidator)(ServerConfig *, const char *, const char **, ValueList &, int *);
|
||||
/** A callback indicating the end of a group of entries
|
||||
*/
|
||||
typedef bool (*MultiNotify)(ServerConfig *, const char *);
|
||||
|
||||
/** Holds a core configuration item and its callbacks
|
||||
*/
|
||||
struct InitialConfig
|
||||
{
|
||||
/** Tag name */
|
||||
const char *tag;
|
||||
/** Value name */
|
||||
const char *value;
|
||||
/** Default, if not defined */
|
||||
const char *default_value;
|
||||
/** Value containers */
|
||||
ValueContainerBase *val;
|
||||
/** Data types */
|
||||
int datatype;
|
||||
/** Validation function */
|
||||
Validator validation_function;
|
||||
};
|
||||
|
||||
/** Holds a core configuration item and its callbacks
|
||||
* where there may be more than one item
|
||||
*/
|
||||
struct MultiConfig
|
||||
{
|
||||
/** Tag name */
|
||||
const char *tag;
|
||||
/** One or more items within tag */
|
||||
const char *items[17];
|
||||
/** One or more defaults for items within tags */
|
||||
const char *items_default[17];
|
||||
/** One or more data types */
|
||||
int datatype[17];
|
||||
/** Initialization function */
|
||||
MultiNotify init_function;
|
||||
/** Validation function */
|
||||
MultiValidator validation_function;
|
||||
/** Completion function */
|
||||
MultiNotify finish_function;
|
||||
};
|
||||
|
||||
/** This class holds the bulk of the runtime configuration for the ircd.
|
||||
* It allows for reading new config values, accessing configuration files,
|
||||
* and storage of the configuration data needed to run the ircd, such as
|
||||
* the servername, connect classes, /ADMIN data, MOTDs and filenames etc.
|
||||
*/
|
||||
class ServerConfig
|
||||
{
|
||||
private:
|
||||
/** This variable holds the names of all
|
||||
* files included from the main one. This
|
||||
* is used to make sure that no files are
|
||||
* recursively included.
|
||||
*/
|
||||
std::vector<std::string> include_stack;
|
||||
/** Process an include directive
|
||||
*/
|
||||
bool DoInclude(ConfigDataHash &, const std::string &, std::ostringstream &);
|
||||
/** Check that there is only one of each configuration item
|
||||
*/
|
||||
bool CheckOnce(const char *);
|
||||
public:
|
||||
std::ostringstream errstr;
|
||||
ConfigDataHash newconfig;
|
||||
/** This holds all the information in the config file,
|
||||
* it's indexed by tag name to a vector of key/values.
|
||||
*/
|
||||
ConfigDataHash config_data;
|
||||
/** Construct a new ServerConfig
|
||||
*/
|
||||
ServerConfig();
|
||||
/** Clears the include stack in preperation for a Read() call.
|
||||
*/
|
||||
void ClearStack();
|
||||
/** Read the entire configuration into memory
|
||||
* and initialize this class. All other methods
|
||||
* should be used only by the core.
|
||||
*/
|
||||
int Read(bool);
|
||||
/** Report a configuration error given in errormessage.
|
||||
* @param bail If this is set to true, the error is sent to the console, and the program exits
|
||||
* @param connection If this is set to a non-null value, and bail is false, the errors are spooled to
|
||||
* this connection as SNOTICEs.
|
||||
* If the parameter is NULL, the messages are spooled to all connections via WriteOpers as SNOTICEs.
|
||||
*/
|
||||
void ReportConfigError(const std::string &, bool);
|
||||
/** Load 'filename' into 'target', with the new config parser everything is parsed into
|
||||
* tag/key/value at load-time rather than at read-value time.
|
||||
*/
|
||||
bool LoadConf(ConfigDataHash &, const char *, std::ostringstream &);
|
||||
/** Load 'filename' into 'target', with the new config parser everything is parsed into
|
||||
* tag/key/value at load-time rather than at read-value time.
|
||||
*/
|
||||
bool LoadConf(ConfigDataHash &, const std::string &, std::ostringstream &);
|
||||
// Both these return true if the value existed or false otherwise
|
||||
/** Writes 'length' chars into 'result' as a string
|
||||
*/
|
||||
bool ConfValue(ConfigDataHash &, const char *, const char *, int, char *, int, bool = false);
|
||||
/** Writes 'length' chars into 'result' as a string
|
||||
*/
|
||||
bool ConfValue(ConfigDataHash &, const char *, const char *, const char *, int, char *, int, bool = false);
|
||||
/** Writes 'length' chars into 'result' as a string
|
||||
*/
|
||||
bool ConfValue(ConfigDataHash &, const std::string &, const std::string &, int, std::string &, bool = false);
|
||||
/** Writes 'length' chars into 'result' as a string
|
||||
*/
|
||||
bool ConfValue(ConfigDataHash &, const std::string &, const std::string &, const std::string &, int, std::string &, bool = false);
|
||||
/** Tries to convert the value to an integer and write it to 'result'
|
||||
*/
|
||||
bool ConfValueInteger(ConfigDataHash &, const char *, const char *, int, int &);
|
||||
/** Tries to convert the value to an integer and write it to 'result'
|
||||
*/
|
||||
bool ConfValueInteger(ConfigDataHash &, const char *, const char *, const char *, int, int &);
|
||||
/** Tries to convert the value to an integer and write it to 'result'
|
||||
*/
|
||||
bool ConfValueInteger(ConfigDataHash &, const std::string &, const std::string &, int, int &);
|
||||
/** Tries to convert the value to an integer and write it to 'result'
|
||||
*/
|
||||
bool ConfValueInteger(ConfigDataHash &, const std::string &, const std::string &, const std::string &, int, int &);
|
||||
/** Returns true if the value exists and has a true value, false otherwise
|
||||
*/
|
||||
bool ConfValueBool(ConfigDataHash &, const char *, const char *, int);
|
||||
/** Returns true if the value exists and has a true value, false otherwise
|
||||
*/
|
||||
bool ConfValueBool(ConfigDataHash &, const char *, const char *, const char *, int);
|
||||
/** Returns true if the value exists and has a true value, false otherwise
|
||||
*/
|
||||
bool ConfValueBool(ConfigDataHash &, const std::string &, const std::string &, int);
|
||||
/** Returns true if the value exists and has a true value, false otherwise
|
||||
*/
|
||||
bool ConfValueBool(ConfigDataHash &, const std::string &, const std::string &, const std::string &, int);
|
||||
/** Returns the number of occurences of tag in the config file
|
||||
*/
|
||||
int ConfValueEnum(ConfigDataHash &, const char *);
|
||||
/** Returns the number of occurences of tag in the config file
|
||||
*/
|
||||
int ConfValueEnum(ConfigDataHash &, const std::string &);
|
||||
/** Returns the numbers of vars inside the index'th 'tag in the config file
|
||||
*/
|
||||
int ConfVarEnum(ConfigDataHash &, const char *, int);
|
||||
/** Returns the numbers of vars inside the index'th 'tag in the config file
|
||||
*/
|
||||
int ConfVarEnum(ConfigDataHash &, const std::string &, int);
|
||||
void ValidateHostname(const char *, const std::string &, const std::string &);
|
||||
void ValidateIP(const char *p, const std::string &, const std::string &, bool);
|
||||
void ValidateNoSpaces(const char *, const std::string &, const std::string &);
|
||||
};
|
||||
|
||||
/** Initialize the disabled commands list
|
||||
*/
|
||||
E bool InitializeDisabledCommands(const char *);
|
||||
|
||||
/** This class can be used on its own to represent an exception, or derived to represent a module-specific exception.
|
||||
* When a module whishes to abort, e.g. within a constructor, it should throw an exception using ModuleException or
|
||||
* a class derived from ModuleException. If a module throws an exception during its constructor, the module will not
|
||||
* be loaded. If this happens, the error message returned by ModuleException::GetReason will be displayed to the user
|
||||
* attempting to load the module, or dumped to the console if the ircd is currently loading for the first time.
|
||||
*/
|
||||
class ConfigException : public std::exception
|
||||
{
|
||||
protected:
|
||||
/** Holds the error message to be displayed
|
||||
*/
|
||||
const std::string err;
|
||||
public:
|
||||
/** Default constructor, just uses the error mesage 'Config threw an exception'.
|
||||
*/
|
||||
ConfigException() : err("Config threw an exception") { }
|
||||
/** This constructor can be used to specify an error message before throwing.
|
||||
*/
|
||||
ConfigException(const std::string &message) : err(message) {}
|
||||
/** This destructor solves world hunger, cancels the world debt, and causes the world to end.
|
||||
* Actually no, it does nothing. Never mind.
|
||||
* @throws Nothing!
|
||||
*/
|
||||
virtual ~ConfigException() throw() { };
|
||||
/** Returns the reason for the exception.
|
||||
* The module should probably put something informative here as the user will see this upon failure.
|
||||
*/
|
||||
virtual const char *GetReason()
|
||||
{
|
||||
return err.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
+701
-4
@@ -6,13 +6,14 @@
|
||||
* Please read COPYING and README for further details.
|
||||
*
|
||||
* Based on the original code of Epona by Lara.
|
||||
* Based on the original code of Services by Andy Church.
|
||||
*
|
||||
* $Id$
|
||||
* Based on the original code of Services by Andy Church.
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "services.h"
|
||||
#include "configreader.h"
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
@@ -277,7 +278,7 @@ int ModulesDelayedNumber;
|
||||
char **ModulesDelayedAutoload;
|
||||
|
||||
/**
|
||||
* Core Module Stuff
|
||||
* Core Module Stuff
|
||||
**/
|
||||
char *HostCoreModules;
|
||||
char **HostServCoreModules;
|
||||
@@ -351,6 +352,702 @@ int NumUlines;
|
||||
int UseTS6;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
ServerConfig::ServerConfig() : include_stack(), errstr(""), newconfig(), config_data()
|
||||
{
|
||||
this->ClearStack();
|
||||
}
|
||||
|
||||
void ServerConfig::ClearStack()
|
||||
{
|
||||
include_stack.clear();
|
||||
}
|
||||
|
||||
bool ServerConfig::CheckOnce(const char *tag)
|
||||
{
|
||||
int count = ConfValueEnum(config_data, tag);
|
||||
if (count > 1) {
|
||||
throw ConfigException(static_cast<std::string>("You have more than one <") + tag + "> tag, this is not permitted.");
|
||||
}
|
||||
if (count < 1) {
|
||||
throw ConfigException(static_cast<std::string>("You have not defined a <") + tag + "> tag, this is required.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoValidation(ServerConfig *, const char *, const char *, ValueItem &)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DoneConfItem(ServerConfig *, const char *)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void ServerConfig::ValidateNoSpaces(const char *p, const std::string &tag, const std::string &val)
|
||||
{
|
||||
for (const char *ptr = p; *ptr; ++ptr) if (*ptr == ' ') throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val +
|
||||
"> cannot contain spaces");
|
||||
}
|
||||
|
||||
/* NOTE: Before anyone asks why we're not using inet_pton for this, it is because inet_pton and friends do not return so much detail,
|
||||
* even in strerror(errno). They just return 'yes' or 'no' to an address without such detail as to whats WRONG with the address.
|
||||
* Because ircd users arent as technical as they used to be (;)) we are going to give more of a useful error message.
|
||||
*/
|
||||
void ServerConfig::ValidateIP(const char *p, const std::string &tag, const std::string &val, bool wild)
|
||||
{
|
||||
int num_dots = 0, num_seps = 0;
|
||||
bool not_numbers = false, not_hex = false;
|
||||
if (*p) {
|
||||
if (*p == '.') throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val + "> is not an IP address");
|
||||
for (const char *ptr = p; *ptr; ++ptr) {
|
||||
if (wild && (*ptr == '*' || *ptr == '?' || *ptr == '/')) continue;
|
||||
if (*ptr != ':' && *ptr != '.') {
|
||||
if (*ptr < '0' || *ptr > '9') {
|
||||
not_numbers = true;
|
||||
if (toupper(*ptr) < 'A' || toupper(*ptr) > 'F') not_hex = true;
|
||||
}
|
||||
}
|
||||
switch (*ptr) {
|
||||
case ' ':
|
||||
throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val + "> is not an IP address");
|
||||
case '.':
|
||||
++num_dots;
|
||||
break;
|
||||
case ':':
|
||||
++num_seps;
|
||||
}
|
||||
}
|
||||
if (num_dots > 3) throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val +
|
||||
"> is an IPv4 address with too many fields!");
|
||||
if (num_seps > 8) throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val +
|
||||
"> is an IPv6 address with too many fields!");
|
||||
if (!num_seps && num_dots < 3 && !wild) throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val +
|
||||
"> looks to be a malformed IPv4 address");
|
||||
if (!num_seps && num_dots == 3 && not_numbers) throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val +
|
||||
"> contains non-numeric characters in an IPv4 address");
|
||||
if (num_seps && not_hex) throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val +
|
||||
"> contains non-hexdecimal characters in an IPv6 address");
|
||||
if (num_seps && num_dots != 3 && num_dots && !wild) throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val +
|
||||
"> is a malformed IPv6 4in6 address");
|
||||
}
|
||||
}
|
||||
|
||||
void ServerConfig::ValidateHostname(const char *p, const std::string &tag, const std::string &val)
|
||||
{
|
||||
int num_dots = 0;
|
||||
if (*p) {
|
||||
if (*p == '.') throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val + "> is not a valid hostname");
|
||||
for (const char *ptr = p; *ptr; ++ptr) {
|
||||
switch (*ptr) {
|
||||
case ' ':
|
||||
throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val + "> is not a valid hostname");
|
||||
case '.':
|
||||
++num_dots;
|
||||
}
|
||||
}
|
||||
if (!num_dots) throw ConfigException(static_cast<std::string>("The value of <") + tag + ":" + val + "> is not a valid hostname");
|
||||
}
|
||||
}
|
||||
|
||||
bool ValidateMaxTargets(ServerConfig *, const char *, const char *, ValueItem &data)
|
||||
{
|
||||
if (data.GetInteger() < 0 || data.GetInteger() > 31) {
|
||||
alog("WARNING: <options:maxtargets> value is greater than 31 or less than 0, set to 20.");
|
||||
data.Set(20);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateNotEmpty(ServerConfig *, const char *tag, const char *value, ValueItem &data)
|
||||
{
|
||||
if (!*data.GetString()) throw ConfigException(static_cast<std::string>("The value for <") + tag + ":" + value + "> cannot be empty!");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatePort(ServerConfig *, const char *tag, const char *value, ValueItem &data)
|
||||
{
|
||||
int port = data.GetInteger();
|
||||
if (!port) return true;
|
||||
if (port < 1 || port > 65535) throw ConfigException(static_cast<std::string>("The value for <") + tag + ":" + value +
|
||||
"> is not a value port, it must be between 1 and 65535!");
|
||||
return true;
|
||||
}
|
||||
|
||||
void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail)
|
||||
{
|
||||
alog("There were errors in your configuration file: %s", errormessage.c_str());
|
||||
if (bail) {
|
||||
// TODO -- Need a way to stop loading in a safe way -- CyberBotX
|
||||
//ServerInstance->Exit(EXIT_STATUS_CONFIG);
|
||||
}
|
||||
}
|
||||
|
||||
int ServerConfig::Read(bool bail)
|
||||
{
|
||||
errstr.clear();
|
||||
// These tags MUST occur and must ONLY occur once in the config file
|
||||
static const char *Once[] = {NULL};
|
||||
// These tags can occur ONCE or not at all
|
||||
InitialConfig Values[] = {
|
||||
{NULL, NULL, NULL, NULL, DT_NOTHING, NoValidation}
|
||||
};
|
||||
/* These tags can occur multiple times, and therefore they have special code to read them
|
||||
* which is different to the code for reading the singular tags listed above. */
|
||||
MultiConfig MultiValues[] = {
|
||||
{NULL,
|
||||
{NULL},
|
||||
{NULL},
|
||||
{0},
|
||||
NULL, NULL, NULL}
|
||||
};
|
||||
// Load and parse the config file, if there are any errors then explode
|
||||
// Make a copy here so if it fails then we can carry on running with an unaffected config
|
||||
newconfig.clear();
|
||||
if (LoadConf(newconfig, SERVICES_CONF, errstr)) {
|
||||
// If we succeeded, set the ircd config to the new one
|
||||
config_data = newconfig;
|
||||
}
|
||||
else {
|
||||
ReportConfigError(errstr.str(), bail);
|
||||
return 0;
|
||||
}
|
||||
// The stuff in here may throw CoreException, be sure we're in a position to catch it.
|
||||
try {
|
||||
// Read the values of all the tags which occur once or not at all, and call their callbacks.
|
||||
for (int Index = 0; Values[Index].tag; ++Index) {
|
||||
char item[BUFSIZE];
|
||||
int dt = Values[Index].datatype;
|
||||
bool allow_newlines = dt & DT_ALLOW_NEWLINE, allow_wild = dt & DT_ALLOW_WILD;
|
||||
dt &= ~DT_ALLOW_NEWLINE;
|
||||
dt &= ~DT_ALLOW_WILD;
|
||||
ConfValue(config_data, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, BUFSIZE, allow_newlines);
|
||||
ValueItem vi(item);
|
||||
if (!Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, vi))
|
||||
throw ConfigException("One or more values in your configuration file failed to validate. Please see your ircd.log for more information.");
|
||||
switch (dt) {
|
||||
case DT_NOSPACES: {
|
||||
ValueContainerChar *vcc = dynamic_cast<ValueContainerChar *>(Values[Index].val);
|
||||
ValidateNoSpaces(vi.GetString(), Values[Index].tag, Values[Index].value);
|
||||
vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
|
||||
}
|
||||
break;
|
||||
case DT_HOSTNAME: {
|
||||
ValueContainerChar *vcc = dynamic_cast<ValueContainerChar *>(Values[Index].val);
|
||||
ValidateHostname(vi.GetString(), Values[Index].tag, Values[Index].value);
|
||||
vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
|
||||
}
|
||||
break;
|
||||
case DT_IPADDRESS: {
|
||||
ValueContainerChar *vcc = dynamic_cast<ValueContainerChar *>(Values[Index].val);
|
||||
ValidateIP(vi.GetString(), Values[Index].tag, Values[Index].value, allow_wild);
|
||||
vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
|
||||
}
|
||||
break;
|
||||
case DT_CHARPTR: {
|
||||
ValueContainerChar *vcc = dynamic_cast<ValueContainerChar *>(Values[Index].val);
|
||||
// Make sure we also copy the null terminator
|
||||
vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
|
||||
}
|
||||
break;
|
||||
case DT_STRING: {
|
||||
ValueContainerString *vcs = dynamic_cast<ValueContainerString *>(Values[Index].val);
|
||||
vcs->Set(vi.GetString());
|
||||
}
|
||||
break;
|
||||
case DT_INTEGER: {
|
||||
int val = vi.GetInteger();
|
||||
ValueContainerInt *vci = dynamic_cast<ValueContainerInt *>(Values[Index].val);
|
||||
vci->Set(&val, sizeof(int));
|
||||
}
|
||||
break;
|
||||
case DT_UINTEGER: {
|
||||
unsigned val = vi.GetInteger();
|
||||
ValueContainerUInt *vci = dynamic_cast<ValueContainerUInt *>(Values[Index].val);
|
||||
vci->Set(&val, sizeof(int));
|
||||
}
|
||||
break;
|
||||
case DT_TIME: {
|
||||
time_t time = dotime(vi.GetString());
|
||||
ValueContainerTime *vci = dynamic_cast<ValueContainerTime *>(Values[Index].val);
|
||||
vci->Set(&time, sizeof(time_t));
|
||||
}
|
||||
break;
|
||||
case DT_BOOLEAN: {
|
||||
bool val = vi.GetBool();
|
||||
ValueContainerBool *vcb = dynamic_cast<ValueContainerBool *>(Values[Index].val);
|
||||
vcb->Set(&val, sizeof(bool));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// We're done with this now
|
||||
delete Values[Index].val;
|
||||
}
|
||||
/* Read the multiple-tag items (class tags, connect tags, etc)
|
||||
* and call the callbacks associated with them. We have three
|
||||
* callbacks for these, a 'start', 'item' and 'end' callback. */
|
||||
for (int Index = 0; MultiValues[Index].tag; ++Index) {
|
||||
MultiValues[Index].init_function(this, MultiValues[Index].tag);
|
||||
int number_of_tags = ConfValueEnum(config_data, MultiValues[Index].tag);
|
||||
for (int tagnum = 0; tagnum < number_of_tags; ++tagnum) {
|
||||
ValueList vl;
|
||||
for (int valuenum = 0; MultiValues[Index].items[valuenum]; ++valuenum) {
|
||||
int dt = MultiValues[Index].datatype[valuenum];
|
||||
bool allow_newlines = dt & DT_ALLOW_NEWLINE, allow_wild = dt & DT_ALLOW_WILD;
|
||||
dt &= ~DT_ALLOW_NEWLINE;
|
||||
dt &= ~DT_ALLOW_WILD;
|
||||
switch (dt) {
|
||||
case DT_NOSPACES: {
|
||||
char item[BUFSIZE];
|
||||
if (ConfValue(config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum],
|
||||
MultiValues[Index].items_default[valuenum], tagnum, item, BUFSIZE, allow_newlines)) {
|
||||
vl.push_back(ValueItem(item));
|
||||
}
|
||||
else vl.push_back(ValueItem(""));
|
||||
ValidateNoSpaces(vl[vl.size() - 1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]);
|
||||
}
|
||||
break;
|
||||
case DT_HOSTNAME: {
|
||||
char item[BUFSIZE];
|
||||
if (ConfValue(config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum],
|
||||
MultiValues[Index].items_default[valuenum], tagnum, item, BUFSIZE, allow_newlines)) {
|
||||
vl.push_back(ValueItem(item));
|
||||
}
|
||||
else vl.push_back(ValueItem(""));
|
||||
ValidateHostname(vl[vl.size() - 1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]);
|
||||
}
|
||||
break;
|
||||
case DT_IPADDRESS: {
|
||||
char item[BUFSIZE];
|
||||
if (ConfValue(config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum],
|
||||
MultiValues[Index].items_default[valuenum], tagnum, item, BUFSIZE, allow_newlines)) {
|
||||
vl.push_back(ValueItem(item));
|
||||
}
|
||||
else vl.push_back(ValueItem(""));
|
||||
ValidateIP(vl[vl.size() - 1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum], allow_wild);
|
||||
}
|
||||
break;
|
||||
case DT_CHARPTR: {
|
||||
char item[BUFSIZE];
|
||||
if (ConfValue(config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum],
|
||||
MultiValues[Index].items_default[valuenum], tagnum, item, BUFSIZE, allow_newlines)) {
|
||||
vl.push_back(ValueItem(item));
|
||||
}
|
||||
else vl.push_back(ValueItem(""));
|
||||
}
|
||||
break;
|
||||
case DT_STRING: {
|
||||
std::string item;
|
||||
if (ConfValue(config_data, static_cast<std::string>(MultiValues[Index].tag),
|
||||
static_cast<std::string>(MultiValues[Index].items[valuenum]),
|
||||
static_cast<std::string>(MultiValues[Index].items_default[valuenum]), tagnum, item, allow_newlines)) {
|
||||
vl.push_back(ValueItem(item));
|
||||
}
|
||||
else vl.push_back(ValueItem(""));
|
||||
}
|
||||
break;
|
||||
case DT_INTEGER:
|
||||
case DT_UINTEGER: {
|
||||
int item = 0;
|
||||
if (ConfValueInteger(config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum],
|
||||
MultiValues[Index].items_default[valuenum], tagnum, item)) vl.push_back(ValueItem(item));
|
||||
else vl.push_back(ValueItem(0));
|
||||
}
|
||||
break;
|
||||
case DT_TIME: {
|
||||
std::string item;
|
||||
if (ConfValue(config_data, static_cast<std::string>(MultiValues[Index].tag),
|
||||
static_cast<std::string>(MultiValues[Index].items[valuenum]),
|
||||
static_cast<std::string>(MultiValues[Index].items_default[valuenum]), tagnum, item, allow_newlines)) {
|
||||
int time = dotime(item.c_str());
|
||||
vl.push_back(ValueItem(time));
|
||||
}
|
||||
else vl.push_back(ValueItem(0));
|
||||
}
|
||||
break;
|
||||
case DT_BOOLEAN: {
|
||||
bool item = ConfValueBool(config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum],
|
||||
MultiValues[Index].items_default[valuenum], tagnum);
|
||||
vl.push_back(ValueItem(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
MultiValues[Index].validation_function(this, MultiValues[Index].tag, static_cast<const char **>(MultiValues[Index].items), vl,
|
||||
MultiValues[Index].datatype);
|
||||
}
|
||||
MultiValues[Index].finish_function(this, MultiValues[Index].tag);
|
||||
}
|
||||
}
|
||||
catch (ConfigException &ce) {
|
||||
ReportConfigError(ce.GetReason(), bail);
|
||||
return 0;
|
||||
}
|
||||
if (debug) alog("End config");
|
||||
for (int Index = 0; Once[Index]; ++Index) if (!CheckOnce(Once[Index])) return 0;
|
||||
alog("Done reading configuration file.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool ServerConfig::LoadConf(ConfigDataHash &target, const char *filename, std::ostringstream &errorstream)
|
||||
{
|
||||
std::string line, wordbuffer, section, itemname;
|
||||
std::ifstream conf(filename);
|
||||
int linenumber = 0;
|
||||
bool in_word = false, in_quote = false, in_ml_comment = false;
|
||||
KeyValList sectiondata;
|
||||
if (conf.fail()) {
|
||||
errorstream << "File " << filename << " could not be opened." << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (debug) alog("Start to read conf %s", filename);
|
||||
// Start reading characters...
|
||||
while (getline(conf, line)) {
|
||||
++linenumber;
|
||||
unsigned c = 0, len = line.size();
|
||||
for (; c < len; ++c) {
|
||||
char ch = line[c];
|
||||
if (in_quote) {
|
||||
if (ch == '"') {
|
||||
in_quote = in_word = false;
|
||||
continue;
|
||||
}
|
||||
wordbuffer += ch;
|
||||
continue;
|
||||
}
|
||||
if (in_ml_comment) {
|
||||
if (ch == '*' && c + 1 < len && line[c + 1] == '/') {
|
||||
in_ml_comment = false;
|
||||
++c;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (ch == '#' || (ch == '/' && c + 1 < len && line[c + 1] == '/')) break; // Line comment, ignore the rest of the line (much like this one!)
|
||||
else if (ch == '/' && c + 1 < len && line[c + 1] == '*') {
|
||||
// Multiline (or less than one line) comment
|
||||
in_ml_comment = true;
|
||||
++c;
|
||||
continue;
|
||||
}
|
||||
else if (ch == '"') {
|
||||
// Quotes are valid only in the value position
|
||||
if (section.empty() || itemname.empty()) {
|
||||
errorstream << "Unexpected quoted string: " << filename << ":" << linenumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (in_word || !wordbuffer.empty()) {
|
||||
errorstream << "Unexpected quoted string (prior unhandled words): " << filename << ":" << linenumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
in_quote = in_word = true;
|
||||
continue;
|
||||
}
|
||||
else if (ch == '=') {
|
||||
if (section.empty()) {
|
||||
errorstream << "Config item outside of section (or stray '='): " << filename << ":" << linenumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!itemname.empty()) {
|
||||
errorstream << "Stray '=' sign or item without value: " << filename << ":" << linenumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (in_word) in_word = false;
|
||||
itemname = wordbuffer;
|
||||
wordbuffer.clear();
|
||||
}
|
||||
else if (ch == '{') {
|
||||
if (!section.empty()) {
|
||||
errorstream << "Section inside another section: " << filename << ":" << linenumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (wordbuffer.empty()) {
|
||||
errorstream << "Section without a name or unexpected '{': " << filename << ":" << linenumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (in_word) in_word = false;
|
||||
section = wordbuffer;
|
||||
wordbuffer.clear();
|
||||
}
|
||||
else if (ch == '}') {
|
||||
if (section.empty()) {
|
||||
errorstream << "Stray '}': " << filename << ":" << linenumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!wordbuffer.empty() || !itemname.empty()) {
|
||||
errorstream << "Unexpected end of section: " << filename << ":" << linenumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
target.insert(std::pair<std::string, KeyValList>(section, sectiondata));
|
||||
section.clear();
|
||||
sectiondata.clear();
|
||||
}
|
||||
else if (ch == ';' || ch == '\r') continue; // Ignore
|
||||
else if (ch == ' ' || ch == '\t') {
|
||||
// Terminate word
|
||||
if (in_word) in_word = false;
|
||||
}
|
||||
else {
|
||||
if (!in_word && !wordbuffer.empty()) {
|
||||
errorstream << "Unexpected word: " << filename << ":" << linenumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
wordbuffer += ch;
|
||||
in_word = true;
|
||||
}
|
||||
}
|
||||
if (in_quote) {
|
||||
// Quotes can span multiple lines; all we need to do is go to the next line without clearing things
|
||||
wordbuffer += "\n";
|
||||
continue;
|
||||
}
|
||||
in_word = false;
|
||||
if (!itemname.empty()) {
|
||||
if (wordbuffer.empty()) {
|
||||
errorstream << "Item without value: " << filename << ":" << linenumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (debug) alog("ln %d EOL: s='%s' '%s' set to '%s'", linenumber, section.c_str(), itemname.c_str(), wordbuffer.c_str());
|
||||
sectiondata.push_back(KeyVal(itemname, wordbuffer));
|
||||
wordbuffer.clear();
|
||||
itemname.clear();
|
||||
}
|
||||
}
|
||||
if (in_ml_comment) {
|
||||
errorstream << "Unterminated multiline comment at end of file: " << filename << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (in_quote) {
|
||||
errorstream << "Unterminated quote at end of file: " << filename << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!itemname.empty() || !wordbuffer.empty()) {
|
||||
errorstream << "Unexpected garbage at end of file: " << filename << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!section.empty()) {
|
||||
errorstream << "Unterminated section at end of file: " << filename << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream)
|
||||
{
|
||||
return LoadConf(target, filename.c_str(), errorstream);
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValue(ConfigDataHash &target, const char *tag, const char *var, int index, char *result, int length, bool allow_linefeeds)
|
||||
{
|
||||
return ConfValue(target, tag, var, "", index, result, length, allow_linefeeds);
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValue(ConfigDataHash &target, const char *tag, const char *var, const char *default_value, int index, char *result,
|
||||
int length, bool allow_linefeeds)
|
||||
{
|
||||
std::string value;
|
||||
bool r = ConfValue(target, static_cast<std::string>(tag), static_cast<std::string>(var), static_cast<std::string>(default_value), index, value,
|
||||
allow_linefeeds);
|
||||
strlcpy(result, value.c_str(), length);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result,
|
||||
bool allow_linefeeds)
|
||||
{
|
||||
return ConfValue(target, tag, var, "", index, result, allow_linefeeds);
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index,
|
||||
std::string &result, bool allow_linefeeds)
|
||||
{
|
||||
ConfigDataHash::size_type pos = index;
|
||||
if (pos < target.count(tag)) {
|
||||
ConfigDataHash::iterator iter = target.find(tag);
|
||||
for (int i = 0; i < index; ++i) ++iter;
|
||||
KeyValList::iterator j = iter->second.begin(), jend = iter->second.end();
|
||||
for (; j != jend; ++j) {
|
||||
if (j->first == var) {
|
||||
if (!allow_linefeeds && j->second.find('\n') != std::string::npos) {
|
||||
alog("Value of <%s:%s> contains a linefeed, and linefeeds in this value are not permitted -- stripped to spaces.", tag.c_str(), var.c_str());
|
||||
std::string::iterator n = j->second.begin(), nend = j->second.end();
|
||||
for (; n != nend; ++n) if (*n == '\n') *n = ' ';
|
||||
}
|
||||
else {
|
||||
result = j->second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!default_value.empty()) {
|
||||
result = default_value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (!pos) {
|
||||
if (!default_value.empty()) {
|
||||
result = default_value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char *tag, const char *var, int index, int &result)
|
||||
{
|
||||
return ConfValueInteger(target, static_cast<std::string>(tag), static_cast<std::string>(var), "", index, result);
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char *tag, const char *var, const char *default_value, int index, int &result)
|
||||
{
|
||||
return ConfValueInteger(target, static_cast<std::string>(tag), static_cast<std::string>(var), static_cast<std::string>(default_value), index,
|
||||
result);
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result)
|
||||
{
|
||||
return ConfValueInteger(target, tag, var, "", index, result);
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index, int &result)
|
||||
{
|
||||
std::string value;
|
||||
std::istringstream stream;
|
||||
bool r = ConfValue(target, tag, var, default_value, index, value);
|
||||
stream.str(value);
|
||||
if (!(stream >> result)) return false;
|
||||
else {
|
||||
if (!value.empty()) {
|
||||
if (value.substr(0, 2) == "0x") {
|
||||
char *endptr;
|
||||
value.erase(0, 2);
|
||||
result = strtol(value.c_str(), &endptr, 16);
|
||||
/* No digits found */
|
||||
if (endptr == value.c_str()) return false;
|
||||
}
|
||||
else {
|
||||
char denominator = *(value.end() - 1);
|
||||
switch (toupper(denominator)) {
|
||||
case 'K':
|
||||
// Kilobytes -> bytes
|
||||
result = result * 1024;
|
||||
break;
|
||||
case 'M':
|
||||
// Megabytes -> bytes
|
||||
result = result * 1048576;
|
||||
break;
|
||||
case 'G':
|
||||
// Gigabytes -> bytes
|
||||
result = result * 1073741824;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char *tag, const char *var, int index)
|
||||
{
|
||||
return ConfValueBool(target, static_cast<std::string>(tag), static_cast<std::string>(var), "", index);
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char *tag, const char *var, const char *default_value, int index)
|
||||
{
|
||||
return ConfValueBool(target, static_cast<std::string>(tag), static_cast<std::string>(var), static_cast<std::string>(default_value), index);
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index)
|
||||
{
|
||||
return ConfValueBool(target, tag, var, "", index);
|
||||
}
|
||||
|
||||
bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, const std::string &default_value, int index)
|
||||
{
|
||||
std::string result;
|
||||
if (!ConfValue(target, tag, var, default_value, index, result)) return false;
|
||||
return result == "yes" || result == "true" || result == "1";
|
||||
}
|
||||
|
||||
int ServerConfig::ConfValueEnum(ConfigDataHash &target, const char *tag)
|
||||
{
|
||||
return target.count(tag);
|
||||
}
|
||||
|
||||
int ServerConfig::ConfValueEnum(ConfigDataHash &target, const std::string &tag)
|
||||
{
|
||||
return target.count(tag);
|
||||
}
|
||||
|
||||
int ServerConfig::ConfVarEnum(ConfigDataHash &target, const char *tag, int index)
|
||||
{
|
||||
return ConfVarEnum(target, static_cast<std::string>(tag), index);
|
||||
}
|
||||
|
||||
int ServerConfig::ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index)
|
||||
{
|
||||
ConfigDataHash::size_type pos = index;
|
||||
if (pos < target.count(tag)) {
|
||||
ConfigDataHash::const_iterator iter = target.find(tag);
|
||||
for (int i = 0; i < index; ++i) ++iter;
|
||||
return iter->second.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ValueItem::ValueItem(int value) : v("")
|
||||
{
|
||||
std::stringstream n;
|
||||
n << value;
|
||||
v = n.str();
|
||||
}
|
||||
|
||||
ValueItem::ValueItem(bool value) : v("")
|
||||
{
|
||||
std::stringstream n;
|
||||
n << value;
|
||||
v = n.str();
|
||||
}
|
||||
|
||||
ValueItem::ValueItem(const char *value) : v(value) { }
|
||||
|
||||
ValueItem::ValueItem(const std::string &value) : v(value) { }
|
||||
|
||||
void ValueItem::Set(const char *value)
|
||||
{
|
||||
v = value;
|
||||
}
|
||||
|
||||
void ValueItem::Set(const std::string &value)
|
||||
{
|
||||
v = value;
|
||||
}
|
||||
|
||||
void ValueItem::Set(int value)
|
||||
{
|
||||
std::stringstream n;
|
||||
n << value;
|
||||
v = n.str();
|
||||
}
|
||||
|
||||
int ValueItem::GetInteger()
|
||||
{
|
||||
if (v.empty()) return 0;
|
||||
return atoi(v.c_str());
|
||||
}
|
||||
|
||||
char *ValueItem::GetString()
|
||||
{
|
||||
return const_cast<char *>(v.c_str());
|
||||
}
|
||||
|
||||
bool ValueItem::GetBool()
|
||||
{
|
||||
return GetInteger() || v == "yes" || v == "true";
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/* Deprecated directive (dep_) and value checking (chk_) functions: */
|
||||
|
||||
Reference in New Issue
Block a user