iptables plugin: Adds a plugin to collect iptables'-counters.
[collectd.git] / src / libconfig / libconfig.c
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"
10
11 #ifdef HAVE_STRING_H
12 #include <string.h>
13 #endif
14
15 #ifdef HAVE_STDLIB_H
16 #include <stdlib.h>
17 #endif
18
19 #ifdef HAVE_CTYPE_H
20 #include <ctype.h>
21 #endif
22
23 #ifdef HAVE_STDIO_H
24 #include <stdio.h>
25 #endif
26
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30
31 #ifdef HAVE_SYS_TYPES_H
32 #include <sys/types.h>
33 #endif
34
35 #ifdef HAVE_PWD_H
36 #include <pwd.h>
37 #endif
38
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;
44
45 extern char **environ;
46
47 static int lc_process_var_string(void *data, const char *value, const char **endptr) {
48         char **dataval;
49
50         dataval = data;
51         *dataval = strdup(value);
52
53         *endptr = NULL;
54
55         return(0);
56 }
57
58 static int lc_process_var_cidr(void *data, const char *value, const char **endptr) {
59         return(-1);
60 }
61
62 static int lc_process_var_hostname6(void *data, const char *value, const char **endptr) {
63         return(-1);
64 }
65
66 static int lc_process_var_hostname4(void *data, const char *value, const char **endptr) {
67         return(-1);
68 }
69
70 static int lc_process_var_ip6(void *data, const char *value, const char **endptr) {
71         return(-1);
72 }
73
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
79
80         dataval = data;
81
82         dotptr = value;
83
84         while (1) {
85                 tmpval = atoi(dotptr);
86                 if (tmpval < 0) {
87                         break;
88                 }
89
90                 retval <<= 8;
91                 retval |= tmpval;
92
93                 dotptr = strpbrk(dotptr, "./ \t");
94                 if (dotptr == NULL) {
95                         break;
96                 }
97                 if (*dotptr != '.') {
98                         break;
99                 }
100                 dotptr++;
101         }
102
103         *dataval = retval;
104
105         *endptr = (char *) dotptr;
106
107         return(0);
108 }
109
110 static int lc_process_var_longlong(void *data, const char *value, const char **endptr) {
111         long long *dataval;
112
113         dataval = data;
114         *dataval = strtoll(value, (char **) endptr, 10);
115
116         return(0);
117 }
118
119 static int lc_process_var_long(void *data, const char *value, const char **endptr) {
120         long *dataval;
121
122         dataval = data;
123         *dataval = strtoll(value, (char **) endptr, 10);
124
125         return(0);
126 }
127
128 static int lc_process_var_int(void *data, const char *value, const char **endptr) {
129         int *dataval;
130
131         dataval = data;
132         *dataval = strtoll(value, (char **) endptr, 10);
133
134         return(0);
135 }
136
137 static int lc_process_var_short(void *data, const char *value, const char **endptr) {
138         short *dataval;
139
140         dataval = data;
141         *dataval = strtoll(value, (char **) endptr, 10);
142
143         return(0);
144 }
145
146 static int lc_process_var_bool_byexistance(void *data, const char *value, const char **endptr) {
147         int *dataval;
148
149         dataval = data;
150
151         *dataval = 1;
152
153         *endptr = NULL;
154
155         return(0);
156 }
157
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;
164
165         dataval = data;
166
167         *dataval = -1;
168
169         vallen = strlen(value);
170
171         for (i = 0; i < (sizeof(trueval) / sizeof(*trueval)); i++) {
172                 chkvallen = strlen(trueval[i]);
173
174                 /*
175                  * Skip if there's no way we could find a match here.
176                  */
177                 if (chkvallen > vallen) {
178                         continue;
179                 }
180
181                 /*
182                  * Skip if there is no partial match.
183                  */
184                 if (strncasecmp(value, trueval[i], chkvallen) != 0) {
185                         continue;
186                 }
187
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         }
196
197         for (i = 0; i < (sizeof(falseval) / sizeof(*falseval)); i++) {
198                 chkvallen = strlen(falseval[i]);
199
200                 /*
201                  * Skip if there's no way we could find a match here.
202                  */
203                 if (chkvallen > vallen) {
204                         continue;
205                 }
206
207                 /*
208                  * Skip if there is no partial match.
209                  */
210                 if (strncasecmp(value, falseval[i], chkvallen) != 0) {
211                         continue;
212                 }
213
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         }
222
223         lc_errno = LC_ERR_BADFORMAT;
224         return(-1);
225 }
226
227 static unsigned long long lc_process_size(const char *value, const char **endptr) {
228         unsigned long long retval = 0;
229         char *mult = NULL;
230
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         }
253
254         return(retval);
255 }
256
257 static int lc_process_var_sizelonglong(void *data, const char *value, const char **endptr) {
258         long long *dataval;
259
260         dataval = data;
261         *dataval = lc_process_size(value, endptr);
262
263         return(0);
264 }
265
266 static int lc_process_var_sizelong(void *data, const char *value, const char **endptr) {
267         long *dataval;
268
269         dataval = data;
270         *dataval = lc_process_size(value, endptr);
271
272         return(0);
273 }
274
275 static int lc_process_var_sizeint(void *data, const char *value, const char **endptr) {
276         int *dataval;
277
278         dataval = data;
279         *dataval = lc_process_size(value, endptr);
280
281         return(0);
282 }
283
284 static int lc_process_var_sizeshort(void *data, const char *value, const char **endptr) {
285         short *dataval;
286
287         dataval = data;
288         *dataval = lc_process_size(value, endptr);
289
290         return(0);
291 }
292
293 static int lc_process_var_sizesizet(void *data, const char *value, const char **endptr) {
294         size_t *dataval;
295
296         dataval = data;
297         *dataval = lc_process_size(value, endptr);
298
299         return(0);
300 }
301
302
303 static int lc_handle_type(lc_var_type_t type, const char *value, void *data) {
304         const char *next;
305         int is_list;
306
307         is_list = type & LC_VAR_LIST;
308
309         if (is_list == LC_VAR_LIST) {
310         }
311
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         }
381
382         return(-1);
383 }
384
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;
388
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         }
399
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         }
414
415         return(-1);
416 }
417
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;
430
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         }
436
437         local_lc_errfile = "<environment>";
438         local_lc_errline = 0;
439
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         }
451
452         appnamelen = strlen(ucase_appname);
453
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                 }
459
460                 /* Find our seperator. */
461                 sep = strchr(*currvar, '=');
462                 if (sep == NULL) {
463                         continue;
464                 }
465
466                 varnamelen = sep - *currvar;
467
468                 /* Skip variables that would overflow our buffer. */
469                 if (varnamelen >= sizeof(varnamebuf)) {
470                         continue;
471                 }
472
473                 strncpy(varnamebuf, *currvar, varnamelen);
474
475                 varnamebuf[varnamelen] = '\0';
476                 value = sep + 1;
477
478                 /* We ignore APPNAME by itself. */
479                 if (strlen(varnamebuf) <= appnamelen) {
480                         continue;
481                 }
482
483                 /* Further it must be <APPNAME>_ */
484                 if (varnamebuf[appnamelen] != '_') {
485                         continue;
486                 }
487
488                 cmd = varnamebuf + appnamelen + 1;
489
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                 }
495
496                 for (handler = varhandlers; handler != NULL; handler = handler->_next) {
497                         if (handler->var == NULL) {
498                                 continue;
499                         }
500
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                         }
509
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                         }
522
523                         /* Ignore this handler if they don't match. */
524                         if (strcasecmp(lastcomponent_handler, cmd) != 0) {
525                                 continue;
526                         }
527
528                         if (handler->type == LC_VAR_NONE || handler->type == LC_VAR_BOOL_BY_EXISTANCE) {
529                                 value = NULL;
530                         }
531
532                         /* We ignore errors from the environment variables,
533                            they're mostly insignificant. */
534                         lc_handle(handler, cmd, NULL, value, LC_FLAGS_ENVIRON);
535
536                         break;
537                 }
538         }
539
540         free(ucase_appname);
541
542 #endif
543         return(0);
544 }
545
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;
558
559         local_lc_errfile = "<cmdline>";
560         local_lc_errline = 0;
561
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;
572
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         }
585
586         for (cmdargidx = 1; cmdargidx < argc; cmdargidx++) {
587                 cmdarg = argv[cmdargidx];
588
589                 /* Make sure we have an argument here. */
590                 if (cmdarg == NULL) {
591                         break;
592                 }
593
594                 /* If the argument isn't an option, skip. */
595                 if (cmdarg[0] != '-') {
596                         continue;
597                 }
598
599                 /* Setup a pointer in the new array for the actual argument. */
600                 newargv[newargvidx++] = cmdarg;
601                 usedargv[cmdargidx] = 1;
602
603                 /* Then shift the argument past the '-' so we can ignore it. */
604                 cmdarg++;
605
606                 /* Handle long options. */
607                 if (cmdarg[0] == '-') {
608                         cmdarg++;
609
610                         /* Don't process arguments after the '--' option. */
611                         if (cmdarg[0] == '\0') {
612                                 break;
613                         }
614
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                                 }
629
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                                 }
648
649                                 /* Ignore this handler if they don't match. */
650                                 if (strcasecmp(lastcomponent_handler, cmdarg) != 0) {
651                                         continue;
652                                 }
653
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                                 }
671
672                                 chkretval = lc_handle(handler, handler->var, NULL, cmdoptarg, LC_FLAGS_CMDLINE);
673                                 if (chkretval < 0) {
674                                         retval = -1;
675                                 }
676
677                                 break;
678                         }
679
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;
692
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                                         }
705
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                                         }
723
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                                         }
730
731                                         break;
732                                 }
733
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         }
746
747         if (retval >= 0) {
748                 lc_optind = newargvidx;
749                 for (cmdargidx = 1; cmdargidx < argc; cmdargidx++) {
750                         if (usedargv[cmdargidx] != 0) {
751                                 continue;
752                         }
753                         
754                         cmdarg = argv[cmdargidx];
755
756                         newargv[newargvidx++] = cmdarg;
757                 }
758                 for (cmdargidx = 1; cmdargidx < argc; cmdargidx++) {
759                         argv[cmdargidx] = newargv[cmdargidx];
760                 }
761         }
762
763         free(usedargv);
764         free(newargv);
765
766         return(retval);
767 }
768
769
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;
773
774         lastcomponent_var = strrchr(var, '.');
775         if (lastcomponent_var == NULL) {
776                 lastcomponent_var = var;
777         } else {
778                 lastcomponent_var++;
779         }
780
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                 }
786
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 */
792
793                                 lastcomponent_handler = strrchr(handler->var, '.') + 1; /* strrchr() won't return NULL, because we already checked it. */
794
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                 }
803
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                 }
813
814                 return(lc_handle(handler, var, varargs, value, flags));
815         }
816
817         return(-1);
818 }
819
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;
822
823         newhandler = malloc(sizeof(*newhandler));
824
825         if (newhandler == NULL) {
826                 return(-1);
827         }
828
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;
840
841         varhandlers = newhandler;
842
843         return(0);
844 }
845
846 int lc_register_var(const char *var, lc_var_type_t type, void *data, char opt) {
847         struct lc_varhandler_st *newhandler = NULL;
848
849         newhandler = malloc(sizeof(*newhandler));
850
851         if (newhandler == NULL) {
852                 return(-1);
853         }
854
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;
866
867         varhandlers = newhandler;
868
869         return(0);
870 }
871
872 int lc_process_file(const char *appname, const char *pathname, lc_conf_type_t type) {
873         int chkretval = 0;
874
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         }
899
900         return(chkretval);
901 }
902
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;
912
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         }
949
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         }
965
966         return(retval);
967 }
968
969 void lc_cleanup(void) {
970         struct lc_varhandler_st *handler = NULL, *next = NULL;
971
972         handler = varhandlers;
973         while (handler != NULL) {
974                 if (handler->var != NULL) {
975                         free(handler->var);
976                 }
977
978                 next = handler->_next;
979
980                 free(handler);
981
982                 handler = next;
983         }
984
985         varhandlers = NULL;
986
987         return;
988 }
989
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;
992
993         /* Handle config files. */
994         chkretval = lc_process_files(appname, type, extra);
995         if (chkretval < 0) {
996                 retval = -1;
997         }
998
999         /* Handle environment variables.*/
1000         chkretval = lc_process_environment(appname);
1001         if (chkretval < 0) {
1002                 retval = -1;
1003         }
1004
1005         /* Handle command line arguments */
1006         chkretval = lc_process_cmdline(argc, argv);
1007         if (chkretval < 0) {
1008                 retval = -1;
1009         }
1010
1011         return(retval);
1012 }
1013
1014
1015 lc_err_t lc_geterrno(void) {
1016         lc_err_t retval;
1017
1018         retval = lc_errno;
1019
1020         lc_errno = LC_ERR_NONE;
1021
1022         return(retval);
1023 }
1024
1025 char *lc_geterrstr(void) {
1026         static char retval[512];
1027         char *errmsg = NULL;
1028
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         }
1055
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         }
1063
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         }
1069
1070         retval[sizeof(retval) - 1] = '\0';
1071
1072         return(retval);
1073 }