1 #include "compat.h"
2 #include "libconfig.h"
3 #include "libconfig_private.h"
4 #include "conf_section.h"
5 #include "conf_apache.h"
6 #include "conf_colon.h"
7 #include "conf_equal.h"
8 #include "conf_space.h"
9 #include "conf_xml.h"
11 #ifdef HAVE_STRING_H
12 #include <string.h>
13 #endif
15 #ifdef HAVE_STDLIB_H
16 #include <stdlib.h>
17 #endif
19 #ifdef HAVE_CTYPE_H
20 #include <ctype.h>
21 #endif
23 #ifdef HAVE_STDIO_H
24 #include <stdio.h>
25 #endif
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
31 #ifdef HAVE_SYS_TYPES_H
32 #include <sys/types.h>
33 #endif
35 #ifdef HAVE_PWD_H
36 #include <pwd.h>
37 #endif
39 struct lc_varhandler_st *varhandlers = NULL;
40 lc_err_t lc_errno = LC_ERR_NONE;
41 const char *lc_errfile = NULL;
42 int lc_optind = 0;
43 int lc_errline = 0;
45 extern char **environ;
47 static int lc_process_var_string(void *data, const char *value, const char **endptr) {
48 char **dataval;
50 dataval = data;
51 *dataval = strdup(value);
53 *endptr = NULL;
55 return(0);
56 }
58 static int lc_process_var_cidr(void *data, const char *value, const char **endptr) {
59 return(-1);
60 }
62 static int lc_process_var_hostname6(void *data, const char *value, const char **endptr) {
63 return(-1);
64 }
66 static int lc_process_var_hostname4(void *data, const char *value, const char **endptr) {
67 return(-1);
68 }
70 static int lc_process_var_ip6(void *data, const char *value, const char **endptr) {
71 return(-1);
72 }
74 static int lc_process_var_ip4(void *data, const char *value, const char **endptr) {
75 uint32_t *dataval, retval = 0;
76 const char *dotptr = NULL;
77 int tmpval = -1;
78 // int dotcount
80 dataval = data;
82 dotptr = value;
84 while (1) {
85 tmpval = atoi(dotptr);
86 if (tmpval < 0) {
87 break;
88 }
90 retval <<= 8;
91 retval |= tmpval;
93 dotptr = strpbrk(dotptr, "./ \t");
94 if (dotptr == NULL) {
95 break;
96 }
97 if (*dotptr != '.') {
98 break;
99 }
100 dotptr++;
101 }
103 *dataval = retval;
105 *endptr = (char *) dotptr;
107 return(0);
108 }
110 static int lc_process_var_longlong(void *data, const char *value, const char **endptr) {
111 long long *dataval;
113 dataval = data;
114 *dataval = strtoll(value, (char **) endptr, 10);
116 return(0);
117 }
119 static int lc_process_var_long(void *data, const char *value, const char **endptr) {
120 long *dataval;
122 dataval = data;
123 *dataval = strtoll(value, (char **) endptr, 10);
125 return(0);
126 }
128 static int lc_process_var_int(void *data, const char *value, const char **endptr) {
129 int *dataval;
131 dataval = data;
132 *dataval = strtoll(value, (char **) endptr, 10);
134 return(0);
135 }
137 static int lc_process_var_short(void *data, const char *value, const char **endptr) {
138 short *dataval;
140 dataval = data;
141 *dataval = strtoll(value, (char **) endptr, 10);
143 return(0);
144 }
146 static int lc_process_var_bool_byexistance(void *data, const char *value, const char **endptr) {
147 int *dataval;
149 dataval = data;
151 *dataval = 1;
153 *endptr = NULL;
155 return(0);
156 }
158 static int lc_process_var_bool(void *data, const char *value, const char **endptr) {
159 char *trueval[] = {"enable", "true", "yes", "on", "y", "1"};
160 char *falseval[] = {"disable", "false", "no", "off", "n", "0"};
161 size_t chkvallen, vallen;
162 int *dataval;
163 int i;
165 dataval = data;
167 *dataval = -1;
169 vallen = strlen(value);
171 for (i = 0; i < (sizeof(trueval) / sizeof(*trueval)); i++) {
172 chkvallen = strlen(trueval[i]);
174 /*
175 * Skip if there's no way we could find a match here.
176 */
177 if (chkvallen > vallen) {
178 continue;
179 }
181 /*
182 * Skip if there is no partial match.
183 */
184 if (strncasecmp(value, trueval[i], chkvallen) != 0) {
185 continue;
186 }
188 if (value[chkvallen] == '\0' || value[chkvallen] == ',' || \
189 value[chkvallen] == ' ') {
190 /* Declare a winner and set the next token. */
191 *endptr = value + chkvallen;
192 *dataval = 1;
193 return(0);
194 }
195 }
197 for (i = 0; i < (sizeof(falseval) / sizeof(*falseval)); i++) {
198 chkvallen = strlen(falseval[i]);
200 /*
201 * Skip if there's no way we could find a match here.
202 */
203 if (chkvallen > vallen) {
204 continue;
205 }
207 /*
208 * Skip if there is no partial match.
209 */
210 if (strncasecmp(value, falseval[i], chkvallen) != 0) {
211 continue;
212 }
214 if (value[chkvallen] == '\0' || value[chkvallen] == ',' || \
215 value[chkvallen] == ' ') {
216 /* Declare a winner and set the next token. */
217 *endptr = value + chkvallen;
218 *dataval = 0;
219 return(0);
220 }
221 }
223 lc_errno = LC_ERR_BADFORMAT;
224 return(-1);
225 }
227 static unsigned long long lc_process_size(const char *value, const char **endptr) {
228 unsigned long long retval = 0;
229 char *mult = NULL;
231 retval = strtoll(value, &mult, 10);
232 if (mult != NULL) {
233 switch (tolower(mult[0])) {
234 case 'p':
235 retval *= 1125899906842624LLU;
236 break;
237 case 't':
238 retval *= 1958505086976LLU;
239 break;
240 case 'g':
241 retval *= 1073741824;
242 break;
243 case 'm':
244 retval *= 1048576;
245 break;
246 case 'k':
247 retval *= 1024;
248 break;
249 default:
250 break;
251 }
252 }
254 return(retval);
255 }
257 static int lc_process_var_sizelonglong(void *data, const char *value, const char **endptr) {
258 long long *dataval;
260 dataval = data;
261 *dataval = lc_process_size(value, endptr);
263 return(0);
264 }
266 static int lc_process_var_sizelong(void *data, const char *value, const char **endptr) {
267 long *dataval;
269 dataval = data;
270 *dataval = lc_process_size(value, endptr);
272 return(0);
273 }
275 static int lc_process_var_sizeint(void *data, const char *value, const char **endptr) {
276 int *dataval;
278 dataval = data;
279 *dataval = lc_process_size(value, endptr);
281 return(0);
282 }
284 static int lc_process_var_sizeshort(void *data, const char *value, const char **endptr) {
285 short *dataval;
287 dataval = data;
288 *dataval = lc_process_size(value, endptr);
290 return(0);
291 }
293 static int lc_process_var_sizesizet(void *data, const char *value, const char **endptr) {
294 size_t *dataval;
296 dataval = data;
297 *dataval = lc_process_size(value, endptr);
299 return(0);
300 }
303 static int lc_handle_type(lc_var_type_t type, const char *value, void *data) {
304 const char *next;
305 int is_list;
307 is_list = type & LC_VAR_LIST;
309 if (is_list == LC_VAR_LIST) {
310 }
312 switch (type) {
313 case LC_VAR_STRING:
314 return(lc_process_var_string(data, value, &next));
315 break;
316 case LC_VAR_LONG_LONG:
317 return(lc_process_var_longlong(data, value, &next));
318 break;
319 case LC_VAR_LONG:
320 return(lc_process_var_long(data, value, &next));
321 break;
322 case LC_VAR_INT:
323 return(lc_process_var_int(data, value, &next));
324 break;
325 case LC_VAR_SHORT:
326 return(lc_process_var_short(data, value, &next));
327 break;
328 case LC_VAR_BOOL:
329 return(lc_process_var_bool(data, value, &next));
330 break;
331 case LC_VAR_SIZE_LONG_LONG:
332 return(lc_process_var_sizelonglong(data, value, &next));
333 break;
334 case LC_VAR_SIZE_LONG:
335 return(lc_process_var_sizelong(data, value, &next));
336 break;
337 case LC_VAR_SIZE_INT:
338 return(lc_process_var_sizeint(data, value, &next));
339 break;
340 case LC_VAR_SIZE_SHORT:
341 return(lc_process_var_sizeshort(data, value, &next));
342 break;
343 case LC_VAR_BOOL_BY_EXISTANCE:
344 return(lc_process_var_bool_byexistance(data, value, &next));
345 break;
346 case LC_VAR_SIZE_SIZE_T:
347 return(lc_process_var_sizesizet(data, value, &next));
348 break;
349 case LC_VAR_IP:
350 case LC_VAR_IP4:
351 return(lc_process_var_ip4(data, value, &next));
352 break;
353 case LC_VAR_IP6:
354 return(lc_process_var_ip6(data, value, &next));
355 break;
356 case LC_VAR_HOSTNAME4:
357 return(lc_process_var_hostname4(data, value, &next));
358 break;
359 case LC_VAR_HOSTNAME6:
360 return(lc_process_var_hostname6(data, value, &next));
361 break;
362 case LC_VAR_CIDR:
363 return(lc_process_var_cidr(data, value, &next));
364 break;
365 case LC_VAR_TIME:
366 case LC_VAR_DATE:
367 case LC_VAR_FILENAME:
368 case LC_VAR_DIRECTORY:
369 #ifdef DEBUG
370 fprintf(stderr, "Not implemented yet!\n");
371 #endif
372 return(-1);
373 case LC_VAR_NONE:
374 case LC_VAR_UNKNOWN:
375 case LC_VAR_SECTION:
376 case LC_VAR_SECTIONSTART:
377 case LC_VAR_SECTIONEND:
378 return(0);
379 break;
380 }
382 return(-1);
383 }
385 static int lc_handle(struct lc_varhandler_st *handler, const char *var, const char *varargs, const char *value, lc_flags_t flags) {
386 const char *localvar = NULL;
387 int retval;
389 if (var != NULL) {
390 localvar = strrchr(var, '.');
391 if (localvar == NULL) {
392 localvar = var;
393 } else {
394 localvar++;
395 }
396 } else {
397 localvar = NULL;
398 }
400 switch (handler->mode) {
401 case LC_MODE_CALLBACK:
402 if (handler->callback != NULL) {
403 retval = handler->callback(localvar, var, varargs, value, flags, handler->extra);
404 if (retval < 0) {
405 lc_errno = LC_ERR_CALLBACK;
406 }
407 return(retval);
408 }
409 break;
410 case LC_MODE_VAR:
411 return(lc_handle_type(handler->type, value, handler->data));
412 break;
413 }
415 return(-1);
416 }
418 static int lc_process_environment(const char *appname) {
419 #ifndef ENABLE_SMALL
420 struct lc_varhandler_st *handler = NULL;
421 size_t appnamelen = 0;
422 char varnamebuf[128] = {0};
423 char **currvar;
424 char *sep = NULL, *value = NULL, *cmd = NULL;
425 char *ucase_appname = NULL, *ucase_appname_itr = NULL;
426 char *lastcomponent_handler = NULL;
427 int varnamelen = 0;
428 char *local_lc_errfile;
429 int local_lc_errline;
431 /* Make sure we have an environment to screw with, if not,
432 no arguments were found to be in error */
433 if (environ == NULL || appname == NULL) {
434 return(0);
435 }
437 local_lc_errfile = "<environment>";
438 local_lc_errline = 0;
440 /* Allocate and create our uppercase appname. */
441 ucase_appname = strdup(appname);
442 if (ucase_appname == NULL) {
443 lc_errfile = local_lc_errfile;
444 lc_errline = local_lc_errline;
445 lc_errno = LC_ERR_ENOMEM;
446 return(-1);
447 }
448 for (ucase_appname_itr = ucase_appname; *ucase_appname_itr != '\0'; ucase_appname_itr++) {
449 *ucase_appname_itr = toupper(*ucase_appname_itr);
450 }
452 appnamelen = strlen(ucase_appname);
454 for (currvar = environ; *currvar != NULL; currvar++) {
455 /* If it doesn't begin with our appname ignore it completely. */
456 if (strncmp(*currvar, ucase_appname, appnamelen) != 0) {
457 continue;
458 }
460 /* Find our seperator. */
461 sep = strchr(*currvar, '=');
462 if (sep == NULL) {
463 continue;
464 }
466 varnamelen = sep - *currvar;
468 /* Skip variables that would overflow our buffer. */
469 if (varnamelen >= sizeof(varnamebuf)) {
470 continue;
471 }
473 strncpy(varnamebuf, *currvar, varnamelen);
475 varnamebuf[varnamelen] = '\0';
476 value = sep + 1;
478 /* We ignore APPNAME by itself. */
479 if (strlen(varnamebuf) <= appnamelen) {
480 continue;
481 }
483 /* Further it must be <APPNAME>_ */
484 if (varnamebuf[appnamelen] != '_') {
485 continue;
486 }
488 cmd = varnamebuf + appnamelen + 1;
490 /* We don't allow section specifiers, for reasons see notes in
491 the cmdline processor (below). */
492 if (strchr(cmd, '.') != NULL) {
493 continue;
494 }
496 for (handler = varhandlers; handler != NULL; handler = handler->_next) {
497 if (handler->var == NULL) {
498 continue;
499 }
501 /* Skip handlers which don't agree with being
502 processed outside a config file */
503 if (handler->type == LC_VAR_SECTION ||
504 handler->type == LC_VAR_SECTIONSTART ||
505 handler->type == LC_VAR_SECTIONEND ||
506 handler->type == LC_VAR_UNKNOWN) {
507 continue;
508 }
510 /* Find the last part of the variable and compare it with
511 the option being processed, if a wildcard is given. */
512 if (handler->var[0] == '*' && handler->var[1] == '.') {
513 lastcomponent_handler = strrchr(handler->var, '.');
514 if (lastcomponent_handler == NULL) {
515 lastcomponent_handler = handler->var;
516 } else {
517 lastcomponent_handler++;
518 }
519 } else {
520 lastcomponent_handler = handler->var;
521 }
523 /* Ignore this handler if they don't match. */
524 if (strcasecmp(lastcomponent_handler, cmd) != 0) {
525 continue;
526 }
528 if (handler->type == LC_VAR_NONE || handler->type == LC_VAR_BOOL_BY_EXISTANCE) {
529 value = NULL;
530 }
532 /* We ignore errors from the environment variables,
533 they're mostly insignificant. */
534 lc_handle(handler, cmd, NULL, value, LC_FLAGS_ENVIRON);
536 break;
537 }
538 }
540 free(ucase_appname);
542 #endif
543 return(0);
544 }
546 static int lc_process_cmdline(int argc, char **argv) {
547 struct lc_varhandler_st *handler = NULL;
548 char *cmdarg = NULL, *cmdoptarg = NULL;
549 char *lastcomponent_handler = NULL;
550 char **newargv = NULL;
551 char *usedargv = NULL;
552 int cmdargidx = 0;
553 int newargvidx = 0;
554 int retval = 0, chkretval = 0;
555 int ch = 0;
556 char *local_lc_errfile;
557 int local_lc_errline;
559 local_lc_errfile = "<cmdline>";
560 local_lc_errline = 0;
562 /* Allocate "argc + 1" (+1 for the NULL terminator) elements. */
563 newargv = malloc((argc + 1) * sizeof(*newargv));
564 if (newargv == NULL) {
565 lc_errfile = local_lc_errfile;
566 lc_errline = local_lc_errline;
567 lc_errno = LC_ERR_ENOMEM;
568 return(-1);
569 }
570 newargv[newargvidx++] = argv[0];
571 newargv[argc] = NULL;
573 /* Allocate space to indicate which arguments have been used. */
574 usedargv = malloc(argc * sizeof(*usedargv));
575 if (usedargv == NULL) {
576 lc_errfile = local_lc_errfile;
577 lc_errline = local_lc_errline;
578 lc_errno = LC_ERR_ENOMEM;
579 free(newargv);
580 return(-1);
581 }
582 for (cmdargidx = 0; cmdargidx < argc; cmdargidx++) {
583 usedargv[cmdargidx] = 0;
584 }
586 for (cmdargidx = 1; cmdargidx < argc; cmdargidx++) {
587 cmdarg = argv[cmdargidx];
589 /* Make sure we have an argument here. */
590 if (cmdarg == NULL) {
591 break;
592 }
594 /* If the argument isn't an option, skip. */
595 if (cmdarg[0] != '-') {
596 continue;
597 }
599 /* Setup a pointer in the new array for the actual argument. */
600 newargv[newargvidx++] = cmdarg;
601 usedargv[cmdargidx] = 1;
603 /* Then shift the argument past the '-' so we can ignore it. */
604 cmdarg++;
606 /* Handle long options. */
607 if (cmdarg[0] == '-') {
608 cmdarg++;
610 /* Don't process arguments after the '--' option. */
611 if (cmdarg[0] == '\0') {
612 break;
613 }
615 /* Look for a variable name that matches */
616 for (handler = varhandlers; handler != NULL; handler = handler->_next) {
617 /* Skip handlers with no variable name. */
618 if (handler->var == NULL) {
619 continue;
620 }
621 /* Skip handlers which don't agree with being
622 processed on the command line. */
623 if (handler->type == LC_VAR_SECTION ||
624 handler->type == LC_VAR_SECTIONSTART ||
625 handler->type == LC_VAR_SECTIONEND ||
626 handler->type == LC_VAR_UNKNOWN) {
627 continue;
628 }
630 /* Find the last part of the variable and compare it with
631 the option being processed, if a wildcard is given. */
632 if (handler->var[0] == '*' && handler->var[1] == '.') {
633 lastcomponent_handler = strrchr(handler->var, '.');
634 if (lastcomponent_handler == NULL) {
635 lastcomponent_handler = handler->var;
636 } else {
637 lastcomponent_handler++;
638 }
639 } else {
640 /* Disallow use of the fully qualified name
641 since there was no sectionstart portion
642 we cannot allow it to handle children of it. */
643 if (strchr(cmdarg, '.') != NULL) {
644 continue;
645 }
646 lastcomponent_handler = handler->var;
647 }
649 /* Ignore this handler if they don't match. */
650 if (strcasecmp(lastcomponent_handler, cmdarg) != 0) {
651 continue;
652 }
654 if (handler->type == LC_VAR_NONE || handler->type == LC_VAR_BOOL_BY_EXISTANCE) {
655 cmdoptarg = NULL;
656 } else {
657 cmdargidx++;
658 if (cmdargidx >= argc) {
659 fprintf(stderr, "Argument required.\n");
660 lc_errfile = local_lc_errfile;
661 lc_errline = local_lc_errline;
662 lc_errno = LC_ERR_BADFORMAT;
663 free(usedargv);
664 free(newargv);
665 return(-1);
666 }
667 cmdoptarg = argv[cmdargidx];
668 newargv[newargvidx++] = cmdoptarg;
669 usedargv[cmdargidx] = 1;
670 }
672 chkretval = lc_handle(handler, handler->var, NULL, cmdoptarg, LC_FLAGS_CMDLINE);
673 if (chkretval < 0) {
674 retval = -1;
675 }
677 break;
678 }
680 if (handler == NULL) {
681 fprintf(stderr, "Unknown option: --%s\n", cmdarg);
682 lc_errfile = local_lc_errfile;
683 lc_errline = local_lc_errline;
684 lc_errno = LC_ERR_INVCMD;
685 free(usedargv);
686 free(newargv);
687 return(-1);
688 }
689 } else {
690 for (; *cmdarg != '\0'; cmdarg++) {
691 ch = *cmdarg;
693 for (handler = varhandlers; handler != NULL; handler = handler->_next) {
694 if (handler->opt != ch || handler->opt == '\0') {
695 continue;
696 }
697 /* Skip handlers which don't agree with being
698 processed on the command line. */
699 if (handler->type == LC_VAR_SECTION ||
700 handler->type == LC_VAR_SECTIONSTART ||
701 handler->type == LC_VAR_SECTIONEND ||
702 handler->type == LC_VAR_UNKNOWN) {
703 continue;
704 }
706 if (handler->type == LC_VAR_NONE || handler->type == LC_VAR_BOOL_BY_EXISTANCE) {
707 cmdoptarg = NULL;
708 } else {
709 cmdargidx++;
710 if (cmdargidx >= argc) {
711 fprintf(stderr, "Argument required.\n");
712 lc_errfile = local_lc_errfile;
713 lc_errline = local_lc_errline;
714 lc_errno = LC_ERR_BADFORMAT;
715 free(usedargv);
716 free(newargv);
717 return(-1);
718 }
719 cmdoptarg = argv[cmdargidx];
720 newargv[newargvidx++] = cmdoptarg;
721 usedargv[cmdargidx] = 1;
722 }
724 chkretval = lc_handle(handler, handler->var, NULL, cmdoptarg, LC_FLAGS_CMDLINE);
725 if (chkretval < 0) {
726 lc_errfile = local_lc_errfile;
727 lc_errline = local_lc_errline;
728 retval = -1;
729 }
731 break;
732 }
734 if (handler == NULL) {
735 fprintf(stderr, "Unknown option: -%c\n", ch);
736 lc_errfile = local_lc_errfile;
737 lc_errline = local_lc_errline;
738 lc_errno = LC_ERR_INVCMD;
739 free(usedargv);
740 free(newargv);
741 return(-1);
742 }
743 }
744 }
745 }
747 if (retval >= 0) {
748 lc_optind = newargvidx;
749 for (cmdargidx = 1; cmdargidx < argc; cmdargidx++) {
750 if (usedargv[cmdargidx] != 0) {
751 continue;
752 }
754 cmdarg = argv[cmdargidx];
756 newargv[newargvidx++] = cmdarg;
757 }
758 for (cmdargidx = 1; cmdargidx < argc; cmdargidx++) {
759 argv[cmdargidx] = newargv[cmdargidx];
760 }
761 }
763 free(usedargv);
764 free(newargv);
766 return(retval);
767 }
770 int lc_process_var(const char *var, const char *varargs, const char *value, lc_flags_t flags) {
771 struct lc_varhandler_st *handler = NULL;
772 const char *lastcomponent_handler = NULL, *lastcomponent_var = NULL;
774 lastcomponent_var = strrchr(var, '.');
775 if (lastcomponent_var == NULL) {
776 lastcomponent_var = var;
777 } else {
778 lastcomponent_var++;
779 }
781 for (handler = varhandlers; handler != NULL; handler = handler->_next) {
782 /* If either handler->var or var is NULL, skip, unless both are NULL. */
783 if (handler->var != var && (handler->var == NULL || var == NULL)) {
784 continue;
785 }
787 /* If both are not-NULL, compare them. */
788 if (handler->var != NULL) {
789 /* Wild-card-ish match. */
790 if (handler->var[0] == '*' && handler->var[1] == '.') {
791 /* Only compare the last components */
793 lastcomponent_handler = strrchr(handler->var, '.') + 1; /* strrchr() won't return NULL, because we already checked it. */
795 if (strcasecmp(lastcomponent_handler, lastcomponent_var) != 0) {
796 continue;
797 }
798 } else if (strcasecmp(handler->var, var) != 0) {
799 /* Exact (case-insensitive comparison) failed. */
800 continue;
801 }
802 }
804 if (value == NULL &&
805 handler->type != LC_VAR_NONE &&
806 handler->type != LC_VAR_BOOL_BY_EXISTANCE &&
807 handler->type != LC_VAR_SECTION &&
808 handler->type != LC_VAR_SECTIONSTART &&
809 handler->type != LC_VAR_SECTIONEND) {
810 lc_errno = LC_ERR_BADFORMAT;
811 break;
812 }
814 return(lc_handle(handler, var, varargs, value, flags));
815 }
817 return(-1);
818 }
820 int lc_register_callback(const char *var, char opt, lc_var_type_t type, int (*callback)(const char *, const char *, const char *, const char *, lc_flags_t, void *), void *extra) {
821 struct lc_varhandler_st *newhandler = NULL;
823 newhandler = malloc(sizeof(*newhandler));
825 if (newhandler == NULL) {
826 return(-1);
827 }
829 if (var == NULL) {
830 newhandler->var = NULL;
831 } else {
832 newhandler->var = strdup(var);
833 }
834 newhandler->mode = LC_MODE_CALLBACK;
835 newhandler->type = type;
836 newhandler->callback = callback;
837 newhandler->opt = opt;
838 newhandler->extra = extra;
839 newhandler->_next = varhandlers;
841 varhandlers = newhandler;
843 return(0);
844 }
846 int lc_register_var(const char *var, lc_var_type_t type, void *data, char opt) {
847 struct lc_varhandler_st *newhandler = NULL;
849 newhandler = malloc(sizeof(*newhandler));
851 if (newhandler == NULL) {
852 return(-1);
853 }
855 if (var == NULL) {
856 newhandler->var = NULL;
857 } else {
858 newhandler->var = strdup(var);
859 }
860 newhandler->mode = LC_MODE_VAR;
861 newhandler->type = type;
862 newhandler->data = data;
863 newhandler->opt = opt;
864 newhandler->extra = NULL;
865 newhandler->_next = varhandlers;
867 varhandlers = newhandler;
869 return(0);
870 }
872 int lc_process_file(const char *appname, const char *pathname, lc_conf_type_t type) {
873 int chkretval = 0;
875 switch (type) {
876 case LC_CONF_SECTION:
877 chkretval = lc_process_conf_section(appname, pathname);
878 break;
879 case LC_CONF_APACHE:
880 chkretval = lc_process_conf_apache(appname, pathname);
881 break;
882 case LC_CONF_COLON:
883 chkretval = lc_process_conf_colon(appname, pathname);
884 break;
885 case LC_CONF_EQUAL:
886 chkretval = lc_process_conf_equal(appname, pathname);
887 break;
888 case LC_CONF_SPACE:
889 chkretval = lc_process_conf_space(appname, pathname);
890 break;
891 case LC_CONF_XML:
892 chkretval = lc_process_conf_xml(appname, pathname);
893 break;
894 default:
895 chkretval = -1;
896 lc_errno = LC_ERR_INVDATA;
897 break;
898 }
900 return(chkretval);
901 }
903 static int lc_process_files(const char *appname, lc_conf_type_t type, const char *extraconfig) {
904 #ifdef HAVE_GETPWUID
905 struct passwd *pwinfo = NULL;
906 #endif
907 char configfiles[3][13][512] = {{{0}}};
908 char *configfile = NULL;
909 char *homedir = NULL;
910 int configsetidx = 0, configidx = 0;
911 int chkretval = 0, retval = 0;
913 if (extraconfig != NULL) {
914 snprintf(configfiles[0][0], sizeof(**configfiles) - 1, "%s", extraconfig);
915 }
916 snprintf(configfiles[1][0], sizeof(**configfiles) - 1, "/etc/%s.cfg", appname);
917 snprintf(configfiles[1][1], sizeof(**configfiles) - 1, "/etc/%s.conf", appname);
918 snprintf(configfiles[1][2], sizeof(**configfiles) - 1, "/etc/%s/%s.cfg", appname, appname);
919 snprintf(configfiles[1][3], sizeof(**configfiles) - 1, "/etc/%s/%s.conf", appname, appname);
920 snprintf(configfiles[1][4], sizeof(**configfiles) - 1, "/usr/etc/%s.cfg", appname);
921 snprintf(configfiles[1][5], sizeof(**configfiles) - 1, "/usr/etc/%s.conf", appname);
922 snprintf(configfiles[1][6], sizeof(**configfiles) - 1, "/usr/etc/%s/%s.cfg", appname, appname);
923 snprintf(configfiles[1][7], sizeof(**configfiles) - 1, "/usr/etc/%s/%s.conf", appname, appname);
924 snprintf(configfiles[1][8], sizeof(**configfiles) - 1, "/usr/local/etc/%s.cfg", appname);
925 snprintf(configfiles[1][9], sizeof(**configfiles) - 1, "/usr/local/etc/%s.conf", appname);
926 snprintf(configfiles[1][10], sizeof(**configfiles) - 1, "/usr/local/etc/%s/%s.cfg", appname, appname);
927 snprintf(configfiles[1][11], sizeof(**configfiles) - 1, "/usr/local/etc/%s/%s.conf", appname, appname);
928 if (getuid() != 0) {
929 homedir = getenv("HOME");
930 #ifdef HAVE_GETPWUID
931 if (homedir == NULL) {
932 pwinfo = getpwuid(getuid());
933 if (pwinfo != NULL) {
934 homedir = pwinfo->pw_dir;
935 }
936 }
937 #endif
938 if (homedir != NULL) {
939 if (strcmp(homedir, "/") != 0 && access(homedir, R_OK|W_OK|X_OK) == 0) {
940 snprintf(configfiles[2][0], sizeof(**configfiles) - 1, "%s/.%src", homedir, appname);
941 snprintf(configfiles[2][1], sizeof(**configfiles) - 1, "%s/.%s.cfg", homedir, appname);
942 snprintf(configfiles[2][2], sizeof(**configfiles) - 1, "%s/.%s.conf", homedir, appname);
943 snprintf(configfiles[2][3], sizeof(**configfiles) - 1, "%s/.%s/%s.cfg", homedir, appname, appname);
944 snprintf(configfiles[2][4], sizeof(**configfiles) - 1, "%s/.%s/%s.conf", homedir, appname, appname);
945 snprintf(configfiles[2][5], sizeof(**configfiles) - 1, "%s/.%s/config", homedir, appname);
946 }
947 }
948 }
950 for (configsetidx = 0; configsetidx < 3; configsetidx++) {
951 for (configidx = 0; configidx < 13; configidx++) {
952 configfile = configfiles[configsetidx][configidx];
953 if (configfile[0] == '\0') {
954 break;
955 }
956 if (access(configfile, R_OK) == 0) {
957 chkretval = lc_process_file(appname, configfile, type);
958 if (chkretval < 0) {
959 retval = -1;
960 }
961 break;
962 }
963 }
964 }
966 return(retval);
967 }
969 void lc_cleanup(void) {
970 struct lc_varhandler_st *handler = NULL, *next = NULL;
972 handler = varhandlers;
973 while (handler != NULL) {
974 if (handler->var != NULL) {
975 free(handler->var);
976 }
978 next = handler->_next;
980 free(handler);
982 handler = next;
983 }
985 varhandlers = NULL;
987 return;
988 }
990 int lc_process(int argc, char **argv, const char *appname, lc_conf_type_t type, const char *extra) {
991 int retval = 0, chkretval = 0;
993 /* Handle config files. */
994 chkretval = lc_process_files(appname, type, extra);
995 if (chkretval < 0) {
996 retval = -1;
997 }
999 /* Handle environment variables.*/
1000 chkretval = lc_process_environment(appname);
1001 if (chkretval < 0) {
1002 retval = -1;
1003 }
1005 /* Handle command line arguments */
1006 chkretval = lc_process_cmdline(argc, argv);
1007 if (chkretval < 0) {
1008 retval = -1;
1009 }
1011 return(retval);
1012 }
1015 lc_err_t lc_geterrno(void) {
1016 lc_err_t retval;
1018 retval = lc_errno;
1020 lc_errno = LC_ERR_NONE;
1022 return(retval);
1023 }
1025 char *lc_geterrstr(void) {
1026 static char retval[512];
1027 char *errmsg = NULL;
1029 switch (lc_errno) {
1030 case LC_ERR_NONE:
1031 errmsg = "Success";
1032 break;
1033 case LC_ERR_INVCMD:
1034 errmsg = "Invalid command or option";
1035 break;
1036 case LC_ERR_INVSECTION:
1037 errmsg = "Invalid section";
1038 break;
1039 case LC_ERR_INVDATA:
1040 errmsg = "Invalid application data (internal error)";
1041 break;
1042 case LC_ERR_BADFORMAT:
1043 errmsg = "Bad data specified or incorrect format.";
1044 break;
1045 case LC_ERR_CANTOPEN:
1046 errmsg = "Can't open file.";
1047 break;
1048 case LC_ERR_CALLBACK:
1049 errmsg = "Error return from application handler.";
1050 break;
1051 case LC_ERR_ENOMEM:
1052 errmsg = "Insuffcient memory.";
1053 break;
1054 }
1056 /*
1057 * This is not part of the switch statement so we will get warnings
1058 * about unhandled enum values.
1059 */
1060 if (errmsg == NULL) {
1061 errmsg = "Unknown error";
1062 }
1064 if (lc_errfile == NULL) {
1065 snprintf(retval, sizeof(retval), "%s:%i: %s", "<no file>", lc_errline, errmsg);
1066 } else {
1067 snprintf(retval, sizeof(retval), "%s:%i: %s", lc_errfile, lc_errline, errmsg);
1068 }
1070 retval[sizeof(retval) - 1] = '\0';
1072 return(retval);
1073 }