1 |
/* Configuration file handling. |
2 |
* |
3 |
* IRC Services is copyright (c) 1996-2009 Andrew Church. |
4 |
* E-mail: <achurch@achurch.org> |
5 |
* Parts written by Andrew Kempe and others. |
6 |
* This program is free but copyrighted software; see the file GPL.txt for |
7 |
* details. |
8 |
*/ |
9 |
|
10 |
#include "services.h" |
11 |
#include "conffile.h" |
12 |
|
13 |
/*************************************************************************/ |
14 |
|
15 |
static void do_all_directives(int action, ConfigDirective *directives); |
16 |
|
17 |
static int read_config_file(const char *modulename, |
18 |
ConfigDirective *directives); |
19 |
|
20 |
static int do_read_config_file(const char *modulename, |
21 |
ConfigDirective *directives, FILE *f, |
22 |
const char *filename, int recursion_level); |
23 |
|
24 |
static int parse_config_line(const char *filename, int linenum, char *buf, |
25 |
ConfigDirective *directives); |
26 |
|
27 |
|
28 |
/* Actions for do_all_directives(): */ |
29 |
|
30 |
#define ACTION_COPYNEW 0 /* Copy `new' parameters to config variables */ |
31 |
#define ACTION_RESTORESAVED 1 /* Restore saved values of config variables */ |
32 |
|
33 |
/*************************************************************************/ |
34 |
/*************************************************************************/ |
35 |
|
36 |
/* Set configuration options for the given module (if `modulename' is NULL, |
37 |
* set core configuration options). Returns nonzero on success, 0 on error |
38 |
* (an error message is logged, and printed to the terminal if applicable, |
39 |
* in this case). Returns successfully without doing anything if |
40 |
* `directives' is NULL. |
41 |
* |
42 |
* `action' is a bitmask of CONFIGURE_* values (services.h), specifying |
43 |
* what this function should do, as follows: |
44 |
* - CONFIGURE_READ: read new values from the configuration file |
45 |
* - CONFIGURE_SET: copy new values to configuration variables |
46 |
* If both CONFIGURE_READ and CONFIGURE_SET are specified, new values are |
47 |
* copied to the configuration variables only if all values are read in |
48 |
* successfully (i.e. if a configure(...,CONFIGURE_READ) call would have |
49 |
* returned success). CONFIGURE_SET alone will never fail. |
50 |
*/ |
51 |
|
52 |
int configure(const char *modulename, ConfigDirective *directives, |
53 |
int action) |
54 |
{ |
55 |
/* If no directives were given, return success */ |
56 |
if (!directives) |
57 |
return 1; |
58 |
|
59 |
if (action & CONFIGURE_READ) { |
60 |
if (!read_config_file(modulename, directives)) |
61 |
return 0; |
62 |
} |
63 |
|
64 |
if (action & CONFIGURE_SET) |
65 |
do_all_directives(ACTION_COPYNEW, directives); |
66 |
|
67 |
return 1; |
68 |
} |
69 |
|
70 |
/*************************************************************************/ |
71 |
|
72 |
/* Deconfigure given directive array (free any allocated storage and |
73 |
* restore original values). A no-op if `directives' is NULL. |
74 |
*/ |
75 |
|
76 |
void deconfigure(ConfigDirective *directives) |
77 |
{ |
78 |
if (directives) |
79 |
do_all_directives(ACTION_RESTORESAVED, directives); |
80 |
} |
81 |
|
82 |
/*************************************************************************/ |
83 |
|
84 |
/* Print a warning or error message to the log (and the console, if open). */ |
85 |
|
86 |
void config_error(const char *filename, int linenum, const char *message, ...) |
87 |
{ |
88 |
char buf[4096]; |
89 |
va_list args; |
90 |
|
91 |
va_start(args, message); |
92 |
vsnprintf(buf, sizeof(buf), message, args); |
93 |
va_end(args); |
94 |
if (linenum) |
95 |
log("%s:%d: %s", filename, linenum, buf); |
96 |
else |
97 |
log("%s: %s", filename, buf); |
98 |
if (!nofork && isatty(2)) { |
99 |
if (linenum) |
100 |
fprintf(stderr, "%s:%d: %s\n", filename, linenum, buf); |
101 |
else |
102 |
fprintf(stderr, "%s: %s\n", filename, buf); |
103 |
} |
104 |
} |
105 |
|
106 |
/*************************************************************************/ |
107 |
/*************************************************************************/ |
108 |
|
109 |
/* Perform an action for all directives in an array; the action is given |
110 |
* by ACTION_*, defined above. |
111 |
*/ |
112 |
|
113 |
static void do_all_directives(int action, ConfigDirective *directives) |
114 |
{ |
115 |
int n, i; |
116 |
|
117 |
for (n = 0; directives[n].name; n++) { |
118 |
ConfigDirective *d = &directives[n]; |
119 |
for (i = 0; i < CONFIG_MAXPARAMS && d->params[i].type != CD_NONE; i++){ |
120 |
CDValue val; |
121 |
|
122 |
/* Select the appropriate value to copy */ |
123 |
if (action == ACTION_COPYNEW) |
124 |
val = d->params[i].new; |
125 |
else |
126 |
val = d->params[i].prev; |
127 |
|
128 |
/* In any case, we'll be rewriting the config variable, so free |
129 |
* the previous value if it was one we allocated */ |
130 |
if (d->params[i].flags & CF_ALLOCED) { |
131 |
free(*(void **)d->params[i].ptr); |
132 |
d->params[i].flags &= ~CF_ALLOCED; |
133 |
} |
134 |
|
135 |
/* Don't do anything if we're copying new values and this |
136 |
* directive/parameter wasn't seen, or if we're restoring saved |
137 |
* values and this parameter hasn't had its value saved (except |
138 |
* for function parameters) */ |
139 |
if (action == ACTION_COPYNEW |
140 |
&& (!d->was_seen || !(d->params[i].flags & CF_WASSET))) |
141 |
continue; |
142 |
if (action == ACTION_RESTORESAVED |
143 |
&& d->params[i].type != CD_FUNC |
144 |
&& !(d->params[i].flags & CF_SAVED)) |
145 |
continue; |
146 |
|
147 |
/* Copy new value to configuration variable */ |
148 |
switch (d->params[i].type) { |
149 |
case CD_SET: |
150 |
if (action == ACTION_COPYNEW) |
151 |
*(int *)d->params[i].ptr = (int)val.intval; |
152 |
break; |
153 |
case CD_TIME: |
154 |
*(time_t *)d->params[i].ptr = val.timeval; |
155 |
break; |
156 |
case CD_STRING: |
157 |
*(char **)d->params[i].ptr = val.ptrval; |
158 |
break; |
159 |
case CD_INT: |
160 |
case CD_POSINT: |
161 |
case CD_PORT: |
162 |
case CD_TIMEMSEC: |
163 |
*(int32 *)d->params[i].ptr = val.intval; |
164 |
break; |
165 |
case CD_FUNC: { |
166 |
int (*func)(const char *, int, char *) |
167 |
= (int (*)(const char *,int,char *))(d->params[i].ptr); |
168 |
if (action == ACTION_COPYNEW) |
169 |
func(NULL, CDFUNC_SET, NULL); |
170 |
else |
171 |
func(NULL, CDFUNC_DECONFIG, NULL); |
172 |
break; |
173 |
} /* case CD_FUNC */ |
174 |
case CD_DEPRECATED: |
175 |
/* Nothing to do */ |
176 |
break; |
177 |
default: |
178 |
log("conffile: do_all_directives BUG: don't know how to " |
179 |
" copy type %d (%s/%d)", d->params[i].type, d->name, i); |
180 |
break; |
181 |
} /* switch */ |
182 |
|
183 |
/* Fix up flags */ |
184 |
if (action == ACTION_COPYNEW) { |
185 |
if (d->params[i].flags & CF_ALLOCED_NEW) { |
186 |
d->params[i].flags |= CF_ALLOCED; |
187 |
/* The value is still allocated, but it's now stored in |
188 |
* the configuration variable, so we don't want to free |
189 |
* it when clearing `new' */ |
190 |
d->params[i].flags &= ~CF_ALLOCED_NEW; |
191 |
} |
192 |
} else { |
193 |
d->params[i].flags &= ~CF_SAVED; |
194 |
} |
195 |
} /* for each parameter */ |
196 |
} /* for each directive */ |
197 |
} |
198 |
|
199 |
/*************************************************************************/ |
200 |
|
201 |
/* Read in configuration options, and return nonzero for success, zero for |
202 |
* failure. Performs the actions needed by configure(...,CONFIGURE_READ). |
203 |
*/ |
204 |
|
205 |
static int read_config_file(const char *modulename, |
206 |
ConfigDirective *directives) |
207 |
{ |
208 |
const char *filename; |
209 |
FILE *f; |
210 |
int retval, i, n; |
211 |
|
212 |
/* Open default file */ |
213 |
filename = modulename==NULL ? IRCSERVICES_CONF : MODULES_CONF; |
214 |
f = fopen(filename, "r"); |
215 |
if (!f) { |
216 |
log_perror("Unable to open %s", filename); |
217 |
if (!nofork && isatty(2)) |
218 |
fprintf(stderr, "Unable to open %s: %s\n", filename, |
219 |
strerror(errno)); |
220 |
return 0; |
221 |
} |
222 |
|
223 |
/* Clear `was_set' flag and `new' value for all directives */ |
224 |
for (n = 0; directives[n].name != NULL; n++) { |
225 |
directives[n].was_seen = 0; |
226 |
for (i = 0; i < CONFIG_MAXPARAMS; i++) { |
227 |
if (directives[n].params[i].flags & CF_ALLOCED_NEW) |
228 |
free(directives[n].params[i].new.ptrval); |
229 |
directives[n].params[i].flags &= ~(CF_WASSET | CF_ALLOCED_NEW); |
230 |
memset(&directives[n].params[i].new, 0, |
231 |
sizeof(directives[n].params[i].new)); |
232 |
if (directives[n].params[i].type == CD_FUNC) { |
233 |
int (*func)(const char *, int, char *) |
234 |
= (int (*)(const char *, int, char *)) |
235 |
(directives[n].params[i].ptr); |
236 |
func(NULL, CDFUNC_INIT, NULL); |
237 |
} |
238 |
} |
239 |
} |
240 |
|
241 |
/* Actually do the work */ |
242 |
retval = do_read_config_file(modulename, directives, f, filename, 0); |
243 |
|
244 |
fclose(f); |
245 |
|
246 |
/* Make sure all required directives were seen */ |
247 |
for (n = 0; directives[n].name != NULL; n++) { |
248 |
if (!directives[n].was_seen |
249 |
&& (directives[n].params[0].flags & CF_DIRREQ) |
250 |
) { |
251 |
config_error(filename, 0, "Required directive `%s' missing", |
252 |
directives[n].name); |
253 |
retval = 0; |
254 |
} |
255 |
} |
256 |
|
257 |
return retval; |
258 |
} |
259 |
|
260 |
/*************************************************************************/ |
261 |
|
262 |
/* Read in a single configuration file, recursively processing IncludeFile |
263 |
* directives. |
264 |
*/ |
265 |
|
266 |
static int do_read_config_file(const char *modulename, |
267 |
ConfigDirective *directives, FILE *f, |
268 |
const char *filename, int recursion_level) |
269 |
{ |
270 |
char *current_module = NULL; /* Current module in modules.conf */ |
271 |
int retval = 1; /* Return value */ |
272 |
int linenum = 0; |
273 |
char buf[4096], tmpbuf[4096], *s; |
274 |
|
275 |
while (fgets(buf, sizeof(buf), f)) { |
276 |
/* Check for pathologically long files */ |
277 |
if (linenum+1 < linenum) { |
278 |
config_error(filename, linenum, "File too long"); |
279 |
retval = 0; |
280 |
break; |
281 |
} |
282 |
linenum++; |
283 |
/* Check for pathologically long lines */ |
284 |
if (strlen(buf) == sizeof(buf)-1 && buf[sizeof(buf)-1] != '\n') { |
285 |
/* Report the maximum size as sizeof(buf)-3 to allow \r\n as |
286 |
* well as \n to fit */ |
287 |
config_error(filename, linenum, "Line too long (%d bytes maximum)", |
288 |
sizeof(buf)-3); |
289 |
/* Skip everything else until an EOL (or EOF) is seen */ |
290 |
while (fgets(buf, sizeof(buf), f) && buf[strlen(buf)-1] != '\n') |
291 |
/*nothing*/; |
292 |
retval = 0; |
293 |
} |
294 |
/* Strip out comments (but don't touch # inside of quotes) */ |
295 |
s = buf; |
296 |
while (*s) { |
297 |
if (*s == '"') { |
298 |
if (!(s = strchr(s+1, '"'))) |
299 |
break; |
300 |
} else if (*s == '#') { |
301 |
*s = 0; |
302 |
break; |
303 |
} |
304 |
s++; |
305 |
} |
306 |
/* Check for IncludeFile directives (use tmpbuf to avoid damaging |
307 |
* the original copy of the line) */ |
308 |
strbcpy(tmpbuf, buf); |
309 |
s = strtok(tmpbuf, " \t\r\n"); |
310 |
if (s && stricmp(s, "IncludeFile") == 0) { |
311 |
FILE *f2; |
312 |
/* Check recursion level for infinite loops */ |
313 |
if (recursion_level > 100) { |
314 |
config_error(filename, linenum, |
315 |
"IncludeFile recursion depth limit exceeded"); |
316 |
retval = 0; |
317 |
continue; |
318 |
} |
319 |
/* Find the filename */ |
320 |
s = strtok_remaining(); |
321 |
if (s && *s == '"') { |
322 |
char *t = strchr(s+1, '"'); |
323 |
if (!t) { |
324 |
config_error(filename, linenum, |
325 |
"Missing closing double quote"); |
326 |
retval = 0; |
327 |
continue; |
328 |
} |
329 |
*t = 0; |
330 |
s++; |
331 |
} else { |
332 |
s = strtok(s, " \t\r\n"); |
333 |
} |
334 |
if (!s || !*s) { |
335 |
config_error(filename, linenum, |
336 |
"Missing filename for IncludeFile"); |
337 |
retval = 0; |
338 |
continue; |
339 |
} |
340 |
/* Valid filename string; try to open it */ |
341 |
f2 = fopen(s, "r"); |
342 |
if (!f2) { |
343 |
config_error(filename, linenum, |
344 |
"Unable to open %s: %s", s, strerror(errno)); |
345 |
retval = 0; |
346 |
continue; |
347 |
} |
348 |
if (!do_read_config_file(modulename, directives, f2, s, |
349 |
recursion_level+1)) |
350 |
retval = 0; |
351 |
fclose(f2); |
352 |
continue; |
353 |
} |
354 |
/* Handle Module/EndModule lines specially, and don't parse lines |
355 |
* belonging to other modules */ |
356 |
if (modulename) { |
357 |
if (current_module) { |
358 |
/* Inside a Module/EndModule pair: discard lines belonging |
359 |
* to other modules, and handle EndModule directives. If |
360 |
* we reach EndModule for the module we're supposed to be |
361 |
* processing, exit the loop to avoid unneeded processing. */ |
362 |
strbcpy(tmpbuf, buf); |
363 |
s = strtok(tmpbuf, " \t\r\n"); |
364 |
if (s && stricmp(s, "EndModule") == 0) { |
365 |
int strcmp_result = strcmp(current_module, modulename); |
366 |
free(current_module); |
367 |
if (strcmp_result == 0) |
368 |
break; /* stop processing file, we're finished */ |
369 |
else |
370 |
current_module = NULL; |
371 |
continue; |
372 |
} else if (strcmp(current_module, modulename) != 0) { |
373 |
continue; |
374 |
} |
375 |
} else { /* !current_module */ |
376 |
/* Outside a Module/EndModule pair: handle Module |
377 |
* directives, and report errors for anything else */ |
378 |
s = strtok(buf, " \t\r\n"); |
379 |
if (!s) |
380 |
continue; |
381 |
if (stricmp(s, "Module") != 0) { |
382 |
config_error(filename, linenum, |
383 |
"Expected `Module' directive"); |
384 |
retval = 0; |
385 |
} else { |
386 |
current_module = strtok(NULL, " \t\r\n"); |
387 |
if (!current_module) { |
388 |
config_error(filename, linenum, "Module name missing"); |
389 |
retval = 0; |
390 |
} |
391 |
current_module = strdup(current_module); |
392 |
if (!current_module) { |
393 |
config_error(filename, linenum, "Out of memory"); |
394 |
retval = 0; |
395 |
break; |
396 |
} |
397 |
} |
398 |
continue; |
399 |
} /* if (current_module) */ |
400 |
} /* if (modulename) */ |
401 |
/* Valid line--parse it */ |
402 |
if (!parse_config_line(filename, linenum, buf, directives)) |
403 |
retval = 0; |
404 |
} |
405 |
|
406 |
return retval; |
407 |
} |
408 |
|
409 |
/*************************************************************************/ |
410 |
|
411 |
/* Parse a configuration line. Return 1 on success; otherwise, print (and |
412 |
* log, if applicable) appropriate error message and return 0. Destroys |
413 |
* the buffer by side effect. |
414 |
*/ |
415 |
|
416 |
static int parse_config_line(const char *filename, int linenum, char *buf, |
417 |
ConfigDirective *directives) |
418 |
{ |
419 |
char *s, *t, *directive; |
420 |
int i, n, optind; |
421 |
long longval; |
422 |
unsigned long ulongval; |
423 |
int retval = 1; |
424 |
int ac = 0; |
425 |
char *av[CONFIG_MAXPARAMS]; |
426 |
|
427 |
directive = strtok(buf, " \t\r\n"); |
428 |
s = strtok(NULL, ""); |
429 |
if (s) { |
430 |
while (isspace(*s)) |
431 |
s++; |
432 |
while (*s) { |
433 |
if (ac >= CONFIG_MAXPARAMS) { |
434 |
config_error(filename, linenum, |
435 |
"Warning: too many parameters (%d max)", |
436 |
CONFIG_MAXPARAMS); |
437 |
break; |
438 |
} |
439 |
t = s; |
440 |
if (*s == '"') { |
441 |
t++; |
442 |
s++; |
443 |
while (*s && *s != '"') { |
444 |
if (*s == '\\' && s[1] != 0) |
445 |
strmove(s, s+1); |
446 |
s++; |
447 |
} |
448 |
if (!*s) |
449 |
config_error(filename, linenum, |
450 |
"Warning: unterminated double-quoted string"); |
451 |
else |
452 |
*s++ = 0; |
453 |
} else { |
454 |
s += strcspn(s, " \t\r\n"); |
455 |
if (*s) |
456 |
*s++ = 0; |
457 |
} |
458 |
av[ac++] = t; |
459 |
while (isspace(*s)) |
460 |
s++; |
461 |
} |
462 |
} |
463 |
|
464 |
if (!directive) |
465 |
return 1; |
466 |
|
467 |
for (n = 0; directives[n].name; n++) { |
468 |
ConfigDirective *d = &directives[n]; |
469 |
if (stricmp(directive, d->name) != 0) |
470 |
continue; |
471 |
d->was_seen = 1; |
472 |
optind = 0; |
473 |
for (i = 0; i < CONFIG_MAXPARAMS && d->params[i].type != CD_NONE; i++){ |
474 |
if (d->params[i].type == CD_SET) { |
475 |
if (!(d->params[i].flags & CF_SAVED)) { |
476 |
d->params[i].prev.intval = *(int *)d->params[i].ptr; |
477 |
d->params[i].flags |= CF_SAVED; |
478 |
} |
479 |
d->params[i].new.intval = 1; |
480 |
d->params[i].flags |= CF_WASSET; |
481 |
continue; |
482 |
} |
483 |
if (d->params[i].type == CD_DEPRECATED) { |
484 |
config_error(filename, linenum, |
485 |
"Deprecated directive `%s' used", d->name); |
486 |
d->params[i].flags |= CF_WASSET; |
487 |
continue; |
488 |
} |
489 |
if (optind >= ac) { |
490 |
if (!(d->params[i].flags & CF_OPTIONAL)) { |
491 |
config_error(filename, linenum, |
492 |
"Not enough parameters for `%s'", d->name); |
493 |
retval = 0; |
494 |
} |
495 |
break; |
496 |
} |
497 |
switch (d->params[i].type) { |
498 |
case CD_INT: |
499 |
if (!(d->params[i].flags & CF_SAVED)) { |
500 |
d->params[i].prev.intval = *(int32 *)d->params[i].ptr; |
501 |
d->params[i].flags |= CF_SAVED; |
502 |
} |
503 |
longval = strtol(av[optind++], &s, 0); |
504 |
if (*s) { |
505 |
config_error(filename, linenum, |
506 |
"%s: Expected an integer for parameter %d", |
507 |
d->name, optind); |
508 |
retval = 0; |
509 |
break; |
510 |
} |
511 |
#if SIZEOF_LONG > 4 |
512 |
if (longval < -0x80000000L || longval > 0x7FFFFFFFL) { |
513 |
config_error(filename, linenum, |
514 |
"%s: Value out of range for parameter %d", |
515 |
d->name, optind); |
516 |
retval = 0; |
517 |
break; |
518 |
} |
519 |
#endif |
520 |
d->params[i].new.intval = (int32)longval; |
521 |
break; |
522 |
case CD_POSINT: |
523 |
if (!(d->params[i].flags & CF_SAVED)) { |
524 |
d->params[i].prev.intval = *(int32 *)d->params[i].ptr; |
525 |
d->params[i].flags |= CF_SAVED; |
526 |
} |
527 |
ulongval = strtoul(av[optind++], &s, 0); |
528 |
if (*s || ulongval <= 0) { |
529 |
config_error(filename, linenum, |
530 |
"%s: Expected a positive integer for" |
531 |
" parameter %d", d->name, optind); |
532 |
retval = 0; |
533 |
break; |
534 |
} |
535 |
#if SIZEOF_LONG > 4 |
536 |
if (ulongval > 0xFFFFFFFFL) { |
537 |
config_error(filename, linenum, |
538 |
"%s: Value out of range for parameter %d", |
539 |
d->name, optind); |
540 |
retval = 0; |
541 |
break; |
542 |
} |
543 |
#endif |
544 |
d->params[i].new.intval = (int32)ulongval; |
545 |
break; |
546 |
case CD_PORT: |
547 |
if (!(d->params[i].flags & CF_SAVED)) { |
548 |
d->params[i].prev.intval = *(int32 *)d->params[i].ptr; |
549 |
d->params[i].flags |= CF_SAVED; |
550 |
} |
551 |
longval = strtol(av[optind++], &s, 0); |
552 |
if (*s) { |
553 |
config_error(filename, linenum, |
554 |
"%s: Expected a port number for parameter %d", |
555 |
d->name, optind); |
556 |
retval = 0; |
557 |
break; |
558 |
} |
559 |
if (longval < 1 || longval > 65535) { |
560 |
config_error(filename, linenum, |
561 |
"Port numbers must be in the range 1..65535"); |
562 |
retval = 0; |
563 |
break; |
564 |
} |
565 |
d->params[i].new.intval = (int32)longval; |
566 |
break; |
567 |
case CD_STRING: |
568 |
if (!(d->params[i].flags & CF_SAVED)) { |
569 |
d->params[i].prev.ptrval = *(char **)d->params[i].ptr; |
570 |
d->params[i].flags |= CF_SAVED; |
571 |
} |
572 |
d->params[i].new.ptrval = strdup(av[optind++]); |
573 |
if (!d->params[i].new.ptrval) { |
574 |
config_error(filename, linenum, "%s: Out of memory", |
575 |
d->name); |
576 |
return 0; |
577 |
} |
578 |
d->params[i].flags |= CF_ALLOCED_NEW; |
579 |
break; |
580 |
case CD_TIME: |
581 |
if (!(d->params[i].flags & CF_SAVED)) { |
582 |
d->params[i].prev.timeval = *(time_t *)d->params[i].ptr; |
583 |
d->params[i].flags |= CF_SAVED; |
584 |
} |
585 |
d->params[i].new.timeval = dotime(av[optind++]); |
586 |
if (d->params[i].new.timeval < 0) { |
587 |
config_error(filename, linenum, |
588 |
"%s: Expected a time value for parameter %d", |
589 |
d->name, optind); |
590 |
retval = 0; |
591 |
break; |
592 |
} |
593 |
break; |
594 |
case CD_TIMEMSEC: |
595 |
if (!(d->params[i].flags & CF_SAVED)) { |
596 |
d->params[i].prev.intval = *(int32 *)d->params[i].ptr; |
597 |
d->params[i].flags |= CF_SAVED; |
598 |
} |
599 |
longval = strtol(av[optind++], &s, 10); |
600 |
if (longval < 0) { |
601 |
config_error(filename, linenum, |
602 |
"%s: Expected a positive value for" |
603 |
" parameter %d", d->name, optind); |
604 |
retval = 0; |
605 |
break; |
606 |
} else if (longval > 1000000) { |
607 |
config_error(filename, linenum, |
608 |
"%s: Value too large (maximum 1000000)", |
609 |
d->name); |
610 |
} |
611 |
longval *= 1000; |
612 |
if (*s == '.') { |
613 |
int decimal = 0; |
614 |
int count = 0; |
615 |
s++; |
616 |
while (count < 3 && isdigit(*s)) { |
617 |
decimal = decimal*10 + (*s++ - '0'); |
618 |
count++; |
619 |
} |
620 |
while (count++ < 3) |
621 |
decimal *= 10; |
622 |
longval += decimal; |
623 |
while (isdigit(*s)) |
624 |
s++; |
625 |
} |
626 |
if (*s) { |
627 |
config_error(filename, linenum, |
628 |
"%s: Expected a decimal number for" |
629 |
" parameter %d", d->name, optind); |
630 |
retval = 0; |
631 |
break; |
632 |
} |
633 |
d->params[i].new.intval = (int32)longval; |
634 |
break; |
635 |
case CD_FUNC: { |
636 |
int (*func)(const char *, int, char *) |
637 |
= (int (*)(const char *, int, char *))(d->params[i].ptr); |
638 |
if (!func(filename, linenum, av[optind++])) |
639 |
retval = 0; |
640 |
break; |
641 |
} |
642 |
default: |
643 |
config_error(filename, linenum, "%s: Unknown type %d for" |
644 |
" param %d", d->name, d->params[i].type, i+1); |
645 |
return 0; /* don't bother continuing--something's bizarre */ |
646 |
} /* switch (d->params[i].type) */ |
647 |
d->params[i].flags |= CF_WASSET; |
648 |
} /* for all parameters */ |
649 |
break; /* because we found a match */ |
650 |
} /* for all directives in array */ |
651 |
|
652 |
if (!directives[n].name) { |
653 |
config_error(filename, linenum, "Unknown directive `%s'", directive); |
654 |
return 1; /* don't cause abort */ |
655 |
} |
656 |
|
657 |
return retval; |
658 |
} /* parse_config_line() */ |
659 |
|
660 |
/*************************************************************************/ |
661 |
|
662 |
/* |
663 |
* Local variables: |
664 |
* c-file-style: "stroustrup" |
665 |
* c-file-offsets: ((case-label . *) (statement-case-intro . *)) |
666 |
* indent-tabs-mode: nil |
667 |
* End: |
668 |
* |
669 |
* vim: expandtab shiftwidth=4: |
670 |
*/ |