xglcomp

view src/optcfg/optcfg.c @ 0:d9b3fba68705

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 21 Jan 2016 08:45:31 +0200
parents
children
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;
26 /* config file parsing state */
27 const char *cfg_fname;
28 int cfg_nline;
29 char *cfg_value;
30 };
32 static int get_opt(struct optcfg *oc, const char *s);
33 static char *skip_spaces(char *s);
34 static void strip_comments(char *s);
35 static void strip_trailing_spaces(char *s);
36 static char *parse_keyval(char *line);
39 struct optcfg *optcfg_init(struct optcfg_option *optv)
40 {
41 struct optcfg *oc;
43 if(!(oc = calloc(1, sizeof *oc))) {
44 return 0;
45 }
46 oc->optlist = optv;
47 oc->err_abort = 1;
48 return oc;
49 }
51 void optcfg_destroy(struct optcfg *oc)
52 {
53 memset(oc, 0, sizeof *oc);
54 free(oc);
55 }
57 void optcfg_set_opt_callback(struct optcfg *oc, optcfg_opt_callback func, void *cls)
58 {
59 oc->opt_func = func;
60 oc->opt_cls = cls;
61 }
63 void optcfg_set_arg_callback(struct optcfg *oc, optcfg_arg_callback func, void *cls)
64 {
65 oc->arg_func = func;
66 oc->arg_cls = cls;
67 }
69 void optcfg_set_error_action(struct optcfg *oc, int act)
70 {
71 if(act == OPTCFG_ERROR_FAIL) {
72 oc->err_abort = 1;
73 } else if(act == OPTCFG_ERROR_IGNORE) {
74 oc->err_abort = 0;
75 }
76 }
78 int optcfg_parse_args(struct optcfg *oc, int argc, char **argv)
79 {
80 int i;
82 oc->argv = argv;
84 for(i=1; i<argc; i++) {
85 oc->argidx = i;
87 if(argv[i][0] == '-') {
88 if(oc->opt_func) {
89 int o = get_opt(oc, argv[i]);
90 if(o == -1 || oc->opt_func(oc, o, oc->opt_cls) == -1) {
91 if(oc->err_abort) {
92 return -1;
93 }
94 }
95 } else {
96 fprintf(stderr, "unexpected option: %s\n", argv[i]);
97 if(oc->err_abort) {
98 return -1;
99 }
100 }
101 } else {
102 if(oc->arg_func) {
103 if(oc->arg_func(oc, argv[i], oc->arg_cls) == -1) {
104 if(oc->err_abort) {
105 return -1;
106 }
107 }
108 } else {
109 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
110 if(oc->err_abort) {
111 return -1;
112 }
113 }
114 }
116 i = oc->argidx;
117 }
119 oc->argidx = 0; /* done parsing args */
120 return 0;
121 }
123 int optcfg_parse_config_file(struct optcfg *oc, const char *fname)
124 {
125 int res;
126 FILE *fp = fopen(fname, "rb");
127 if(!fp) {
128 return -1;
129 }
131 oc->cfg_fname = fname;
132 res = optcfg_parse_config_stream(oc, fp);
133 oc->cfg_fname = 0;
134 fclose(fp);
135 return res;
136 }
138 int optcfg_parse_config_stream(struct optcfg *oc, FILE *fp)
139 {
140 char buf[512];
142 oc->cfg_nline = 0;
143 while(fgets(buf, sizeof buf, fp)) {
144 ++oc->cfg_nline;
146 if(optcfg_parse_config_line(oc, buf) == -1) {
147 if(oc->err_abort) {
148 return -1;
149 }
150 }
151 }
152 return 0;
153 }
155 int optcfg_parse_config_line(struct optcfg *oc, const char *line)
156 {
157 int opt, len;
158 char *start, *val, *buf;
160 len = strlen(line);
161 buf = alloca(len + 1);
162 memcpy(buf, line, len + 1);
164 start = skip_spaces(buf);
165 strip_comments(start);
166 strip_trailing_spaces(start);
167 if(!*start) {
168 return 0;
169 }
171 if(!(val = parse_keyval(start))) {
172 fprintf(stderr, "error parsing %s line %d: invalid syntax\n", oc->cfg_fname ? oc->cfg_fname : "", oc->cfg_nline);
173 return -1;
174 }
175 oc->cfg_value = val;
176 if((opt = get_opt(oc, start)) == -1) {
177 fprintf(stderr, "error parsing %s line %d: unknown option: %s\n", oc->cfg_fname ? oc->cfg_fname : "",
178 oc->cfg_nline, start);
179 return -1;
180 }
182 if(oc->opt_func) {
183 if(oc->opt_func(oc, opt, oc->opt_cls) == -1) {
184 return -1;
185 }
186 }
187 oc->cfg_value = 0;
188 return 0;
189 }
191 int optcfg_enabled_value(struct optcfg *oc, int *enabledp)
192 {
193 if(oc->argidx) {
194 *enabledp = 1; /* TODO take -no- prefix into account */
195 } else {
196 char *val = optcfg_next_value(oc);
197 if(optcfg_bool_value(val, enabledp) == -1) {
198 return -1;
199 }
200 }
201 return 0;
202 }
205 char *optcfg_next_value(struct optcfg *oc)
206 {
207 if(oc->argidx) { /* we're in the middle of parsing arguments, so get the next one */
208 return oc->argv[++oc->argidx];
209 }
210 if(oc->cfg_value) {
211 char *val = oc->cfg_value;
212 oc->cfg_value = 0;
213 return val;
214 }
215 return 0;
216 }
218 void optcfg_print_options(struct optcfg *oc)
219 {
220 int i;
221 for(i=0; oc->optlist[i].opt != -1; i++) {
222 struct optcfg_option *opt = oc->optlist + i;
224 if(opt->c) {
225 printf(" -%c", opt->c);
226 } else {
227 printf(" ");
228 }
229 if(opt->s) {
230 printf("%c-%s: ", opt->c ? ',' : ' ', opt->s);
231 } else {
232 printf(": ");
233 }
234 printf("%s\n", opt->desc ? opt->desc : "undocumented");
235 }
236 }
238 int optcfg_bool_value(char *s, int *valret)
239 {
240 if(strcasecmp(s, "yes") == 0 || strcasecmp(s, "true") == 0 || strcmp(s, "1") == 0) {
241 *valret = 1;
242 return 0;
243 }
244 if(strcasecmp(s, "no") == 0 || strcasecmp(s, "false") == 0 || strcmp(s, "0") == 0) {
245 *valret = 0;
246 return 0;
247 }
248 return -1;
249 }
251 int optcfg_int_value(char *str, int *valret)
252 {
253 char *endp;
254 *valret = strtol(str, &endp, 0);
255 if(endp == str) {
256 return -1;
257 }
258 return 0;
259 }
261 int optcfg_float_value(char *str, float *valret)
262 {
263 char *endp;
264 *valret = strtod(str, &endp);
265 if(endp == str) {
266 return -1;
267 }
268 return 0;
269 }
273 static int get_opt(struct optcfg *oc, const char *arg)
274 {
275 int i, ndashes = 0;
277 while(*arg && *arg == '-') {
278 ndashes++;
279 arg++;
280 }
282 if(ndashes > 2) {
283 arg -= ndashes;
284 }
286 if(arg[1]) { /* match long options */
287 for(i=0; oc->optlist[i].opt != -1; i++) {
288 if(strcmp(arg, oc->optlist[i].s) == 0) {
289 return i;
290 }
291 }
292 } else {
293 for(i=0; oc->optlist[i].opt != -1; i++) {
294 if(arg[0] == oc->optlist[i].c) {
295 return i;
296 }
297 }
298 }
299 return -1;
300 }
302 static char *skip_spaces(char *s)
303 {
304 while(*s && isspace(*s)) ++s;
305 return s;
306 }
308 static void strip_comments(char *s)
309 {
310 while(*s && *s != '#') ++s;
311 if(*s == '#') *s = 0;
312 }
314 static void strip_trailing_spaces(char *s)
315 {
316 char *end = s + strlen(s) - 1;
317 while(end >= s && isspace(*end)) {
318 *end-- = 0;
319 }
320 }
322 static char *parse_keyval(char *line)
323 {
324 char *val;
325 char *eq = strchr(line, '=');
326 if(!eq) return 0;
328 *eq = 0;
329 strip_trailing_spaces(line);
330 val = skip_spaces(eq + 1);
331 strip_trailing_spaces(val);
333 if(!*line || !*val) {
334 return 0;
335 }
336 return val;
337 }