1 /* dot.conf - configuration file parser library
2  * Copyright (C) 1999,2000,2001,2002 Lukas Schroeder <lukas@azzit.de>,
3  *   and others.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA  02111-1307, USA.
19  *
20  */
21 
22 /* -- dotconf.c - this code is responsible for the input, parsing and dispatching of options  */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 /* Added by Stephen W. Boyer <sboyer@caldera.com>
29  * for wildcard support in Include file paths
30  */
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35 
36 /* -- AIX 4.3 compile time fix
37  * by Eduardo Marcel Macan <macan@colband.com.br>
38  *
39  * modified by Stephen W. Boyer <sboyer@caldera.com>
40  * for Unixware and OpenServer
41  */
42 
43 #if defined (_AIX43) || defined(UNIXWARE) || defined(OSR5)
44 #include <strings.h>
45 #endif
46 
47 #include <stdarg.h>
48 #include <time.h>
49 #include <sys/stat.h>
50 
51 #ifndef WIN32
52 
53 #include <dirent.h>
54 #include <unistd.h>
55 
56 #else /* ndef WIN32 */
57 
58 #include "readdir.h"		/* WIN32 fix by Robert J. Buck */
59 
60 #define strncasecmp strnicmp
61 typedef unsigned long ulong;
62 #define snprintf _snprintf
63 #define vsnprintf _vsnprintf
64 #endif /* !WIN32 */
65 
66 #include <ctype.h>
67 #include "dotconf.h"
68 #include "dotconf_priv.h"
69 
70 #ifndef MIN
71 #define MIN(a,b) ((a)<(b)?(a):(b))
72 #endif
73 
74 static char name[CFG_MAX_OPTION + 1];	/* option name */
75 
76 /*
77  * some 'magic' options that are predefined by dot.conf itself for
78  * advanced functionality
79  */
80 static DOTCONF_CB(dotconf_cb_include);	/* internal 'Include'     */
81 static DOTCONF_CB(dotconf_cb_includepath);	/* internal 'IncludePath' */
82 
83 static configoption_t dotconf_options[] = {
84 	{"Include", ARG_STR, dotconf_cb_include, NULL, CTX_ALL},
85 	{"IncludePath", ARG_STR, dotconf_cb_includepath, NULL, CTX_ALL},
86 	LAST_CONTEXT_OPTION
87 };
88 
skip_whitespace(signed char ** cp,int n,char term)89 static void skip_whitespace(signed char **cp, int n, char term)
90 {
91 	signed char *cp1 = *cp;
92 	while (isspace((int)*cp1) && *cp1 != term && n--)
93 		cp1++;
94 	*cp = cp1;
95 }
96 
copy_word(signed char ** dest,signed char ** src,int max,char term)97 static void copy_word(signed char **dest, signed char **src, int max, char term)
98 {
99 	signed char *cp1 = *src;
100 	signed char *cp2 = *dest;
101 	while (max-- && !isspace((int)*cp1) && *cp1 != term)
102 		*cp2++ = *cp1++;
103 	*cp2 = 0;
104 
105 	*src = cp1;
106 	*dest = cp2;
107 }
108 
get_argname_fallback(const configoption_t * options)109 static const configoption_t *get_argname_fallback(const configoption_t *
110 						  options)
111 {
112 	int i;
113 
114 	for (i = 0; (options[i].name && options[i].name[0]); i++) ;
115 	if (options[i].type == ARG_NAME && options[i].callback)
116 		return &options[i];
117 	return NULL;
118 }
119 
dotconf_substitute_env(configfile_t * configfile,char * str)120 char *dotconf_substitute_env(configfile_t * configfile, char *str)
121 {
122 	char *cp1, *cp2, *cp3, *eos, *eob;
123 	char *env_value;
124 	char env_name[CFG_MAX_VALUE + 1];
125 	char env_default[CFG_MAX_VALUE + 1];
126 	char tmp_value[CFG_MAX_VALUE + 1];
127 
128 	memset(env_name, 0, CFG_MAX_VALUE + 1);
129 	memset(env_default, 0, CFG_MAX_VALUE + 1);
130 	memset(tmp_value, 0, CFG_MAX_VALUE + 1);
131 
132 	cp1 = str;
133 	eob = cp1 + strlen(str) + 1;
134 	cp2 = tmp_value;
135 	eos = cp2 + CFG_MAX_VALUE + 1;
136 
137 	while ((cp1 < eob) && (cp2 < eos) && (*cp1 != '\0')) {
138 		/* substitution needed ?? */
139 		if (*cp1 == '$' && *(cp1 + 1) == '{') {
140 			cp1 += 2;	/* skip ${ */
141 			cp3 = env_name;
142 			while ((cp1 < eob) && !(*cp1 == '}' || *cp1 == ':'))
143 				*cp3++ = *cp1++;
144 			*cp3 = '\0';	/* terminate */
145 
146 			/* default substitution */
147 			if (*cp1 == ':' && *(cp1 + 1) == '-') {
148 				cp1 += 2;	/* skip :- */
149 				cp3 = env_default;
150 				while ((cp1 < eob) && (*cp1 != '}'))
151 					*cp3++ = *cp1++;
152 				*cp3 = '\0';	/* terminate */
153 			} else {
154 				while ((cp1 < eob) && (*cp1 != '}'))
155 					cp1++;
156 			}
157 
158 			if (*cp1 != '}') {
159 				dotconf_warning(configfile, DCLOG_WARNING,
160 						ERR_PARSE_ERROR,
161 						"Unbalanced '{'");
162 			} else {
163 				cp1++;	/* skip } */
164 				if ((env_value = getenv(env_name)) != NULL) {
165 					strncat(cp2, env_value, eos - cp2);
166 					cp2 += strlen(env_value);
167 				} else {
168 					strncat(cp2, env_default, eos - cp2);
169 					cp2 += strlen(env_default);
170 				}
171 			}
172 
173 		}
174 
175 		*cp2++ = *cp1++;
176 	}
177 	*cp2 = '\0';		/* terminate buffer */
178 
179 	free(str);
180 	return strdup(tmp_value);
181 }
182 
dotconf_warning(configfile_t * configfile,int type,unsigned long errnum,const char * fmt,...)183 int dotconf_warning(configfile_t * configfile, int type, unsigned long errnum,
184 		    const char *fmt, ...)
185 {
186 	va_list args;
187 	int retval = 0;
188 
189 	va_start(args, fmt);
190 	if (configfile->errorhandler != 0) {	/* an errorhandler is registered */
191 		char msg[CFG_BUFSIZE];
192 		vsnprintf(msg, CFG_BUFSIZE, fmt, args);
193 		retval =
194 		    configfile->errorhandler(configfile, type, errnum, msg);
195 	} else {		/* no errorhandler, do-it-yourself */
196 
197 		retval = 0;
198 		fprintf(stderr, "%s:%ld: ", configfile->filename,
199 			configfile->line);
200 		vfprintf(stderr, fmt, args);
201 		fprintf(stderr, "\n");
202 	}
203 	va_end(args);
204 
205 	return retval;
206 }
207 
dotconf_register_options(configfile_t * configfile,const configoption_t * options)208 int dotconf_register_options(configfile_t * configfile,
209 			     const configoption_t * options)
210 {
211 	int num = configfile->config_option_count;
212 	int ret = 0;
213 	configoption_t const **temp = configfile->config_options;
214 
215 #define GROW_BY   10
216 
217 	/* resize memoryblock for options blockwise */
218 	if (temp == NULL)
219 		temp = malloc(sizeof(configoption_t *) * (GROW_BY + 1));
220 	else {
221 		if (!(num % GROW_BY))
222 			temp = realloc(temp,
223 				       sizeof(configoption_t *) * (num +
224 								   GROW_BY +
225 								   1));
226 	}
227 
228 #undef GROW_BY
229 
230 	if (temp != NULL) {
231 		/* Allocation or reallocation was successful. */
232 		/* append new options */
233 		temp[configfile->config_option_count] = options;
234 		configfile->config_options = temp;
235 		configfile->config_options[++configfile->config_option_count] =
236 		    0;
237 		ret = 1;
238 	}
239 
240 	return ret;
241 }
242 
dotconf_callback(configfile_t * configfile,callback_types type,dotconf_callback_t callback)243 void dotconf_callback(configfile_t * configfile, callback_types type,
244 		      dotconf_callback_t callback)
245 {
246 	switch (type) {
247 	case ERROR_HANDLER:
248 		configfile->errorhandler = (dotconf_errorhandler_t) callback;
249 		break;
250 	case CONTEXT_CHECKER:
251 		configfile->contextchecker =
252 		    (dotconf_contextchecker_t) callback;
253 		break;
254 	default:
255 		break;
256 	}
257 }
258 
dotconf_continue_line(char * buffer,size_t length)259 int dotconf_continue_line(char *buffer, size_t length)
260 {
261 	/* ------ match [^\\]\\[\r]\n ------------------------------ */
262 	char *cp1 = buffer + length - 1;
263 
264 	if (length < 2)
265 		return 0;
266 
267 	if (*cp1-- != '\n')
268 		return 0;
269 
270 	if (*cp1 == '\r')
271 		cp1--;
272 
273 	if (*cp1-- != '\\')
274 		return 0;
275 
276 	cp1[1] = 0;		/* strip escape character and/or newline */
277 	return (*cp1 != '\\');
278 }
279 
dotconf_get_next_line(char * buffer,size_t bufsize,configfile_t * configfile)280 int dotconf_get_next_line(char *buffer, size_t bufsize,
281 			  configfile_t * configfile)
282 {
283 	char *cp1, *cp2;
284 	char buf2[CFG_BUFSIZE];
285 	int length;
286 
287 	if (configfile->eof)
288 		return 1;
289 
290 	cp1 = fgets(buffer, CFG_BUFSIZE, configfile->stream);
291 
292 	if (!cp1) {
293 		configfile->eof = 1;
294 		return 1;
295 	}
296 
297 	configfile->line++;
298 	length = strlen(cp1);
299 	while (dotconf_continue_line(cp1, length)) {
300 		cp2 = fgets(buf2, CFG_BUFSIZE, configfile->stream);
301 		if (!cp2) {
302 			fprintf(stderr,
303 				"[dotconf] Parse error. Unexpected end of file at "
304 				"line %ld in file %s\n", configfile->line,
305 				configfile->filename);
306 			configfile->eof = 1;
307 			return 1;
308 		}
309 		configfile->line++;
310 		strcpy(cp1 + length - 2, cp2);
311 		length = strlen(cp1);
312 	}
313 
314 	return 0;
315 }
316 
dotconf_get_here_document(configfile_t * configfile,const char * delimit)317 char *dotconf_get_here_document(configfile_t * configfile, const char *delimit)
318 {
319 	/* it's a here-document: yeah, what a cool feature ;) */
320 	unsigned int limit_len;
321 	char here_string;
322 	char buffer[CFG_BUFSIZE];
323 	char *here_doc = 0;
324 	char here_limit[9];	/* max length for here-document delimiter: 8 */
325 	struct stat finfo;
326 	int offset = 0;
327 
328 	if (configfile->size <= 0) {
329 		if (stat(configfile->filename, &finfo)) {
330 			dotconf_warning(configfile, DCLOG_EMERG, ERR_NOACCESS,
331 					"[emerg] could not stat currently read file (%s)\n",
332 					configfile->filename);
333 			return NULL;
334 		}
335 		configfile->size = finfo.st_size;
336 	}
337 
338 	/*
339 	 * allocate a buffer of filesize bytes; should be enough to
340 	 * prevent buffer overflows
341 	 */
342 	here_doc = malloc(configfile->size);	/* allocate buffer memory */
343 	memset(here_doc, 0, configfile->size);
344 
345 	here_string = 1;
346 	limit_len = snprintf(here_limit, 9, "%s", delimit);
347 	while (!dotconf_get_next_line(buffer, CFG_BUFSIZE, configfile)) {
348 		if (!strncmp(here_limit, buffer, limit_len - 1)) {
349 			here_string = 0;
350 			break;
351 		}
352 		offset +=
353 		    snprintf((here_doc + offset), configfile->size - offset - 1,
354 			     "%s", buffer);
355 	}
356 	if (here_string)
357 		dotconf_warning(configfile, DCLOG_WARNING, ERR_PARSE_ERROR,
358 				"Unterminated here-document!");
359 
360 	here_doc[offset - 1] = '\0';	/* strip newline */
361 
362 	return (char *)realloc(here_doc, offset);
363 }
364 
dotconf_invoke_command(configfile_t * configfile,command_t * cmd)365 const char *dotconf_invoke_command(configfile_t * configfile, command_t * cmd)
366 {
367 	const char *error = 0;
368 
369 	error = cmd->option->callback(cmd, configfile->context);
370 	return error;
371 }
372 
dotconf_read_arg(configfile_t * configfile,signed char ** line)373 char *dotconf_read_arg(configfile_t * configfile, signed char **line)
374 {
375 	int sq = 0, dq = 0;	/* single quote, double quote */
376 	int done;
377 	signed char *cp1 = *line;
378 	char *cp2, *eos;
379 	char buf[CFG_MAX_VALUE];
380 
381 	memset(buf, 0, CFG_MAX_VALUE);
382 	done = 0;
383 	cp2 = buf;
384 	eos = cp2 + CFG_MAX_VALUE - 1;
385 
386 	if (*cp1 == '#' || !*cp1)
387 		return NULL;
388 
389 	skip_whitespace(&cp1, CFG_MAX_VALUE, 0);
390 
391 	while ((*cp1 != '\0') && (cp2 != eos) && !done) {
392 		switch (*cp1) {
393 		case '\'':	/* single quote */
394 			if (dq)
395 				break;	/* already double quoting, break out */
396 			if (sq)
397 				sq--;	/* already single quoting, clear state */
398 			else if (!sq)
399 				sq++;	/* set state for single quoting */
400 			break;
401 		case '"':	/* double quote */
402 			if (sq)
403 				break;	/* already single quoting, break out */
404 			if (dq)
405 				dq--;	/* already double quoting, clear state */
406 			else if (!dq)
407 				dq++;	/* set state for double quoting */
408 			break;
409 		case '\\':	/* protected chars */
410 			if (!cp1[1])	/* dont protect NUL */
411 				break;
412 			*cp2++ = *(++cp1);
413 			cp1++;	/* skip the protected one */
414 			continue;
415 			break;
416 		default:
417 			break;
418 		}
419 
420 		/* unquoted space: start a new option argument */
421 		if (isspace((int)*cp1) && !dq && !sq) {
422 			*cp2 = '\0';
423 			break;
424 		}
425 		/* unquoted, unescaped comment-hash ; break out, unless NO_INLINE_COMMENTS is set */
426 		else if (*cp1 == '#' && !dq && !sq
427 			 && !(configfile->flags & NO_INLINE_COMMENTS)) {
428 			/*
429 			 * NOTE: 1.0.8a got the NO_INLINE_COMMENTS feature wrong: it
430 			 * skipped every argument starting with a #, instead of simply eating it!
431 			 */
432 
433 			*cp2 = 0;
434 			*cp1 = 0;
435 			*line = cp1;
436 			return NULL;
437 		}
438 		/* not space or quoted: eat it; dont take quote if quoting */
439 		else if ((!isspace((int)*cp1) && !dq && !sq && *cp1 != '"'
440 			  && *cp1 != '\'')
441 			 || (dq && (*cp1 != '"')) || (sq && *cp1 != '\'')) {
442 			*cp2++ = *cp1;
443 		}
444 
445 		cp1++;
446 	}
447 
448 	*line = cp1;
449 
450 	/* FIXME: escaping substitutes does not work
451 	   Subst ${HOME} \$\{HOME\}
452 	   BOTH! will be substituted, which is somewhat wrong, ain't it ?? :-(
453 	 */
454 	if ((configfile->flags & DONT_SUBSTITUTE) == DONT_SUBSTITUTE)
455 		return buf[0] ? strdup(buf) : NULL;
456 	return buf[0] ? dotconf_substitute_env(configfile, strdup(buf)) : NULL;
457 }
458 
459 /* dotconf_find_command remains here for backwards compatability. it's
460  * internally unused since dot.conf 1.0.9 because it cannot handle the
461  * DUPLICATE_OPTION_NAMES flag
462  */
dotconf_find_command(configfile_t * configfile,const char * command)463 configoption_t *dotconf_find_command(configfile_t * configfile,
464 				     const char *command)
465 {
466 	configoption_t *option;
467 	int i = 0, mod = 0, done = 0;
468 
469 	for (option = 0, mod = 0; configfile->config_options[mod] && !done;
470 	     mod++)
471 		for (i = 0; configfile->config_options[mod][i].name[0]; i++) {
472 			if (!configfile->cmp_func(name,
473 						  configfile->
474 						  config_options[mod][i].name,
475 						  CFG_MAX_OPTION)) {
476 				option =
477 				    (configoption_t *) & configfile->
478 				    config_options[mod][i];
479 				/* TODO: this could be flagged: option overwriting by modules */
480 				done = 1;
481 				break;	/* found it; break out */
482 			}
483 		}
484 
485 	/* handle ARG_NAME fallback */
486 	if ((option && option->name[0] == 0)
487 	    || configfile->config_options[mod - 1][i].type == ARG_NAME) {
488 		option =
489 		    (configoption_t *) & configfile->config_options[mod - 1][i];
490 	}
491 
492 	return option;
493 }
494 
dotconf_set_command(configfile_t * configfile,const configoption_t * option,signed char * args,command_t * cmd)495 void dotconf_set_command(configfile_t * configfile,
496 			 const configoption_t * option, signed char *args,
497 			 command_t * cmd)
498 {
499 	signed char *eob = args + strlen(args);
500 
501 	/* fill in the command_t structure with values we already know */
502 	cmd->name = option->type == ARG_NAME ? name : option->name;
503 	cmd->option = (configoption_t *) option;
504 	cmd->context = configfile->context;
505 	cmd->configfile = configfile;
506 	cmd->data.list = (char **)calloc(CFG_VALUES, sizeof(char *));
507 	cmd->data.str = 0;
508 
509 	if (option->type == ARG_RAW) {
510 		/* if it is an ARG_RAW type, save some time and call the
511 		   callback now */
512 		cmd->data.str = strdup(args);
513 	} else if (option->type == ARG_STR) {
514 		signed char *cp = args;
515 
516 		/* check if it's a here-document and act accordingly */
517 		skip_whitespace(&cp, eob - cp, 0);
518 
519 		if (!strncmp("<<", cp, 2)) {
520 			cmd->data.str =
521 			    dotconf_get_here_document(configfile, cp + 2);
522 			cmd->arg_count = 1;
523 		}
524 	}
525 
526 	if (!(option->type == ARG_STR && cmd->data.str != 0)) {
527 		/* we only get here for non-heredocument lines */
528 
529 		skip_whitespace(&args, eob - args, 0);
530 
531 		cmd->arg_count = 0;
532 		while (cmd->arg_count < (CFG_VALUES - 1)
533 		       && (cmd->data.list[cmd->arg_count] =
534 			   dotconf_read_arg(configfile, &args))) {
535 			cmd->arg_count++;
536 		}
537 
538 		skip_whitespace(&args, eob - args, 0);
539 
540 		if (cmd->arg_count && cmd->data.list[cmd->arg_count - 1]
541 		    && *args)
542 			cmd->data.list[cmd->arg_count++] = strdup(args);
543 
544 		/* has an option entry been found before or do we have to use a fallback? */
545 		if ((option->name && option->name[0] > 32)
546 		    || option->type == ARG_NAME) {
547 			/* found it, now check the type of args it wants */
548 			switch (option->type) {
549 			case ARG_TOGGLE:
550 				/* the value is true if the argument is Yes, On or 1 */
551 				if (cmd->arg_count < 1) {
552 					dotconf_warning(configfile,
553 							DCLOG_WARNING,
554 							ERR_WRONG_ARG_COUNT,
555 							"Missing argument to option '%s'",
556 							name);
557 					return;
558 				}
559 
560 				cmd->data.value =
561 				    CFG_TOGGLED(cmd->data.list[0]);
562 				break;
563 			case ARG_INT:
564 				if (cmd->arg_count < 1) {
565 					dotconf_warning(configfile,
566 							DCLOG_WARNING,
567 							ERR_WRONG_ARG_COUNT,
568 							"Missing argument to option '%s'",
569 							name);
570 					return;
571 				}
572 
573 				sscanf(cmd->data.list[0], "%li",
574 				       &cmd->data.value);
575 				break;
576 
577 			case ARG_DOUBLE:
578 				if (cmd->arg_count < 1) {
579 					dotconf_warning(configfile,
580 							DCLOG_WARNING,
581 							ERR_WRONG_ARG_COUNT,
582 							"Missing argument to option '%s'",
583 							name);
584 					return;
585 				}
586 
587 				cmd->data.dvalue = strtod(cmd->data.list[0], 0);
588 				break;
589 
590 			case ARG_STR:
591 				if (cmd->arg_count < 1) {
592 					dotconf_warning(configfile,
593 							DCLOG_WARNING,
594 							ERR_WRONG_ARG_COUNT,
595 							"Missing argument to option '%s'",
596 							name);
597 					return;
598 				}
599 
600 				cmd->data.str = strdup(cmd->data.list[0]);
601 				break;
602 			case ARG_NAME:	/* fall through */
603 			case ARG_LIST:
604 			case ARG_NONE:
605 			case ARG_RAW:	/* this has been handled before */
606 			default:
607 				break;
608 			}
609 		}
610 	}
611 }
612 
dotconf_free_command(command_t * command)613 void dotconf_free_command(command_t * command)
614 {
615 	int i;
616 
617 	if (command->data.str)
618 		free(command->data.str);
619 
620 	for (i = 0; i < command->arg_count; i++)
621 		free(command->data.list[i]);
622 	free(command->data.list);
623 }
624 
dotconf_handle_command(configfile_t * configfile,char * buffer)625 const char *dotconf_handle_command(configfile_t * configfile, char *buffer)
626 {
627 	signed char *cp1;
628 	signed char *cp2;
629 	/* generic char pointer      */
630 	signed char *eob;	/* end of buffer; end of string  */
631 	const char *error;	/* error message we'll return */
632 	const char *context_error;	/* error message returned by contextchecker */
633 	command_t command;	/* command structure */
634 	int mod = 0;
635 	int next_opt_idx = 0;
636 
637 	memset(&command, 0, sizeof(command_t));
638 	name[0] = 0;
639 	error = 0;
640 	context_error = 0;
641 
642 	cp1 = buffer;
643 	eob = cp1 + strlen(cp1);
644 
645 	skip_whitespace(&cp1, eob - cp1, 0);
646 
647 	/* ignore comments and empty lines */
648 	if (!cp1 || !*cp1 || *cp1 == '#' || *cp1 == '\n' || *cp1 == EOF)
649 		return NULL;
650 
651 	/* skip line if it only contains whitespace */
652 	if (cp1 == eob)
653 		return NULL;
654 
655 	/* get first token: read the name of a possible option */
656 	cp2 = name;
657 	copy_word(&cp2, &cp1, MIN(eob - cp1, CFG_MAX_OPTION), 0);
658 
659 	while (1) {
660 		const configoption_t *option;
661 		int done = 0;
662 		int opt_idx = 0;
663 
664 		for (option = 0; configfile->config_options[mod] && !done;
665 		     mod++) {
666 			for (opt_idx = next_opt_idx;
667 			     configfile->config_options[mod][opt_idx].name[0];
668 			     opt_idx++) {
669 				if (!configfile->
670 				    cmp_func(name,
671 					     configfile->
672 					     config_options[mod][opt_idx].name,
673 					     CFG_MAX_OPTION)) {
674 					/* TODO: this could be flagged: option overwriting by modules */
675 					option =
676 					    (configoption_t *) & configfile->
677 					    config_options[mod][opt_idx];
678 					done = 1;
679 					break;	/* found one; break out */
680 				}
681 			}
682 		}
683 
684 		if (!option)
685 			option =
686 			    get_argname_fallback(configfile->config_options[1]);
687 
688 		if (!option || !option->callback) {
689 			if (error)
690 				return error;
691 			dotconf_warning(configfile, DCLOG_INFO,
692 					ERR_UNKNOWN_OPTION,
693 					"Unknown Config-Option: '%s'", name);
694 			return NULL;
695 		}
696 
697 		/* set up the command structure (contextchecker wants this) */
698 		dotconf_set_command(configfile, option, cp1, &command);
699 
700 		if (configfile->contextchecker)
701 			context_error =
702 			    configfile->contextchecker(&command,
703 						       command.option->context);
704 
705 		if (!context_error)
706 			error = dotconf_invoke_command(configfile, &command);
707 		else {
708 			if (!error) {
709 				/* avoid returning another error then the first. This makes it easier to
710 				   reproduce problems. */
711 				error = context_error;
712 			}
713 		}
714 
715 		dotconf_free_command(&command);
716 
717 		if (!context_error
718 		    || !(configfile->flags & DUPLICATE_OPTION_NAMES)) {
719 			/* don't try more, just quit now. */
720 			break;
721 		}
722 	}
723 
724 	return error;
725 }
726 
dotconf_command_loop_until_error(configfile_t * configfile)727 const char *dotconf_command_loop_until_error(configfile_t * configfile)
728 {
729 	char buffer[CFG_BUFSIZE];
730 
731 	while (!(dotconf_get_next_line(buffer, CFG_BUFSIZE, configfile))) {
732 		const char *error = dotconf_handle_command(configfile, buffer);
733 		if (error)
734 			return error;
735 	}
736 	return NULL;
737 }
738 
dotconf_command_loop(configfile_t * configfile)739 int dotconf_command_loop(configfile_t * configfile)
740 {
741 	/* ------ returns: 0 for failure -- !0 for success ------------------------------------------ */
742 	char buffer[CFG_BUFSIZE];
743 
744 	while (!(dotconf_get_next_line(buffer, CFG_BUFSIZE, configfile))) {
745 		const char *error = dotconf_handle_command(configfile, buffer);
746 		if (error != NULL) {
747 			if (dotconf_warning(configfile, DCLOG_ERR, 0, error))
748 				return 0;
749 		}
750 	}
751 	return 1;
752 }
753 
dotconf_cleanup(configfile_t * configfile)754 void dotconf_cleanup(configfile_t * configfile)
755 {
756 	if (configfile->stream)
757 		fclose(configfile->stream);
758 
759 	if (configfile->filename)
760 		free(configfile->filename);
761 
762 	if (configfile->config_options)
763 		free(configfile->config_options);
764 
765 	if (configfile->includepath)
766 		free(configfile->includepath);
767 
768 	free(configfile);
769 }
770 
dotconf_create(char * fname,const configoption_t * options,context_t * context,unsigned long flags)771 configfile_t *dotconf_create(char *fname, const configoption_t * options,
772 			     context_t * context, unsigned long flags)
773 {
774 	char *dc_env = NULL;
775 	int registered = 0;
776 	configfile_t *new_cfg = calloc(1, sizeof(configfile_t));
777 	char *path = NULL;
778 	char *cwd = NULL;
779 
780 	if (!new_cfg)
781 		return NULL;
782 
783 	/*
784 	 * From here on, we can use dotconf_cleanup to free resources
785 	 * when errors occur.  All of our pointers are NULL, because
786 	 * we allocated with calloc.  When an error occurs, dotconf_cleanup
787 	 * will free all of the resources that were allocated prior to the
788 	 * error.
789 	 */
790 
791 	new_cfg->context = context;
792 	new_cfg->flags = flags;
793 	if (new_cfg->flags & CASE_INSENSITIVE)
794 		new_cfg->cmp_func = strncasecmp;
795 	else
796 		new_cfg->cmp_func = strncmp;
797 
798 	new_cfg->stream = fopen(fname, "r");
799 	if (new_cfg->stream == NULL) {
800 		fprintf(stderr, "Error opening configuration file '%s'\n",
801 			fname);
802 		dotconf_cleanup(new_cfg);
803 		return NULL;
804 	}
805 
806 	registered = dotconf_register_options(new_cfg, dotconf_options);
807 	if (!registered) {
808 		dotconf_cleanup(new_cfg);
809 		return NULL;
810 	}
811 
812 	registered = dotconf_register_options(new_cfg, options);
813 	if (!registered) {
814 		dotconf_cleanup(new_cfg);
815 		return NULL;
816 	}
817 
818 	new_cfg->filename = strdup(fname);
819 	if (!new_cfg->filename) {
820 		dotconf_cleanup(new_cfg);
821 		return NULL;
822 	}
823 
824 	new_cfg->includepath = malloc(CFG_MAX_FILENAME);
825 	if (!new_cfg->includepath) {
826 		dotconf_cleanup(new_cfg);
827 		return NULL;
828 	}
829 
830 	new_cfg->includepath[0] = 0x00;
831 
832 	/*
833 	 * take default includepath from environment if present
834 	 * Otherwise, resolve the path of the configuration file and use that
835 	 * as default includepath.
836 	 */
837 	dc_env = getenv(CFG_INCLUDEPATH_ENV);
838 	if (dc_env != NULL) {
839 		snprintf(new_cfg->includepath, CFG_MAX_FILENAME, "%s", dc_env);
840 	} else {
841 		path = get_path(fname);
842 		if (path != NULL) {
843 			if (path[0] == '/') {
844 				snprintf(new_cfg->includepath, CFG_MAX_FILENAME, "%s", path);
845 			} else {
846 				cwd = get_cwd();
847 				if (cwd != NULL) {
848 					snprintf(new_cfg->includepath, CFG_MAX_FILENAME,
849 						"%s/%s", cwd, path);
850 					free(cwd);
851 				}
852 			}
853 			free(path);
854 		}
855 	}
856 	return new_cfg;
857 }
858 
859 /* ------ internal utility function that verifies if a character is in the WILDCARDS list -- */
dotconf_is_wild_card(char value)860 int dotconf_is_wild_card(char value)
861 {
862 	int retval = 0;
863 	int i;
864 	int wildcards_len = strlen(WILDCARDS);
865 
866 	for (i = 0; i < wildcards_len; i++) {
867 		if (value == WILDCARDS[i]) {
868 			retval = 1;
869 			break;
870 		}
871 	}
872 
873 	return retval;
874 }
875 
876 /* ------ internal utility function that calls the appropriate routine for the wildcard passed in -- */
dotconf_handle_wild_card(command_t * cmd,char wild_card,char * path,char * pre,char * ext)877 int dotconf_handle_wild_card(command_t * cmd, char wild_card, char *path,
878 			     char *pre, char *ext)
879 {
880 	int retval = 0;
881 
882 	switch (wild_card) {
883 	case '*':
884 
885 		retval = dotconf_handle_star(cmd, path, pre, ext);
886 
887 		break;
888 
889 	case '?':
890 
891 		retval = dotconf_handle_question_mark(cmd, path, pre, ext);
892 
893 		break;
894 
895 	default:
896 		retval = -1;
897 	}
898 
899 	return retval;
900 }
901 
902 /* ------ internal utility function that frees allocated memory from dotcont_find_wild_card -- */
dotconf_wild_card_cleanup(char * path,char * pre)903 void dotconf_wild_card_cleanup(char *path, char *pre)
904 {
905 
906 	if (path != NULL) {
907 		free(path);
908 	}
909 
910 	if (pre != NULL) {
911 		free(pre);
912 	}
913 
914 }
915 
916 /* ------ internal utility function to check for wild cards in file path -- */
917 /* ------ path and pre must be freed by the developer ( dotconf_wild_card_cleanup) -- */
dotconf_find_wild_card(char * filename,char * wildcard,char ** path,char ** pre,char ** ext)918 int dotconf_find_wild_card(char *filename, char *wildcard, char **path,
919 			   char **pre, char **ext)
920 {
921 	int retval = -1;
922 	int prefix_len = 0;
923 	int tmp_count = 0;
924 	char *tmp = 0;
925 	int found_path = 0;
926 
927 	int len = strlen(filename);
928 
929 	if (wildcard != NULL && len > 0 && path != NULL && pre != NULL
930 	    && ext != NULL) {
931 		prefix_len = strcspn(filename, WILDCARDS);	/* find any wildcard in WILDCARDS */
932 
933 		if (prefix_len < len) {	/* Wild card found */
934 			tmp = filename + prefix_len;
935 			tmp_count = prefix_len + 1;
936 
937 			while (tmp != filename && *(tmp) != '/') {
938 				tmp--;
939 				tmp_count--;
940 			}
941 
942 			if (*(tmp) == '/') {
943 				*path = (char *)malloc(tmp_count + 1);
944 				found_path = 1;
945 
946 			} else
947 
948 				*path = (char *)malloc(1);
949 
950 			*pre =
951 			    (char *)
952 			    malloc((prefix_len -
953 				    (tmp_count - (found_path ? 0 : 1))) + 1);
954 
955 			if (*path && *pre) {
956 				if (found_path)
957 					strncpy(*path, filename, tmp_count);
958 				(*path)[tmp_count] = '\0';
959 
960 				strncpy(*pre, (tmp + (found_path ? 1 : 0)),
961 					(prefix_len -
962 					 (tmp_count - (found_path ? 0 : 1))));
963 				(*pre)[(prefix_len -
964 					(tmp_count - (found_path ? 0 : 1)))] =
965 				    '\0';
966 
967 				*ext = filename + prefix_len;
968 				*wildcard = (**ext);
969 				(*ext)++;
970 
971 				retval = prefix_len;
972 
973 			}
974 
975 		}
976 
977 	}
978 
979 	return retval;
980 }
981 
982 /* ------ internal utility function that compares two stings from back to front -- */
dotconf_strcmp_from_back(const char * s1,const char * s2)983 int dotconf_strcmp_from_back(const char *s1, const char *s2)
984 {
985 	int retval = 0;
986 	int i, j;
987 	int len_1 = strlen(s1);
988 	int len_2 = strlen(s2);
989 
990 	for (i = len_1, j = len_2; (i >= 0 && j >= 0); i--, j--) {
991 		if (s1[i] != s2[j]) {
992 			retval = -1;
993 			break;
994 		}
995 	}
996 
997 	return retval;
998 }
999 
1000 /* ------ internal utility function that determins if a string matches the '?' criteria -- */
dotconf_question_mark_match(char * dir_name,char * pre,char * ext)1001 int dotconf_question_mark_match(char *dir_name, char *pre, char *ext)
1002 {
1003 	int retval = -1;
1004 	int dir_name_len = strlen(dir_name);
1005 	int pre_len = strlen(pre);
1006 	int ext_len = strlen(ext);
1007 	int w_card_check = strcspn(ext, WILDCARDS);
1008 
1009 	if ((w_card_check < ext_len) && (strncmp(dir_name, pre, pre_len) == 0)
1010 	    && (strcmp(dir_name, ".") != 0) && (strcmp(dir_name, "..") != 0)) {
1011 		retval = 1;	/* Another wildcard found */
1012 
1013 	} else {
1014 
1015 		if ((dir_name_len >= pre_len) &&
1016 		    (strncmp(dir_name, pre, pre_len) == 0) &&
1017 		    (strcmp(dir_name, ".") != 0) &&
1018 		    (strcmp(dir_name, "..") != 0)) {
1019 			retval = 0;	/* Matches no other wildcards */
1020 		}
1021 
1022 	}
1023 
1024 	return retval;
1025 }
1026 
1027 /* ------ internal utility function that determins if a string matches the '*' criteria -- */
dotconf_star_match(char * dir_name,char * pre,char * ext)1028 int dotconf_star_match(char *dir_name, char *pre, char *ext)
1029 {
1030 	int retval = -1;
1031 	int dir_name_len = strlen(dir_name);
1032 	int pre_len = strlen(pre);
1033 	int ext_len = strlen(ext);
1034 	int w_card_check = strcspn(ext, WILDCARDS);
1035 
1036 	if ((w_card_check < ext_len) && (strncmp(dir_name, pre, pre_len) == 0)
1037 	    && (strcmp(dir_name, ".") != 0) && (strcmp(dir_name, "..") != 0)) {
1038 		retval = 1;	/* Another wildcard found */
1039 
1040 	} else {
1041 
1042 		if ((dir_name_len >= (ext_len + pre_len)) &&
1043 		    (dotconf_strcmp_from_back(dir_name, ext) == 0) &&
1044 		    (strncmp(dir_name, pre, pre_len) == 0) &&
1045 		    (strcmp(dir_name, ".") != 0) &&
1046 		    (strcmp(dir_name, "..") != 0)) {
1047 			retval = 0;	/* Matches no other wildcards */
1048 		}
1049 
1050 	}
1051 
1052 	return retval;
1053 }
1054 
1055 /* ------ internal utility function that determins matches for filenames with   -- */
1056 /* ------ a '?' in name and calls the Internal Include function on that filename -- */
dotconf_handle_question_mark(command_t * cmd,char * path,char * pre,char * ext)1057 int dotconf_handle_question_mark(command_t * cmd, char *path, char *pre,
1058 				 char *ext)
1059 {
1060 	configfile_t *included;
1061 	DIR *dh = 0;
1062 	struct dirent *dirptr = 0;
1063 	int i;
1064 
1065 	char new_pre[CFG_MAX_FILENAME];
1066 	char already_matched[CFG_MAX_FILENAME];
1067 
1068 	char wc = '\0';
1069 
1070 	char *new_path = 0;
1071 	char *wc_path = 0;
1072 	char *wc_pre = 0;
1073 	char *wc_ext = 0;
1074 	char *temp = NULL;
1075 
1076 	int pre_len;
1077 	int new_path_len;
1078 	int name_len = 0;
1079 	int alloced = 0;
1080 	int match_state = 0;
1081 
1082 	pre_len = strlen(pre);
1083 
1084 	if ((dh = opendir(path)) != NULL) {
1085 		while ((dirptr = readdir(dh)) != NULL) {
1086 			match_state =
1087 			    dotconf_question_mark_match(dirptr->d_name, pre,
1088 							ext);
1089 
1090 			if (match_state >= 0) {
1091 				name_len = strlen(dirptr->d_name);
1092 				new_path_len =
1093 				    strlen(path) + name_len + strlen(ext) + 1;
1094 
1095 				if (!alloced) {
1096 					if ((new_path =
1097 					     (char *)malloc(new_path_len)) ==
1098 					    NULL) {
1099 						return -1;
1100 					}
1101 
1102 					alloced = new_path_len;
1103 
1104 				} else {
1105 
1106 					if (new_path_len > alloced) {
1107 						temp =
1108 						    realloc(new_path,
1109 							    new_path_len);
1110 						if (temp == NULL) {
1111 							free(new_path);
1112 							return -1;
1113 						}
1114 						new_path = temp;
1115 						alloced = new_path_len;
1116 
1117 					}
1118 
1119 				}
1120 
1121 				if (match_state == 1) {
1122 
1123 					strncpy(new_pre, dirptr->d_name,
1124 						(name_len >
1125 						 pre_len) ? (pre_len +
1126 							     1) : pre_len);
1127 					new_pre[(name_len >
1128 						 pre_len) ? (pre_len +
1129 							     1) : pre_len] =
1130 					    '\0';
1131 
1132 					sprintf(new_path, "%s%s%s", path,
1133 						new_pre, ext);
1134 
1135 					if (strcmp(new_path, already_matched) ==
1136 					    0) {
1137 						continue;	/* Already searched this expression */
1138 
1139 					} else {
1140 
1141 						strcpy(already_matched,
1142 						       new_path);
1143 
1144 					}
1145 
1146 					if (dotconf_find_wild_card
1147 					    (new_path, &wc, &wc_path, &wc_pre,
1148 					     &wc_ext) >= 0) {
1149 						if (dotconf_handle_wild_card
1150 						    (cmd, wc, wc_path, wc_pre,
1151 						     wc_ext) < 0) {
1152 							dotconf_warning(cmd->
1153 									configfile,
1154 									DCLOG_WARNING,
1155 									ERR_INCLUDE_ERROR,
1156 									"Error occured while processing wildcard %c\n"
1157 									"Filename is '%s'\n",
1158 									wc,
1159 									new_path);
1160 
1161 							free(new_path);
1162 							dotconf_wild_card_cleanup
1163 							    (wc_path, wc_pre);
1164 							return -1;
1165 						}
1166 
1167 						dotconf_wild_card_cleanup
1168 						    (wc_path, wc_pre);
1169 						continue;
1170 					}
1171 
1172 				}
1173 
1174 				sprintf(new_path, "%s%s", path, dirptr->d_name);
1175 
1176 				if (access(new_path, R_OK)) {
1177 					dotconf_warning(cmd->configfile,
1178 							DCLOG_WARNING,
1179 							ERR_INCLUDE_ERROR,
1180 							"Cannot open %s for inclusion.\n"
1181 							"IncludePath is '%s'\n",
1182 							new_path,
1183 							cmd->configfile->
1184 							includepath);
1185 					return -1;
1186 				}
1187 
1188 				included =
1189 				    dotconf_create(new_path,
1190 						   cmd->configfile->
1191 						   config_options[1],
1192 						   cmd->configfile->context,
1193 						   cmd->configfile->flags);
1194 				if (included) {
1195 					for (i = 2;
1196 					     cmd->configfile->config_options[i];
1197 					     i++)
1198 						dotconf_register_options
1199 						    (included,
1200 						     cmd->configfile->
1201 						     config_options[i]);
1202 					included->errorhandler =
1203 					    cmd->configfile->errorhandler;
1204 					included->contextchecker =
1205 					    cmd->configfile->contextchecker;
1206 					dotconf_command_loop(included);
1207 					dotconf_cleanup(included);
1208 				}
1209 
1210 			}
1211 
1212 		}
1213 
1214 		closedir(dh);
1215 		free(new_path);
1216 
1217 	}
1218 
1219 	return 0;
1220 }
1221 
1222 /* ------ internal utility function that determins matches for filenames with   --- */
1223 /* ------ a '*' in name and calls the Internal Include function on that filename -- */
dotconf_handle_star(command_t * cmd,char * path,char * pre,char * ext)1224 int dotconf_handle_star(command_t * cmd, char *path, char *pre, char *ext)
1225 {
1226 	configfile_t *included;
1227 	DIR *dh = 0;
1228 	struct dirent *dirptr = 0;
1229 
1230 	char new_pre[CFG_MAX_FILENAME];
1231 	char new_ext[CFG_MAX_FILENAME];
1232 	char already_matched[CFG_MAX_FILENAME];
1233 
1234 	char wc = '\0';
1235 
1236 	char *new_path = 0;
1237 	char *s_ext = 0;
1238 	char *t_ext = 0;
1239 	char *sub = 0;
1240 	char *wc_path = 0;
1241 	char *wc_pre = 0;
1242 	char *wc_ext = 0;
1243 	char *temp = NULL;
1244 
1245 	int pre_len;
1246 	int new_path_len;
1247 	int name_len = 0;
1248 	int alloced = 0;
1249 	int match_state = 0;
1250 	int t_ext_count = 0;
1251 	int sub_count = 0;
1252 
1253 	pre_len = strlen(pre);
1254 	memset(already_matched, 0, CFG_MAX_FILENAME);
1255 	s_ext = ext;
1256 
1257 	while (dotconf_is_wild_card(*s_ext)) {	/* remove trailing wild-cards proceeded by * */
1258 		s_ext++;
1259 	}
1260 
1261 	t_ext = s_ext;
1262 
1263 	while (t_ext != NULL && !(dotconf_is_wild_card(*t_ext))
1264 	       && *t_ext != '\0') {
1265 		t_ext++;	/* find non-wild-card string */
1266 		t_ext_count++;
1267 	}
1268 
1269 	strncpy(new_ext, s_ext, t_ext_count);
1270 	new_ext[t_ext_count] = '\0';
1271 
1272 	if ((dh = opendir(path)) != NULL) {
1273 		while ((dirptr = readdir(dh)) != NULL) {
1274 			sub_count = 0;
1275 			t_ext_count = 0;
1276 
1277 			match_state =
1278 			    dotconf_star_match(dirptr->d_name, pre, s_ext);
1279 
1280 			if (match_state >= 0) {
1281 				name_len = strlen(dirptr->d_name);
1282 				new_path_len =
1283 				    strlen(path) + name_len + strlen(s_ext) + 1;
1284 
1285 				if (!alloced) {
1286 					if ((new_path =
1287 					     (char *)malloc(new_path_len)) ==
1288 					    NULL) {
1289 						return -1;
1290 					}
1291 
1292 					alloced = new_path_len;
1293 
1294 				} else {
1295 
1296 					if (new_path_len > alloced) {
1297 						temp =
1298 						    realloc(new_path,
1299 							    new_path_len);
1300 						if (temp == NULL) {
1301 							free(new_path);
1302 							return -1;
1303 						}
1304 						new_path = temp;
1305 						alloced = new_path_len;
1306 
1307 					}
1308 
1309 				}
1310 
1311 				if (match_state == 1) {
1312 
1313 					if ((sub =
1314 					     strstr((dirptr->d_name + pre_len),
1315 						    new_ext)) == NULL) {
1316 						continue;
1317 					}
1318 
1319 					while (sub != dirptr->d_name) {
1320 						sub--;
1321 						sub_count++;
1322 					}
1323 
1324 					if (sub_count + t_ext_count > name_len) {
1325 						continue;
1326 					}
1327 
1328 					strncpy(new_pre, dirptr->d_name,
1329 						(sub_count + t_ext_count));
1330 					new_pre[sub_count + t_ext_count] = '\0';
1331 					strcat(new_pre, new_ext);
1332 
1333 					sprintf(new_path, "%s%s%s", path,
1334 						new_pre, t_ext);
1335 
1336 					if (strcmp(new_path, already_matched) ==
1337 					    0) {
1338 						continue;	/* Already searched this expression */
1339 
1340 					} else {
1341 
1342 						strcpy(already_matched,
1343 						       new_path);
1344 
1345 					}
1346 
1347 					if (dotconf_find_wild_card
1348 					    (new_path, &wc, &wc_path, &wc_pre,
1349 					     &wc_ext) >= 0) {
1350 						if (dotconf_handle_wild_card
1351 						    (cmd, wc, wc_path, wc_pre,
1352 						     wc_ext) < 0) {
1353 							dotconf_warning(cmd->
1354 									configfile,
1355 									DCLOG_WARNING,
1356 									ERR_INCLUDE_ERROR,
1357 									"Error occured while processing wildcard %c\n"
1358 									"Filename is '%s'\n",
1359 									wc,
1360 									new_path);
1361 
1362 							free(new_path);
1363 							dotconf_wild_card_cleanup
1364 							    (wc_path, wc_pre);
1365 							return -1;
1366 						}
1367 
1368 						dotconf_wild_card_cleanup
1369 						    (wc_path, wc_pre);
1370 						continue;
1371 					}
1372 
1373 				}
1374 
1375 				sprintf(new_path, "%s%s", path, dirptr->d_name);
1376 
1377 				if (access(new_path, R_OK)) {
1378 					dotconf_warning(cmd->configfile,
1379 							DCLOG_WARNING,
1380 							ERR_INCLUDE_ERROR,
1381 							"Cannot open %s for inclusion.\n"
1382 							"IncludePath is '%s'\n",
1383 							new_path,
1384 							cmd->configfile->
1385 							includepath);
1386 					return -1;
1387 				}
1388 
1389 				included =
1390 				    dotconf_create(new_path,
1391 						   cmd->configfile->
1392 						   config_options[1],
1393 						   cmd->configfile->context,
1394 						   cmd->configfile->flags);
1395 				if (included) {
1396 					included->errorhandler =
1397 					    cmd->configfile->errorhandler;
1398 					included->contextchecker =
1399 					    cmd->configfile->contextchecker;
1400 					dotconf_command_loop(included);
1401 					dotconf_cleanup(included);
1402 				}
1403 
1404 			}
1405 
1406 		}
1407 
1408 		closedir(dh);
1409 		free(new_path);
1410 
1411 	}
1412 
1413 	return 0;
1414 }
1415 
get_cwd(void)1416 char *get_cwd(void)
1417 {
1418 	char *buf = calloc(1, CFG_MAX_FILENAME);
1419 
1420 	if (buf == NULL)
1421 		return NULL;
1422 	getcwd(buf, CFG_MAX_FILENAME);
1423 	return buf;
1424 }
1425 
get_path(char * name)1426 char *get_path(char *name)
1427 {
1428 	char *tmp;
1429 	char *buf = NULL;
1430 	int len = 0;
1431 
1432 	tmp = strrchr(name, '/');
1433 	if (tmp == NULL)
1434 		return NULL;
1435 	buf = calloc(1, CFG_MAX_FILENAME);
1436 	if (buf == NULL)
1437 		return NULL;
1438 	if (tmp == name) {
1439 		sprintf(buf, "/");
1440 	} else {
1441 		len = tmp - name + 1;
1442 		if (len > CFG_MAX_FILENAME)
1443 			len -= 1;
1444 	}
1445 		snprintf(buf, len, "%s", name);
1446 	return buf;
1447 }
1448 
1449 /* ------ callbacks of the internal option (Include, IncludePath) ------------------------------- */
DOTCONF_CB(dotconf_cb_include)1450 DOTCONF_CB(dotconf_cb_include)
1451 {
1452 	char *filename = 0;
1453 	configfile_t *included;
1454 
1455 	char wild_card;
1456 	char *path = 0;
1457 	char *pre = 0;
1458 	char *ext = 0;
1459 
1460 	if (cmd->configfile->includepath
1461 	    && cmd->data.str[0] != '/'
1462 	    && cmd->configfile->includepath[0] != '\0') {
1463 		/* relative file AND include path is used */
1464 		int len, inclen;
1465 		char *sl;
1466 
1467 		inclen = strlen(cmd->configfile->includepath);
1468 		if ((len =
1469 		     (strlen(cmd->data.str) + inclen + 1)) ==
1470 		    CFG_MAX_FILENAME) {
1471 			dotconf_warning(cmd->configfile, DCLOG_WARNING,
1472 					ERR_INCLUDE_ERROR,
1473 					"Absolute filename too long (>%d)",
1474 					CFG_MAX_FILENAME);
1475 			return NULL;
1476 		}
1477 
1478 		if (cmd->configfile->includepath[inclen - 1] == '/')
1479 			sl = "";
1480 		else {
1481 			sl = "/";
1482 			len++;
1483 		}
1484 
1485 		filename = malloc(len);
1486 		snprintf(filename, len, "%s%s%s",
1487 			 cmd->configfile->includepath, sl, cmd->data.str);
1488 	} else			/* fully qualified, or no includepath */
1489 		filename = strdup(cmd->data.str);
1490 
1491 	/* Added wild card support here */
1492 	if (dotconf_find_wild_card(filename, &wild_card, &path, &pre, &ext) >=
1493 	    0) {
1494 		if (dotconf_handle_wild_card(cmd, wild_card, path, pre, ext) <
1495 		    0) {
1496 			dotconf_warning(cmd->configfile, DCLOG_WARNING,
1497 					ERR_INCLUDE_ERROR,
1498 					"Error occured while attempting to process %s for inclusion.\n"
1499 					"IncludePath is '%s'\n", filename,
1500 					cmd->configfile->includepath);
1501 		}
1502 
1503 		dotconf_wild_card_cleanup(path, pre);
1504 		free(filename);
1505 		return NULL;
1506 	}
1507 
1508 	if (access(filename, R_OK)) {
1509 		dotconf_warning(cmd->configfile, DCLOG_WARNING,
1510 				ERR_INCLUDE_ERROR,
1511 				"Cannot open %s for inclusion.\n"
1512 				"IncludePath is '%s'\n", filename,
1513 				cmd->configfile->includepath);
1514 		free(filename);
1515 		return NULL;
1516 	}
1517 
1518 	included = dotconf_create(filename, cmd->configfile->config_options[1],
1519 				  cmd->configfile->context,
1520 				  cmd->configfile->flags);
1521 	if (included) {
1522 		included->contextchecker =
1523 		    (dotconf_contextchecker_t) cmd->configfile->contextchecker;
1524 		included->errorhandler =
1525 		    (dotconf_errorhandler_t) cmd->configfile->errorhandler;
1526 
1527 		dotconf_command_loop(included);
1528 		dotconf_cleanup(included);
1529 	}
1530 
1531 	free(filename);
1532 	return NULL;
1533 }
1534 
DOTCONF_CB(dotconf_cb_includepath)1535 DOTCONF_CB(dotconf_cb_includepath)
1536 {
1537 	char *env = getenv(CFG_INCLUDEPATH_ENV);
1538 	/* environment overrides configuration file setting */
1539 	if (!env)
1540 		snprintf(cmd->configfile->includepath, CFG_MAX_FILENAME, "%s",
1541 			 cmd->data.str);
1542 	return NULL;
1543 }
1544