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