|
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 |