rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <stdlib.h>
|
nuclear@0
|
3 #include <string.h>
|
nuclear@0
|
4 #include <ctype.h>
|
nuclear@5
|
5 #ifdef WIN32
|
nuclear@0
|
6 #include <malloc.h>
|
nuclear@0
|
7 #else
|
nuclear@0
|
8 #include <alloca.h>
|
nuclear@0
|
9 #endif
|
nuclear@0
|
10 #include "optcfg.h"
|
nuclear@0
|
11
|
nuclear@0
|
12 struct optcfg {
|
nuclear@0
|
13 struct optcfg_option *optlist;
|
nuclear@0
|
14
|
nuclear@0
|
15 optcfg_opt_callback opt_func;
|
nuclear@0
|
16 void *opt_cls;
|
nuclear@0
|
17 optcfg_arg_callback arg_func;
|
nuclear@0
|
18 void *arg_cls;
|
nuclear@0
|
19
|
nuclear@0
|
20 int err_abort;
|
nuclear@0
|
21
|
nuclear@0
|
22 /* argument parsing state */
|
nuclear@0
|
23 char **argv;
|
nuclear@0
|
24 int argidx;
|
nuclear@4
|
25 int disable_opt;
|
nuclear@0
|
26
|
nuclear@0
|
27 /* config file parsing state */
|
nuclear@0
|
28 const char *cfg_fname;
|
nuclear@0
|
29 int cfg_nline;
|
nuclear@0
|
30 char *cfg_value;
|
nuclear@0
|
31 };
|
nuclear@0
|
32
|
nuclear@4
|
33 static int get_opt(struct optcfg *oc, const char *s, int *disable_opt);
|
nuclear@0
|
34 static char *skip_spaces(char *s);
|
nuclear@0
|
35 static void strip_comments(char *s);
|
nuclear@0
|
36 static void strip_trailing_spaces(char *s);
|
nuclear@0
|
37 static char *parse_keyval(char *line);
|
nuclear@0
|
38
|
nuclear@0
|
39
|
nuclear@0
|
40 struct optcfg *optcfg_init(struct optcfg_option *optv)
|
nuclear@0
|
41 {
|
nuclear@0
|
42 struct optcfg *oc;
|
nuclear@0
|
43
|
nuclear@0
|
44 if(!(oc = calloc(1, sizeof *oc))) {
|
nuclear@0
|
45 return 0;
|
nuclear@0
|
46 }
|
nuclear@0
|
47 oc->optlist = optv;
|
nuclear@0
|
48 oc->err_abort = 1;
|
nuclear@0
|
49 return oc;
|
nuclear@0
|
50 }
|
nuclear@0
|
51
|
nuclear@0
|
52 void optcfg_destroy(struct optcfg *oc)
|
nuclear@0
|
53 {
|
nuclear@0
|
54 memset(oc, 0, sizeof *oc);
|
nuclear@0
|
55 free(oc);
|
nuclear@0
|
56 }
|
nuclear@0
|
57
|
nuclear@0
|
58 void optcfg_set_opt_callback(struct optcfg *oc, optcfg_opt_callback func, void *cls)
|
nuclear@0
|
59 {
|
nuclear@0
|
60 oc->opt_func = func;
|
nuclear@0
|
61 oc->opt_cls = cls;
|
nuclear@0
|
62 }
|
nuclear@0
|
63
|
nuclear@0
|
64 void optcfg_set_arg_callback(struct optcfg *oc, optcfg_arg_callback func, void *cls)
|
nuclear@0
|
65 {
|
nuclear@0
|
66 oc->arg_func = func;
|
nuclear@0
|
67 oc->arg_cls = cls;
|
nuclear@0
|
68 }
|
nuclear@0
|
69
|
nuclear@0
|
70 void optcfg_set_error_action(struct optcfg *oc, int act)
|
nuclear@0
|
71 {
|
nuclear@0
|
72 if(act == OPTCFG_ERROR_FAIL) {
|
nuclear@0
|
73 oc->err_abort = 1;
|
nuclear@0
|
74 } else if(act == OPTCFG_ERROR_IGNORE) {
|
nuclear@0
|
75 oc->err_abort = 0;
|
nuclear@0
|
76 }
|
nuclear@0
|
77 }
|
nuclear@0
|
78
|
nuclear@0
|
79 int optcfg_parse_args(struct optcfg *oc, int argc, char **argv)
|
nuclear@0
|
80 {
|
nuclear@0
|
81 int i;
|
nuclear@0
|
82
|
nuclear@0
|
83 oc->argv = argv;
|
nuclear@0
|
84
|
nuclear@0
|
85 for(i=1; i<argc; i++) {
|
nuclear@0
|
86 oc->argidx = i;
|
nuclear@0
|
87
|
nuclear@0
|
88 if(argv[i][0] == '-') {
|
nuclear@0
|
89 if(oc->opt_func) {
|
nuclear@4
|
90 int o = get_opt(oc, argv[i], &oc->disable_opt);
|
nuclear@0
|
91 if(o == -1 || oc->opt_func(oc, o, oc->opt_cls) == -1) {
|
nuclear@0
|
92 if(oc->err_abort) {
|
nuclear@4
|
93 fprintf(stderr, "unexpected option: %s\n", argv[i]);
|
nuclear@0
|
94 return -1;
|
nuclear@0
|
95 }
|
nuclear@0
|
96 }
|
nuclear@0
|
97 } else {
|
nuclear@0
|
98 fprintf(stderr, "unexpected option: %s\n", argv[i]);
|
nuclear@0
|
99 if(oc->err_abort) {
|
nuclear@0
|
100 return -1;
|
nuclear@0
|
101 }
|
nuclear@0
|
102 }
|
nuclear@0
|
103 } else {
|
nuclear@0
|
104 if(oc->arg_func) {
|
nuclear@0
|
105 if(oc->arg_func(oc, argv[i], oc->arg_cls) == -1) {
|
nuclear@0
|
106 if(oc->err_abort) {
|
nuclear@4
|
107 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
|
nuclear@0
|
108 return -1;
|
nuclear@0
|
109 }
|
nuclear@0
|
110 }
|
nuclear@0
|
111 } else {
|
nuclear@0
|
112 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
|
nuclear@0
|
113 if(oc->err_abort) {
|
nuclear@0
|
114 return -1;
|
nuclear@0
|
115 }
|
nuclear@0
|
116 }
|
nuclear@0
|
117 }
|
nuclear@0
|
118
|
nuclear@0
|
119 i = oc->argidx;
|
nuclear@0
|
120 }
|
nuclear@0
|
121
|
nuclear@0
|
122 oc->argidx = 0; /* done parsing args */
|
nuclear@0
|
123 return 0;
|
nuclear@0
|
124 }
|
nuclear@0
|
125
|
nuclear@0
|
126 int optcfg_parse_config_file(struct optcfg *oc, const char *fname)
|
nuclear@0
|
127 {
|
nuclear@0
|
128 int res;
|
nuclear@0
|
129 FILE *fp = fopen(fname, "rb");
|
nuclear@0
|
130 if(!fp) {
|
nuclear@0
|
131 return -1;
|
nuclear@0
|
132 }
|
nuclear@0
|
133
|
nuclear@0
|
134 oc->cfg_fname = fname;
|
nuclear@0
|
135 res = optcfg_parse_config_stream(oc, fp);
|
nuclear@0
|
136 oc->cfg_fname = 0;
|
nuclear@0
|
137 fclose(fp);
|
nuclear@0
|
138 return res;
|
nuclear@0
|
139 }
|
nuclear@0
|
140
|
nuclear@0
|
141 int optcfg_parse_config_stream(struct optcfg *oc, FILE *fp)
|
nuclear@0
|
142 {
|
nuclear@0
|
143 char buf[512];
|
nuclear@0
|
144
|
nuclear@0
|
145 oc->cfg_nline = 0;
|
nuclear@0
|
146 while(fgets(buf, sizeof buf, fp)) {
|
nuclear@0
|
147 ++oc->cfg_nline;
|
nuclear@0
|
148
|
nuclear@0
|
149 if(optcfg_parse_config_line(oc, buf) == -1) {
|
nuclear@0
|
150 if(oc->err_abort) {
|
nuclear@0
|
151 return -1;
|
nuclear@0
|
152 }
|
nuclear@0
|
153 }
|
nuclear@0
|
154 }
|
nuclear@0
|
155 return 0;
|
nuclear@0
|
156 }
|
nuclear@0
|
157
|
nuclear@0
|
158 int optcfg_parse_config_line(struct optcfg *oc, const char *line)
|
nuclear@0
|
159 {
|
nuclear@0
|
160 int opt, len;
|
nuclear@0
|
161 char *start, *val, *buf;
|
nuclear@0
|
162
|
nuclear@0
|
163 len = strlen(line);
|
nuclear@0
|
164 buf = alloca(len + 1);
|
nuclear@2
|
165 memcpy(buf, line, len + 1);
|
nuclear@0
|
166
|
nuclear@0
|
167 start = skip_spaces(buf);
|
nuclear@0
|
168 strip_comments(start);
|
nuclear@0
|
169 strip_trailing_spaces(start);
|
nuclear@0
|
170 if(!*start) {
|
nuclear@0
|
171 return 0;
|
nuclear@0
|
172 }
|
nuclear@0
|
173
|
nuclear@0
|
174 if(!(val = parse_keyval(start))) {
|
nuclear@0
|
175 fprintf(stderr, "error parsing %s line %d: invalid syntax\n", oc->cfg_fname ? oc->cfg_fname : "", oc->cfg_nline);
|
nuclear@0
|
176 return -1;
|
nuclear@0
|
177 }
|
nuclear@0
|
178 oc->cfg_value = val;
|
nuclear@4
|
179 if((opt = get_opt(oc, start, 0)) == -1) {
|
nuclear@0
|
180 fprintf(stderr, "error parsing %s line %d: unknown option: %s\n", oc->cfg_fname ? oc->cfg_fname : "",
|
nuclear@0
|
181 oc->cfg_nline, start);
|
nuclear@0
|
182 return -1;
|
nuclear@0
|
183 }
|
nuclear@0
|
184
|
nuclear@0
|
185 if(oc->opt_func) {
|
nuclear@0
|
186 if(oc->opt_func(oc, opt, oc->opt_cls) == -1) {
|
nuclear@0
|
187 return -1;
|
nuclear@0
|
188 }
|
nuclear@0
|
189 }
|
nuclear@0
|
190 oc->cfg_value = 0;
|
nuclear@0
|
191 return 0;
|
nuclear@0
|
192 }
|
nuclear@0
|
193
|
nuclear@2
|
194 int optcfg_enabled_value(struct optcfg *oc, int *enabledp)
|
nuclear@2
|
195 {
|
nuclear@2
|
196 if(oc->argidx) {
|
nuclear@4
|
197 *enabledp = ~oc->disable_opt & 1;
|
nuclear@2
|
198 } else {
|
nuclear@2
|
199 char *val = optcfg_next_value(oc);
|
nuclear@2
|
200 if(optcfg_bool_value(val, enabledp) == -1) {
|
nuclear@2
|
201 return -1;
|
nuclear@2
|
202 }
|
nuclear@2
|
203 }
|
nuclear@2
|
204 return 0;
|
nuclear@2
|
205 }
|
nuclear@2
|
206
|
nuclear@2
|
207
|
nuclear@0
|
208 char *optcfg_next_value(struct optcfg *oc)
|
nuclear@0
|
209 {
|
nuclear@0
|
210 if(oc->argidx) { /* we're in the middle of parsing arguments, so get the next one */
|
nuclear@0
|
211 return oc->argv[++oc->argidx];
|
nuclear@0
|
212 }
|
nuclear@0
|
213 if(oc->cfg_value) {
|
nuclear@0
|
214 char *val = oc->cfg_value;
|
nuclear@0
|
215 oc->cfg_value = 0;
|
nuclear@0
|
216 return val;
|
nuclear@0
|
217 }
|
nuclear@0
|
218 return 0;
|
nuclear@0
|
219 }
|
nuclear@0
|
220
|
nuclear@0
|
221 void optcfg_print_options(struct optcfg *oc)
|
nuclear@0
|
222 {
|
nuclear@0
|
223 int i;
|
nuclear@0
|
224 for(i=0; oc->optlist[i].opt != -1; i++) {
|
nuclear@0
|
225 struct optcfg_option *opt = oc->optlist + i;
|
nuclear@0
|
226
|
nuclear@0
|
227 if(opt->c) {
|
nuclear@0
|
228 printf(" -%c", opt->c);
|
nuclear@0
|
229 } else {
|
nuclear@0
|
230 printf(" ");
|
nuclear@0
|
231 }
|
nuclear@0
|
232 if(opt->s) {
|
nuclear@0
|
233 printf("%c-%s: ", opt->c ? ',' : ' ', opt->s);
|
nuclear@0
|
234 } else {
|
nuclear@0
|
235 printf(": ");
|
nuclear@0
|
236 }
|
nuclear@0
|
237 printf("%s\n", opt->desc ? opt->desc : "undocumented");
|
nuclear@0
|
238 }
|
nuclear@0
|
239 }
|
nuclear@0
|
240
|
nuclear@0
|
241 int optcfg_bool_value(char *s, int *valret)
|
nuclear@0
|
242 {
|
nuclear@0
|
243 if(strcasecmp(s, "yes") == 0 || strcasecmp(s, "true") == 0 || strcmp(s, "1") == 0) {
|
nuclear@0
|
244 *valret = 1;
|
nuclear@0
|
245 return 0;
|
nuclear@0
|
246 }
|
nuclear@0
|
247 if(strcasecmp(s, "no") == 0 || strcasecmp(s, "false") == 0 || strcmp(s, "0") == 0) {
|
nuclear@0
|
248 *valret = 0;
|
nuclear@0
|
249 return 0;
|
nuclear@0
|
250 }
|
nuclear@0
|
251 return -1;
|
nuclear@0
|
252 }
|
nuclear@0
|
253
|
nuclear@0
|
254 int optcfg_int_value(char *str, int *valret)
|
nuclear@0
|
255 {
|
nuclear@0
|
256 char *endp;
|
nuclear@0
|
257 *valret = strtol(str, &endp, 0);
|
nuclear@0
|
258 if(endp == str) {
|
nuclear@0
|
259 return -1;
|
nuclear@0
|
260 }
|
nuclear@0
|
261 return 0;
|
nuclear@0
|
262 }
|
nuclear@0
|
263
|
nuclear@0
|
264 int optcfg_float_value(char *str, float *valret)
|
nuclear@0
|
265 {
|
nuclear@0
|
266 char *endp;
|
nuclear@0
|
267 *valret = strtod(str, &endp);
|
nuclear@0
|
268 if(endp == str) {
|
nuclear@0
|
269 return -1;
|
nuclear@0
|
270 }
|
nuclear@0
|
271 return 0;
|
nuclear@0
|
272 }
|
nuclear@0
|
273
|
nuclear@0
|
274
|
nuclear@0
|
275
|
nuclear@4
|
276 static int get_opt(struct optcfg *oc, const char *arg, int *disable_opt)
|
nuclear@0
|
277 {
|
nuclear@1
|
278 int i, ndashes = 0;
|
nuclear@0
|
279
|
nuclear@1
|
280 while(*arg && *arg == '-') {
|
nuclear@4
|
281 ++ndashes;
|
nuclear@4
|
282 ++arg;
|
nuclear@0
|
283 }
|
nuclear@0
|
284
|
nuclear@1
|
285 if(ndashes > 2) {
|
nuclear@1
|
286 arg -= ndashes;
|
nuclear@4
|
287 ndashes = 0;
|
nuclear@4
|
288 }
|
nuclear@4
|
289
|
nuclear@4
|
290 if(disable_opt) {
|
nuclear@4
|
291 if(ndashes && (strstr(arg, "no-") == arg || strstr(arg, "disable-") == arg)) {
|
nuclear@4
|
292 *disable_opt = 1;
|
nuclear@4
|
293 arg = strchr(arg, '-') + 1; /* guaranteed to exist at this point */
|
nuclear@4
|
294 } else {
|
nuclear@4
|
295 *disable_opt = 0;
|
nuclear@4
|
296 }
|
nuclear@1
|
297 }
|
nuclear@1
|
298
|
nuclear@1
|
299 if(arg[1]) { /* match long options */
|
nuclear@0
|
300 for(i=0; oc->optlist[i].opt != -1; i++) {
|
nuclear@1
|
301 if(strcmp(arg, oc->optlist[i].s) == 0) {
|
nuclear@0
|
302 return i;
|
nuclear@0
|
303 }
|
nuclear@0
|
304 }
|
nuclear@0
|
305 } else {
|
nuclear@0
|
306 for(i=0; oc->optlist[i].opt != -1; i++) {
|
nuclear@1
|
307 if(arg[0] == oc->optlist[i].c) {
|
nuclear@0
|
308 return i;
|
nuclear@0
|
309 }
|
nuclear@0
|
310 }
|
nuclear@0
|
311 }
|
nuclear@0
|
312 return -1;
|
nuclear@0
|
313 }
|
nuclear@0
|
314
|
nuclear@0
|
315 static char *skip_spaces(char *s)
|
nuclear@0
|
316 {
|
nuclear@0
|
317 while(*s && isspace(*s)) ++s;
|
nuclear@0
|
318 return s;
|
nuclear@0
|
319 }
|
nuclear@0
|
320
|
nuclear@0
|
321 static void strip_comments(char *s)
|
nuclear@0
|
322 {
|
nuclear@0
|
323 while(*s && *s != '#') ++s;
|
nuclear@0
|
324 if(*s == '#') *s = 0;
|
nuclear@0
|
325 }
|
nuclear@0
|
326
|
nuclear@0
|
327 static void strip_trailing_spaces(char *s)
|
nuclear@0
|
328 {
|
nuclear@0
|
329 char *end = s + strlen(s) - 1;
|
nuclear@0
|
330 while(end >= s && isspace(*end)) {
|
nuclear@0
|
331 *end-- = 0;
|
nuclear@0
|
332 }
|
nuclear@0
|
333 }
|
nuclear@0
|
334
|
nuclear@0
|
335 static char *parse_keyval(char *line)
|
nuclear@0
|
336 {
|
nuclear@0
|
337 char *val;
|
nuclear@0
|
338 char *eq = strchr(line, '=');
|
nuclear@0
|
339 if(!eq) return 0;
|
nuclear@0
|
340
|
nuclear@0
|
341 *eq = 0;
|
nuclear@0
|
342 strip_trailing_spaces(line);
|
nuclear@0
|
343 val = skip_spaces(eq + 1);
|
nuclear@2
|
344 strip_trailing_spaces(val);
|
nuclear@0
|
345
|
nuclear@0
|
346 if(!*line || !*val) {
|
nuclear@0
|
347 return 0;
|
nuclear@0
|
348 }
|
nuclear@0
|
349 return val;
|
nuclear@0
|
350 }
|