src/spbot/config.c
branchnew-lib-errors
changeset 217 7728d6ec3abf
parent 158 b5a5df4f4421
equal deleted inserted replaced
216:a10ba529ae39 217:7728d6ec3abf
       
     1 #include "config.h"
       
     2 
       
     3 #include <string.h>
       
     4 #include <assert.h>
       
     5 
       
     6 const struct error_list config_errors = ERROR_LIST("config",
       
     7     ERROR_TYPE_STRING(  ERR_CONFIG_NAME,        "unknown config option"         ),
       
     8     ERROR_TYPE(         ERR_CONFIG_TYPE,        "invalid config type"           ),
       
     9     ERROR_TYPE_STRING(  ERR_CONFIG_REQUIRED,    "missing required value"        ),
       
    10     ERROR_TYPE_STRING(  ERR_CONFIG_VALUE,       "invalid value"                 ),
       
    11     ERROR_TYPE(         ERR_CONFIG_PARAMS,      "invalid number of paramters"   )
       
    12 );
       
    13 
       
    14 const struct config_option* config_lookup (const struct config_option *options, const char *name, error_t *err)
       
    15 {
       
    16     const struct config_option *option;
       
    17 
       
    18     // find the matching config opt
       
    19     for (option = options; option->name; option++) {
       
    20         if (strcmp(option->name, name) == 0)
       
    21             return option;
       
    22     }
       
    23 
       
    24     // not found
       
    25     SET_ERROR_STR(err, &config_errors, ERR_CONFIG_NAME, name);
       
    26 
       
    27     return NULL;
       
    28 }
       
    29 
       
    30 int config_params_count (const struct config_option *option)
       
    31 {
       
    32     const struct config_param *param;
       
    33     int count = 0;
       
    34 
       
    35     // handle each param
       
    36     for (param = option->params; param->name && param->type; param++)
       
    37         count++;
       
    38 
       
    39     return count;
       
    40 }
       
    41 
       
    42 /**
       
    43  * Parse a raw value for a CONFIG_IRC_CHAN into an irc_chan
       
    44  */
       
    45 static struct irc_chan* config_parse_irc_chan (struct nexus *nexus, char *raw_value, error_t *err)
       
    46 {
       
    47     const char *network, *channel;
       
    48     struct irc_chan *chan;
       
    49 
       
    50     // XXX: wrong error code
       
    51 
       
    52     // parse required args
       
    53     if ((network = strsep(&raw_value, "/")) == NULL)
       
    54         JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "invalid <network> for CONFIG_IRC_CHAN value");
       
    55 
       
    56     if ((channel = strsep(&raw_value, "/")) == NULL)
       
    57         JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "invalid <channel> for CONFIG_IRC_CHAN value");
       
    58     
       
    59     // extraneous stuff?
       
    60     if (raw_value)
       
    61         JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "trailing data for CONFIG_IRC_CHAN value");
       
    62     
       
    63     // get the channel?
       
    64     if ((chan = irc_client_get_chan(nexus->client, network, channel)) == NULL)
       
    65         JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "unknown network/channel name for CONFIG_IRC_CHAN value");
       
    66     
       
    67     // ok
       
    68     return chan;
       
    69 
       
    70 error:
       
    71     return NULL;    
       
    72 }
       
    73 
       
    74 err_t config_parse_param (const struct config_param *param, struct nexus *nexus, struct config_value *value, char *raw_value, error_t *err)
       
    75 {
       
    76     // parse the value 
       
    77     switch (param->type) {
       
    78         case CONFIG_INVALID:
       
    79             return SET_ERROR_STR(err, &config_errors, ERR_CONFIG_TYPE, "invalid value for invalid type (too many values?)");
       
    80 
       
    81         case CONFIG_STRING:
       
    82             // simple!
       
    83             value->string = raw_value;
       
    84             break;
       
    85 
       
    86         case CONFIG_IRC_CHAN:
       
    87             // parse the value
       
    88             if (!(value->irc_chan = config_parse_irc_chan(nexus, raw_value, err)))
       
    89                 return error_code(err);
       
    90 
       
    91             break;
       
    92         
       
    93         case CONFIG_USER:
       
    94             // fail
       
    95             return SET_ERROR_STR(err, &config_errors, ERR_CONFIG_TYPE, "user type can't be parsed");
       
    96 
       
    97         default:
       
    98             NOT_REACHED(param->type);
       
    99     }
       
   100 
       
   101     // copy the type
       
   102     value->type = param->type;
       
   103 
       
   104     // ok
       
   105     return SUCCESS; 
       
   106 }
       
   107 
       
   108 err_t config_parse (const struct config_option *option, struct nexus *nexus, struct config_value *value, char *raw_value, error_t *err)
       
   109 {
       
   110     // use the first param
       
   111     const struct config_param *param = &option->params[0];
       
   112 
       
   113     // must have exactly one param
       
   114     if (!param->type || (param + 1)->type)
       
   115         return SET_ERROR(err, &config_errors, ERR_CONFIG_PARAMS);
       
   116     
       
   117     // parse it
       
   118     return config_parse_param(param, nexus, value, raw_value, err);
       
   119 }
       
   120 
       
   121 err_t config_apply_opt (const struct config_option *option, void *ctx, const struct config_value values[], error_t *err)
       
   122 {
       
   123     const struct config_param *param;
       
   124     const struct config_value *value;
       
   125 
       
   126     // handle each param
       
   127     for (param = option->params, value = values; param->name && param->type; param++) {
       
   128         // no value given, or value given as NULL?
       
   129         if (!value->type || value->type == CONFIG_NULL) {
       
   130             if (param->optional) {
       
   131                 // optional, so just ignore the value 
       
   132                 
       
   133             } else {
       
   134                 // required
       
   135                 JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_REQUIRED, param->name);
       
   136 
       
   137             }
       
   138 
       
   139         } else if (param->type != value->type) {
       
   140             // invalid type, XXX: also report correct type name?
       
   141             JUMP_SET_ERROR(err, &config_errors, ERR_CONFIG_TYPE);
       
   142 
       
   143         } else if (param->is_handler) {
       
   144             // only applicable for non-optional args
       
   145             err_t tmp;
       
   146             
       
   147             // invoke the handler
       
   148             switch (param->type) {
       
   149                 case CONFIG_STRING:     tmp = param->func.string(ctx, value->string, err); break;
       
   150                 case CONFIG_IRC_CHAN:   tmp = param->func.irc_chan(ctx, value->irc_chan, err); break;
       
   151                 default:                NOT_REACHED(param->type);
       
   152             }
       
   153             
       
   154             // abort on errors
       
   155             if (tmp)
       
   156                 goto error;
       
   157         }
       
   158         
       
   159         // the values list is NULL-terminated, and optional params can be left off
       
   160         if (value->type)
       
   161             value++;
       
   162     }
       
   163 
       
   164     // the option's handler?
       
   165     if (option->func && option->func(ctx, option, values, err))
       
   166         goto error;
       
   167 
       
   168     // ok
       
   169     return SUCCESS;
       
   170 
       
   171 error:
       
   172     return error_code(err);
       
   173 }
       
   174 
       
   175 err_t config_apply (const struct config_option *options, void *ctx, const char *name, const struct config_value values[], error_t *err)
       
   176 {
       
   177     const struct config_option *option;
       
   178 
       
   179     // no matching option found?
       
   180     if (!(option = config_lookup(options, name, err)))
       
   181         return error_code(err);
       
   182     
       
   183     // apply it    
       
   184     return config_apply_opt(option, ctx, values, err);
       
   185 }
       
   186 
       
   187 err_t config_apply_string (const struct config_option *options, void *ctx, const char *name, char *value, error_t *err)
       
   188 {
       
   189     struct config_value conf_value[] = {
       
   190         CONFIG_VALUE_STRING(value),
       
   191         CONFIG_VALUE_END,
       
   192     };
       
   193 
       
   194     return config_apply(options, ctx, name, conf_value, err);
       
   195 }
       
   196 
       
   197 err_t config_apply_irc_chan (const struct config_option *options, void *ctx, const char *name, struct irc_chan *value, error_t *err)
       
   198 {
       
   199     struct config_value conf_value[] = {
       
   200         CONFIG_VALUE_IRC_CHAN(value),
       
   201         CONFIG_VALUE_END,
       
   202     };
       
   203 
       
   204     return config_apply(options, ctx, name, conf_value, err);
       
   205 }
       
   206 
       
   207 err_t config_apply_raw (const struct config_option options[], struct nexus *nexus, void *ctx, const char *name, char *raw_value, error_t *err)
       
   208 {
       
   209     const struct config_option *option;
       
   210     struct config_value value[2] = {
       
   211         CONFIG_VALUE_END,
       
   212         CONFIG_VALUE_END,
       
   213     };
       
   214 
       
   215     // no matching option found?
       
   216     if (!(option = config_lookup(options, name, err)))
       
   217         return error_code(err);
       
   218     
       
   219     // parse it
       
   220     if (config_parse(option, nexus, &value[0], raw_value, err))
       
   221         return error_code(err);
       
   222     
       
   223     // apply it
       
   224     return config_apply_opt(option, ctx, value, err);
       
   225 }
       
   226 
       
   227 const struct config_value* config_get_value (const struct config_option *option, const struct config_value values[], const char *name)
       
   228 {
       
   229     const struct config_param *param;
       
   230     const struct config_value *value;
       
   231     
       
   232     // go through the (param, value) pairs
       
   233     for (param = option->params, value = values; param->name && param->type && value->type; param++, value++) {
       
   234         if (strcmp(param->name, name) == 0) {
       
   235             // not NULL?
       
   236             if (value->type != CONFIG_NULL)
       
   237                 return value;
       
   238 
       
   239             else
       
   240                 return NULL;
       
   241         }
       
   242     }
       
   243 
       
   244     // not found
       
   245     return NULL;
       
   246 }
       
   247 
       
   248 const char* config_get_string (const struct config_option *option, const struct config_value values[], const char *name)
       
   249 {
       
   250     const struct config_value *value;
       
   251 
       
   252     return (value = config_get_value(option, values, name)) ? value->string : NULL;
       
   253 }
       
   254 
       
   255 struct irc_chan* config_get_irc_chan (const struct config_option *option, const struct config_value values[], const char *name)
       
   256 {
       
   257     const struct config_value *value;
       
   258 
       
   259     return (value = config_get_value(option, values, name)) ? value->irc_chan : NULL;
       
   260 }
       
   261 
       
   262 void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const struct config_user_type *user_type)
       
   263 {
       
   264     const struct config_value *value;
       
   265 
       
   266     return ((value = config_get_value(option, values, name)) && value->user.type == user_type) ? value->user.ptr : NULL;
       
   267 }
       
   268