semi-ported to msys2
[laserbrain_demo] / src / optcfg.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #if defined(WIN32) || defined(__WIN32__)
6 #include <malloc.h>
7 #else
8 #include <alloca.h>
9 #endif
10 #include "optcfg.h"
11
12 struct optcfg {
13         struct optcfg_option *optlist;
14
15         optcfg_opt_callback opt_func;
16         void *opt_cls;
17         optcfg_arg_callback arg_func;
18         void *arg_cls;
19
20         int err_abort;
21
22         /* argument parsing state */
23         char **argv;
24         int argidx;
25         int disable_opt;
26
27         /* config file parsing state */
28         const char *cfg_fname;
29         int cfg_nline;
30         char *cfg_value;
31 };
32
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);
38
39
40 struct optcfg *optcfg_init(struct optcfg_option *optv)
41 {
42         struct optcfg *oc;
43
44         if(!(oc = calloc(1, sizeof *oc))) {
45                 return 0;
46         }
47         oc->optlist = optv;
48         oc->err_abort = 1;
49         return oc;
50 }
51
52 void optcfg_destroy(struct optcfg *oc)
53 {
54         memset(oc, 0, sizeof *oc);
55         free(oc);
56 }
57
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 }
63
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 }
69
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 }
78
79 int optcfg_parse_args(struct optcfg *oc, int argc, char **argv)
80 {
81         int i;
82
83         oc->argv = argv;
84
85         for(i=1; i<argc; i++) {
86                 oc->argidx = i;
87
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                 }
118
119                 i = oc->argidx;
120         }
121
122         oc->argidx = 0; /* done parsing args */
123         return 0;
124 }
125
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         }
133
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 }
140
141 int optcfg_parse_config_stream(struct optcfg *oc, FILE *fp)
142 {
143         char buf[512];
144
145         oc->cfg_nline = 0;
146         while(fgets(buf, sizeof buf, fp)) {
147                 ++oc->cfg_nline;
148
149                 if(optcfg_parse_config_line(oc, buf) == -1) {
150                         if(oc->err_abort) {
151                                 return -1;
152                         }
153                 }
154         }
155         return 0;
156 }
157
158 int optcfg_parse_config_line(struct optcfg *oc, const char *line)
159 {
160         int opt, len;
161         char *start, *val, *buf;
162
163         len = strlen(line);
164         buf = alloca(len + 1);
165         memcpy(buf, line, len + 1);
166
167         start = skip_spaces(buf);
168         strip_comments(start);
169         strip_trailing_spaces(start);
170         if(!*start) {
171                 return 0;
172         }
173
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         }
184
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 }
193
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 }
206
207
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 }
220
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;
226
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 }
240
241 #ifdef _MSC_VER
242 #define strcasecmp stricmp
243 #endif
244
245 int optcfg_bool_value(char *s, int *valret)
246 {
247         if(strcasecmp(s, "yes") == 0 || strcasecmp(s, "true") == 0 || strcmp(s, "1") == 0) {
248                 *valret = 1;
249                 return 0;
250         }
251         if(strcasecmp(s, "no") == 0 || strcasecmp(s, "false") == 0 || strcmp(s, "0") == 0) {
252                 *valret = 0;
253                 return 0;
254         }
255         return -1;
256 }
257
258 int optcfg_int_value(char *str, int *valret)
259 {
260         char *endp;
261         *valret = strtol(str, &endp, 0);
262         if(endp == str) {
263                 return -1;
264         }
265         return 0;
266 }
267
268 int optcfg_float_value(char *str, float *valret)
269 {
270         char *endp;
271         *valret = strtod(str, &endp);
272         if(endp == str) {
273                 return -1;
274         }
275         return 0;
276 }
277
278
279
280 static int get_opt(struct optcfg *oc, const char *arg, int *disable_opt)
281 {
282         int i, ndashes = 0;
283
284         while(*arg && *arg == '-') {
285                 ++ndashes;
286                 ++arg;
287         }
288
289         if(ndashes > 2) {
290                 arg -= ndashes;
291                 ndashes = 0;
292         }
293
294         if(disable_opt) {
295                 if(ndashes && (strstr(arg, "no-") == arg || strstr(arg, "disable-") == arg)) {
296                         *disable_opt = 1;
297                         arg = strchr(arg, '-') + 1;     /* guaranteed to exist at this point */
298                 } else {
299                         *disable_opt = 0;
300                 }
301         }
302
303         if(arg[1]) {    /* match long options */
304                 for(i=0; oc->optlist[i].opt != -1; i++) {
305                         if(strcmp(arg, oc->optlist[i].s) == 0) {
306                                 return i;
307                         }
308                 }
309         } else {
310                 for(i=0; oc->optlist[i].opt != -1; i++) {
311                         if(arg[0] == oc->optlist[i].c) {
312                                 return i;
313                         }
314                 }
315         }
316         return -1;
317 }
318
319 static char *skip_spaces(char *s)
320 {
321         while(*s && isspace(*s)) ++s;
322         return s;
323 }
324
325 static void strip_comments(char *s)
326 {
327         while(*s && *s != '#') ++s;
328         if(*s == '#') *s = 0;
329 }
330
331 static void strip_trailing_spaces(char *s)
332 {
333         char *end = s + strlen(s) - 1;
334         while(end >= s && isspace(*end)) {
335                 *end-- = 0;
336         }
337 }
338
339 static char *parse_keyval(char *line)
340 {
341         char *val;
342         char *eq = strchr(line, '=');
343         if(!eq) return 0;
344
345         *eq = 0;
346         strip_trailing_spaces(line);
347         val = skip_spaces(eq + 1);
348         strip_trailing_spaces(val);
349
350         if(!*line || !*val) {
351                 return 0;
352         }
353         return val;
354 }