Mercurial > mplayer.hg
annotate cfgparser.c @ 1104:5015360718f4
Added stuff to my hall of fame =)
author | atmosfear |
---|---|
date | Mon, 11 Jun 2001 19:30:25 +0000 |
parents | 545a55a50885 |
children | ecb834719dc9 |
rev | line source |
---|---|
147 | 1 /* |
2 * command line and config file parser | |
336 | 3 * by Szabolcs Berecz <szabi@inf.elte.hu> |
4 * (C) 2001 | |
147 | 5 */ |
6 | |
7 //#define DEBUG | |
8 | |
9 #include <stdlib.h> | |
10 #include <stdio.h> | |
11 #include <ctype.h> | |
12 #include <unistd.h> | |
13 #include <fcntl.h> | |
14 #include <string.h> | |
336 | 15 #include <errno.h> |
147 | 16 |
17 #define ERR_NOT_AN_OPTION -1 | |
18 #define ERR_MISSING_PARAM -2 | |
19 #define ERR_OUT_OF_RANGE -3 | |
150 | 20 #define ERR_FUNC_ERR -4 |
147 | 21 |
22 #define COMMAND_LINE 0 | |
23 #define CONFIG_FILE 1 | |
24 | |
177 | 25 #define MAX_RECURSION_DEPTH 8 |
166 | 26 |
147 | 27 #ifdef DEBUG |
28 #include <assert.h> | |
29 #endif | |
30 | |
31 #include "cfgparser.h" | |
32 | |
33 static struct config *config; | |
34 static int nr_options; /* number of options in 'conf' */ | |
35 static int parser_mode; /* COMMAND_LINE or CONFIG_FILE */ | |
166 | 36 static int recursion_depth = 0; |
147 | 37 |
38 static int init_conf(struct config *conf, int mode) | |
39 { | |
40 #ifdef DEBUG | |
41 assert(conf != NULL); | |
42 #endif | |
43 | |
44 /* calculate the number of options in 'conf' */ | |
45 for (nr_options = 0; conf[nr_options].name != NULL; nr_options++) | |
46 /* NOTHING */; | |
47 | |
48 config = conf; | |
49 #ifdef DEBUG | |
50 if (mode != COMMAND_LINE && mode != CONFIG_FILE) { | |
150 | 51 printf("init_conf: wrong mode!\n"); |
147 | 52 return -1; |
53 } | |
54 #endif | |
55 parser_mode = mode; | |
56 return 1; | |
57 } | |
58 | |
59 static int read_option(char *opt, char *param) | |
60 { | |
61 int i; | |
195 | 62 long tmp_int; |
63 double tmp_float; | |
160 | 64 int ret = -1; |
195 | 65 char *endptr; |
147 | 66 |
67 for (i = 0; i < nr_options; i++) { | |
68 if (!strcasecmp(opt, config[i].name)) | |
69 break; | |
70 } | |
160 | 71 if (i == nr_options) { |
72 printf("invalid option:\n"); | |
73 ret = ERR_NOT_AN_OPTION; | |
74 goto out; | |
75 } | |
76 if (config[i].flags & CONF_NOCFG && parser_mode == CONFIG_FILE) { | |
77 printf("this option can only be used on command line:\n"); | |
78 ret = ERR_NOT_AN_OPTION; | |
79 goto out; | |
80 } | |
81 if (config[i].flags & CONF_NOCMD && parser_mode == COMMAND_LINE) { | |
82 printf("this option can only be used in config file:\n"); | |
83 ret = ERR_NOT_AN_OPTION; | |
84 goto out; | |
85 } | |
150 | 86 |
147 | 87 switch (config[i].type) { |
88 case CONF_TYPE_FLAG: | |
89 /* flags need a parameter in config file */ | |
90 if (parser_mode == CONFIG_FILE) { | |
91 if (!strcasecmp(param, "yes") || /* any other language? */ | |
92 !strcasecmp(param, "ja") || | |
161
9008302e2dc0
Added support for spanish (Yes, I'm a C programer also ;o)
telenieko
parents:
160
diff
changeset
|
93 !strcasecmp(param, "si") || |
147 | 94 !strcasecmp(param, "igen") || |
95 !strcasecmp(param, "y") || | |
96 !strcasecmp(param, "i") || | |
97 !strcmp(param, "1")) | |
98 *((int *) config[i].p) = config[i].max; | |
151 | 99 else if (!strcasecmp(param, "no") || |
147 | 100 !strcasecmp(param, "nein") || |
101 !strcasecmp(param, "nicht") || | |
102 !strcasecmp(param, "nem") || | |
103 !strcasecmp(param, "n") || | |
104 !strcmp(param, "0")) | |
105 *((int *) config[i].p) = config[i].min; | |
160 | 106 else { |
107 printf("invalid parameter for flag:\n"); | |
108 ret = ERR_OUT_OF_RANGE; | |
109 goto out; | |
110 } | |
111 ret = 1; | |
147 | 112 } else { /* parser_mode == COMMAND_LINE */ |
113 *((int *) config[i].p) = config[i].max; | |
160 | 114 ret = 0; |
147 | 115 } |
116 break; | |
117 case CONF_TYPE_INT: | |
118 if (param == NULL) | |
160 | 119 goto err_missing_param; |
195 | 120 |
121 tmp_int = strtol(param, &endptr, 0); | |
122 if (*endptr) { | |
160 | 123 printf("parameter must be an integer:\n"); |
124 ret = ERR_OUT_OF_RANGE; | |
125 goto out; | |
126 } | |
147 | 127 |
153 | 128 if (config[i].flags & CONF_MIN) |
160 | 129 if (tmp_int < config[i].min) { |
130 printf("parameter must be >= %d:\n", (int) config[i].min); | |
131 ret = ERR_OUT_OF_RANGE; | |
132 goto out; | |
133 } | |
147 | 134 |
153 | 135 if (config[i].flags & CONF_MAX) |
160 | 136 if (tmp_int > config[i].max) { |
137 printf("parameter must be <= %d:\n", (int) config[i].max); | |
138 ret = ERR_OUT_OF_RANGE; | |
139 goto out; | |
140 } | |
147 | 141 |
142 *((int *) config[i].p) = tmp_int; | |
160 | 143 ret = 1; |
147 | 144 break; |
145 case CONF_TYPE_FLOAT: | |
146 if (param == NULL) | |
160 | 147 goto err_missing_param; |
195 | 148 |
149 tmp_float = strtod(param, &endptr); | |
150 if (*endptr) { | |
160 | 151 printf("parameter must be a floating point number:\n"); |
152 ret = ERR_MISSING_PARAM; | |
153 goto out; | |
154 } | |
147 | 155 |
153 | 156 if (config[i].flags & CONF_MIN) |
160 | 157 if (tmp_float < config[i].min) { |
158 printf("parameter must be >= %f:\n", config[i].min); | |
159 ret = ERR_OUT_OF_RANGE; | |
160 goto out; | |
161 } | |
147 | 162 |
153 | 163 if (config[i].flags & CONF_MAX) |
160 | 164 if (tmp_float > config[i].max) { |
165 printf("parameter must be <= %f:\n", config[i].max); | |
166 ret = ERR_OUT_OF_RANGE; | |
167 goto out; | |
168 } | |
147 | 169 |
170 *((float *) config[i].p) = tmp_float; | |
160 | 171 ret = 1; |
147 | 172 break; |
173 case CONF_TYPE_STRING: | |
174 if (param == NULL) | |
160 | 175 goto err_missing_param; |
147 | 176 |
153 | 177 if (config[i].flags & CONF_MIN) |
160 | 178 if (strlen(param) < config[i].min) { |
179 printf("parameter must be >= %d chars:\n", | |
180 (int) config[i].min); | |
181 ret = ERR_OUT_OF_RANGE; | |
182 goto out; | |
183 } | |
147 | 184 |
153 | 185 if (config[i].flags & CONF_MAX) |
160 | 186 if (strlen(param) > config[i].max) { |
187 printf("parameter must be <= %d chars:\n", | |
188 (int) config[i].max); | |
189 ret = ERR_OUT_OF_RANGE; | |
190 goto out; | |
191 } | |
147 | 192 |
193 *((char **) config[i].p) = strdup(param); | |
160 | 194 ret = 1; |
147 | 195 break; |
151 | 196 case CONF_TYPE_FUNC_PARAM: |
197 if (param == NULL) | |
160 | 198 goto err_missing_param; |
199 if ((((cfg_func_param_t) config[i].p)(config + i, param)) < 0) { | |
200 ret = ERR_FUNC_ERR; | |
201 goto out; | |
202 } | |
203 ret = 1; | |
151 | 204 break; |
150 | 205 case CONF_TYPE_FUNC: |
160 | 206 if ((((cfg_func_t) config[i].p)(config + i)) < 0) { |
207 ret = ERR_FUNC_ERR; | |
208 goto out; | |
209 } | |
210 ret = 0; | |
150 | 211 break; |
152 | 212 case CONF_TYPE_PRINT: |
213 printf("%s", (char *) config[i].p); | |
214 exit(1); | |
147 | 215 default: |
216 printf("picsaba\n"); | |
217 break; | |
218 } | |
160 | 219 out: |
220 return ret; | |
221 err_missing_param: | |
222 printf("missing parameter:\n"); | |
223 ret = ERR_MISSING_PARAM; | |
224 goto out; | |
147 | 225 } |
226 | |
227 int parse_config_file(struct config *conf, char *conffile) | |
228 { | |
229 #define PRINT_LINENUM printf("%s(%d): ", conffile, line_num) | |
230 #define MAX_LINE_LEN 1000 | |
231 #define MAX_OPT_LEN 100 | |
232 #define MAX_PARAM_LEN 100 | |
233 FILE *fp; | |
234 char *line; | |
179 | 235 char opt[MAX_OPT_LEN + 1]; |
236 char param[MAX_PARAM_LEN + 1]; | |
167 | 237 char c; /* for the "" and '' check */ |
147 | 238 int tmp; |
239 int line_num = 0; | |
240 int line_pos; /* line pos */ | |
241 int opt_pos; /* opt pos */ | |
242 int param_pos; /* param pos */ | |
243 int ret = 1; | |
1090 | 244 int errors = 0; |
147 | 245 |
246 #ifdef DEBUG | |
247 assert(conffile != NULL); | |
248 #endif | |
1089 | 249 if (++recursion_depth > 1) |
250 printf("Reading config file: %s", conffile); | |
251 | |
252 if (recursion_depth > MAX_RECURSION_DEPTH) { | |
253 printf(": too deep 'include'. check your configfiles\n"); | |
254 ret = -1; | |
255 goto out; | |
256 } | |
166 | 257 |
258 if (init_conf(conf, CONFIG_FILE) == -1) { | |
259 ret = -1; | |
260 goto out; | |
261 } | |
147 | 262 |
179 | 263 if ((line = (char *) malloc(MAX_LINE_LEN + 1)) == NULL) { |
336 | 264 perror("\ncan't get memory for 'line'"); |
166 | 265 ret = -1; |
266 goto out; | |
147 | 267 } |
268 | |
269 if ((fp = fopen(conffile, "r")) == NULL) { | |
1089 | 270 if (recursion_depth > 1) |
271 printf(": %s\n", strerror(errno)); | |
147 | 272 free(line); |
166 | 273 ret = 0; |
274 goto out; | |
147 | 275 } |
1089 | 276 if (recursion_depth > 1) |
277 printf("\n"); | |
147 | 278 |
279 while (fgets(line, MAX_LINE_LEN, fp)) { | |
1090 | 280 if (errors >= 16) { |
281 printf("too many errors\n"); | |
282 goto out; | |
283 } | |
284 | |
147 | 285 line_num++; |
286 line_pos = 0; | |
287 | |
288 /* skip whitespaces */ | |
289 while (isspace(line[line_pos])) | |
290 ++line_pos; | |
291 | |
292 /* EOL / comment */ | |
293 if (line[line_pos] == '\0' || line[line_pos] == '#') | |
294 continue; | |
295 | |
480 | 296 /* read option. */ |
297 for (opt_pos = 0; isprint(line[line_pos]) && | |
298 line[line_pos] != ' ' && | |
299 line[line_pos] != '#' && | |
300 line[line_pos] != '='; /* NOTHING */) { | |
147 | 301 opt[opt_pos++] = line[line_pos++]; |
302 if (opt_pos >= MAX_OPT_LEN) { | |
303 PRINT_LINENUM; | |
304 printf("too long option\n"); | |
1090 | 305 errors++; |
147 | 306 ret = -1; |
1090 | 307 goto nextline; |
147 | 308 } |
309 } | |
310 if (opt_pos == 0) { | |
311 PRINT_LINENUM; | |
312 printf("parse error\n"); | |
313 ret = -1; | |
1090 | 314 errors++; |
147 | 315 continue; |
316 } | |
317 opt[opt_pos] = '\0'; | |
318 #ifdef DEBUG | |
319 PRINT_LINENUM; | |
320 printf("option: %s\n", opt); | |
321 #endif | |
322 | |
323 /* skip whitespaces */ | |
324 while (isspace(line[line_pos])) | |
325 ++line_pos; | |
326 | |
327 /* check '=' */ | |
328 if (line[line_pos++] != '=') { | |
329 PRINT_LINENUM; | |
330 printf("option without parameter\n"); | |
331 ret = -1; | |
1090 | 332 errors++; |
147 | 333 continue; |
334 } | |
335 | |
336 /* whitespaces... */ | |
337 while (isspace(line[line_pos])) | |
338 ++line_pos; | |
339 | |
340 /* read the parameter */ | |
167 | 341 if (line[line_pos] == '"' || line[line_pos] == '\'') { |
342 c = line[line_pos]; | |
343 ++line_pos; | |
344 for (param_pos = 0; line[line_pos] != c; /* NOTHING */) { | |
345 param[param_pos++] = line[line_pos++]; | |
346 if (param_pos >= MAX_PARAM_LEN) { | |
347 PRINT_LINENUM; | |
348 printf("too long parameter\n"); | |
349 ret = -1; | |
1090 | 350 errors++; |
351 goto nextline; | |
167 | 352 } |
353 } | |
354 line_pos++; /* skip the closing " or ' */ | |
355 } else { | |
356 for (param_pos = 0; isprint(line[line_pos]) && !isspace(line[line_pos]) | |
357 && line[line_pos] != '#'; /* NOTHING */) { | |
358 param[param_pos++] = line[line_pos++]; | |
359 if (param_pos >= MAX_PARAM_LEN) { | |
360 PRINT_LINENUM; | |
361 printf("too long parameter\n"); | |
362 ret = -1; | |
1090 | 363 errors++; |
364 goto nextline; | |
167 | 365 } |
147 | 366 } |
367 } | |
368 param[param_pos] = '\0'; | |
369 | |
370 /* did we read a parameter? */ | |
371 if (param_pos == 0) { | |
372 PRINT_LINENUM; | |
373 printf("option without parameter\n"); | |
374 ret = -1; | |
1090 | 375 errors++; |
147 | 376 continue; |
377 } | |
378 #ifdef DEBUG | |
379 PRINT_LINENUM; | |
380 printf("parameter: %s\n", param); | |
381 #endif | |
382 /* now, check if we have some more chars on the line */ | |
383 /* whitespace... */ | |
384 while (isspace(line[line_pos])) | |
385 ++line_pos; | |
386 | |
387 /* EOL / comment */ | |
388 if (line[line_pos] != '\0' && line[line_pos] != '#') { | |
389 PRINT_LINENUM; | |
390 printf("extra characters on line: %s\n", line+line_pos); | |
391 ret = -1; | |
392 } | |
393 | |
394 tmp = read_option(opt, param); | |
395 switch (tmp) { | |
396 case ERR_NOT_AN_OPTION: | |
397 case ERR_MISSING_PARAM: | |
398 case ERR_OUT_OF_RANGE: | |
150 | 399 case ERR_FUNC_ERR: |
400 PRINT_LINENUM; | |
160 | 401 printf("%s\n", opt); |
150 | 402 ret = -1; |
1090 | 403 errors++; |
150 | 404 continue; |
405 /* break */ | |
147 | 406 } |
1093 | 407 nextline: |
147 | 408 } |
409 | |
410 free(line); | |
411 fclose(fp); | |
166 | 412 out: |
413 --recursion_depth; | |
147 | 414 return ret; |
415 } | |
416 | |
417 int parse_command_line(struct config *conf, int argc, char **argv, char **envp, char **filename) | |
418 { | |
419 int i; | |
420 int found_filename = 0; | |
421 int tmp; | |
422 char *opt; | |
423 | |
424 #ifdef DEBUG | |
425 assert(argv != NULL); | |
426 assert(envp != NULL); | |
427 assert(argc >= 1); | |
428 #endif | |
429 | |
430 if (init_conf(conf, COMMAND_LINE) == -1) | |
431 return -1; | |
432 | |
1089 | 433 /* in order to work recursion detection properly in parse_config_file */ |
434 ++recursion_depth; | |
435 | |
147 | 436 for (i = 1; i < argc; i++) { |
437 opt = argv[i]; | |
1075 | 438 if (*opt != '-') { |
439 if (found_filename) { | |
440 printf("invalid option:\n"); | |
441 goto err_out; | |
442 } | |
443 goto filename; | |
444 } | |
147 | 445 |
446 /* remove trailing '-' */ | |
447 opt++; | |
448 | |
449 tmp = read_option(opt, argv[i + 1]); | |
450 | |
451 switch (tmp) { | |
452 case ERR_NOT_AN_OPTION: | |
453 /* opt is not an option -> treat it as a filename */ | |
454 if (found_filename) { | |
455 /* we already have a filename */ | |
160 | 456 goto err_out; |
147 | 457 } |
1075 | 458 filename: |
459 found_filename = 1; | |
460 *filename = argv[i]; | |
147 | 461 break; |
462 case ERR_MISSING_PARAM: | |
463 case ERR_OUT_OF_RANGE: | |
150 | 464 case ERR_FUNC_ERR: |
160 | 465 goto err_out; |
150 | 466 /* break; */ |
1075 | 467 default: |
468 i += tmp; | |
147 | 469 } |
1089 | 470 } |
471 --recursion_depth; | |
147 | 472 return found_filename; |
160 | 473 err_out: |
1089 | 474 --recursion_depth; |
1075 | 475 printf("command line: %s\n", argv[i]); |
160 | 476 return -1; |
147 | 477 } |