diff -r a10ba529ae39 -r 7728d6ec3abf src/spbot/config.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/spbot/config.c Wed May 27 23:57:48 2009 +0300 @@ -0,0 +1,268 @@ +#include "config.h" + +#include +#include + +const struct error_list config_errors = ERROR_LIST("config", + ERROR_TYPE_STRING( ERR_CONFIG_NAME, "unknown config option" ), + ERROR_TYPE( ERR_CONFIG_TYPE, "invalid config type" ), + ERROR_TYPE_STRING( ERR_CONFIG_REQUIRED, "missing required value" ), + ERROR_TYPE_STRING( ERR_CONFIG_VALUE, "invalid value" ), + ERROR_TYPE( ERR_CONFIG_PARAMS, "invalid number of paramters" ) +); + +const struct config_option* config_lookup (const struct config_option *options, const char *name, error_t *err) +{ + const struct config_option *option; + + // find the matching config opt + for (option = options; option->name; option++) { + if (strcmp(option->name, name) == 0) + return option; + } + + // not found + SET_ERROR_STR(err, &config_errors, ERR_CONFIG_NAME, name); + + return NULL; +} + +int config_params_count (const struct config_option *option) +{ + const struct config_param *param; + int count = 0; + + // handle each param + for (param = option->params; param->name && param->type; param++) + count++; + + return count; +} + +/** + * Parse a raw value for a CONFIG_IRC_CHAN into an irc_chan + */ +static struct irc_chan* config_parse_irc_chan (struct nexus *nexus, char *raw_value, error_t *err) +{ + const char *network, *channel; + struct irc_chan *chan; + + // XXX: wrong error code + + // parse required args + if ((network = strsep(&raw_value, "/")) == NULL) + JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "invalid for CONFIG_IRC_CHAN value"); + + if ((channel = strsep(&raw_value, "/")) == NULL) + JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "invalid for CONFIG_IRC_CHAN value"); + + // extraneous stuff? + if (raw_value) + JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "trailing data for CONFIG_IRC_CHAN value"); + + // get the channel? + if ((chan = irc_client_get_chan(nexus->client, network, channel)) == NULL) + JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "unknown network/channel name for CONFIG_IRC_CHAN value"); + + // ok + return chan; + +error: + return NULL; +} + +err_t config_parse_param (const struct config_param *param, struct nexus *nexus, struct config_value *value, char *raw_value, error_t *err) +{ + // parse the value + switch (param->type) { + case CONFIG_INVALID: + return SET_ERROR_STR(err, &config_errors, ERR_CONFIG_TYPE, "invalid value for invalid type (too many values?)"); + + case CONFIG_STRING: + // simple! + value->string = raw_value; + break; + + case CONFIG_IRC_CHAN: + // parse the value + if (!(value->irc_chan = config_parse_irc_chan(nexus, raw_value, err))) + return error_code(err); + + break; + + case CONFIG_USER: + // fail + return SET_ERROR_STR(err, &config_errors, ERR_CONFIG_TYPE, "user type can't be parsed"); + + default: + NOT_REACHED(param->type); + } + + // copy the type + value->type = param->type; + + // ok + return SUCCESS; +} + +err_t config_parse (const struct config_option *option, struct nexus *nexus, struct config_value *value, char *raw_value, error_t *err) +{ + // use the first param + const struct config_param *param = &option->params[0]; + + // must have exactly one param + if (!param->type || (param + 1)->type) + return SET_ERROR(err, &config_errors, ERR_CONFIG_PARAMS); + + // parse it + return config_parse_param(param, nexus, value, raw_value, err); +} + +err_t config_apply_opt (const struct config_option *option, void *ctx, const struct config_value values[], error_t *err) +{ + const struct config_param *param; + const struct config_value *value; + + // handle each param + for (param = option->params, value = values; param->name && param->type; param++) { + // no value given, or value given as NULL? + if (!value->type || value->type == CONFIG_NULL) { + if (param->optional) { + // optional, so just ignore the value + + } else { + // required + JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_REQUIRED, param->name); + + } + + } else if (param->type != value->type) { + // invalid type, XXX: also report correct type name? + JUMP_SET_ERROR(err, &config_errors, ERR_CONFIG_TYPE); + + } else if (param->is_handler) { + // only applicable for non-optional args + err_t tmp; + + // invoke the handler + switch (param->type) { + case CONFIG_STRING: tmp = param->func.string(ctx, value->string, err); break; + case CONFIG_IRC_CHAN: tmp = param->func.irc_chan(ctx, value->irc_chan, err); break; + default: NOT_REACHED(param->type); + } + + // abort on errors + if (tmp) + goto error; + } + + // the values list is NULL-terminated, and optional params can be left off + if (value->type) + value++; + } + + // the option's handler? + if (option->func && option->func(ctx, option, values, err)) + goto error; + + // ok + return SUCCESS; + +error: + return error_code(err); +} + +err_t config_apply (const struct config_option *options, void *ctx, const char *name, const struct config_value values[], error_t *err) +{ + const struct config_option *option; + + // no matching option found? + if (!(option = config_lookup(options, name, err))) + return error_code(err); + + // apply it + return config_apply_opt(option, ctx, values, err); +} + +err_t config_apply_string (const struct config_option *options, void *ctx, const char *name, char *value, error_t *err) +{ + struct config_value conf_value[] = { + CONFIG_VALUE_STRING(value), + CONFIG_VALUE_END, + }; + + return config_apply(options, ctx, name, conf_value, err); +} + +err_t config_apply_irc_chan (const struct config_option *options, void *ctx, const char *name, struct irc_chan *value, error_t *err) +{ + struct config_value conf_value[] = { + CONFIG_VALUE_IRC_CHAN(value), + CONFIG_VALUE_END, + }; + + return config_apply(options, ctx, name, conf_value, err); +} + +err_t config_apply_raw (const struct config_option options[], struct nexus *nexus, void *ctx, const char *name, char *raw_value, error_t *err) +{ + const struct config_option *option; + struct config_value value[2] = { + CONFIG_VALUE_END, + CONFIG_VALUE_END, + }; + + // no matching option found? + if (!(option = config_lookup(options, name, err))) + return error_code(err); + + // parse it + if (config_parse(option, nexus, &value[0], raw_value, err)) + return error_code(err); + + // apply it + return config_apply_opt(option, ctx, value, err); +} + +const struct config_value* config_get_value (const struct config_option *option, const struct config_value values[], const char *name) +{ + const struct config_param *param; + const struct config_value *value; + + // go through the (param, value) pairs + for (param = option->params, value = values; param->name && param->type && value->type; param++, value++) { + if (strcmp(param->name, name) == 0) { + // not NULL? + if (value->type != CONFIG_NULL) + return value; + + else + return NULL; + } + } + + // not found + return NULL; +} + +const char* config_get_string (const struct config_option *option, const struct config_value values[], const char *name) +{ + const struct config_value *value; + + return (value = config_get_value(option, values, name)) ? value->string : NULL; +} + +struct irc_chan* config_get_irc_chan (const struct config_option *option, const struct config_value values[], const char *name) +{ + const struct config_value *value; + + return (value = config_get_value(option, values, name)) ? value->irc_chan : NULL; +} + +void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const struct config_user_type *user_type) +{ + const struct config_value *value; + + return ((value = config_get_value(option, values, name)) && value->user.type == user_type) ? value->user.ptr : NULL; +} +