#include "config.h"
#include <string.h>
#include <assert.h>
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 <network> for CONFIG_IRC_CHAN value");
if ((channel = strsep(&raw_value, "/")) == NULL)
JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "invalid <channel> 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;
}