liboptcfg

view src/optcfg.c @ 4:a6f127f3408d

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