ossp-pkg/lmtp2nntp/lmtp2nntp_dotconf.c
/* dot.conf - configuration file parser library
* Copyright (C) 1999,2000,2001 Lukas Schroeder <lukas@azzit.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
/* -- dotconf.c - this code is responsible for the input, parsing and dispatching of options */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Added by Stephen W. Boyer <sboyer@caldera.com>
* for wildcard support in Include file paths
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
/* -- AIX 4.3 compile time fix
* by Eduardo Marcel Macan <macan@colband.com.br>
*
* modified by Stephen W. Boyer <sboyer@caldera.com>
* for Unixware and OpenServer
*/
#if defined (_AIX43) || defined(UNIXWARE) || defined(OSR5)
#include <strings.h>
#endif
#include <stdarg.h>
#include <time.h>
#include <sys/stat.h>
#ifndef WIN32
#include <dirent.h>
#include <unistd.h>
#else /* ndef WIN32 */
#include "readdir.h" /* WIN32 fix by Robert J. Buck */
#define strncasecmp strnicmp
typedef unsigned long ulong;
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#endif /* !WIN32 */
#include <ctype.h>
#include "lmtp2nntp_dotconf.h"
static char name[CFG_MAX_OPTION + 1]; /* option name */
/*
* some 'magic' options that are predefined by dot.conf itself for
* advanced functionality
*/
static DOTCONF_CB(dotconf_cb_include); /* internal 'Include' */
static DOTCONF_CB(dotconf_cb_includepath); /* internal 'IncludePath' */
static configoption_t dotconf_options[] =
{
{ "Include", ARG_STR, dotconf_cb_include, NULL, CTX_ALL },
{ "IncludePath", ARG_STR, dotconf_cb_includepath, NULL, CTX_ALL },
LAST_CONTEXT_OPTION
};
char *dotconf_substitute_env(configfile_t *configfile, char *str)
{
char *cp1, *cp2, *cp3, *eos, *eob;
char *env_value;
char env_name[CFG_MAX_VALUE + 1];
char env_default[CFG_MAX_VALUE + 1];
char tmp_value[CFG_MAX_VALUE + 1];
memset(env_name, 0, CFG_MAX_VALUE + 1);
memset(env_default, 0, CFG_MAX_VALUE + 1);
memset(tmp_value, 0, CFG_MAX_VALUE + 1);
cp1 = str;
eob = cp1 + strlen(str) + 1;
cp2 = tmp_value;
eos = cp2 + CFG_MAX_VALUE + 1;
while ((cp1 < eob) && (cp2 < eos) && (*cp1 != '\0'))
{
/* substitution needed ?? */
if (*cp1 == '$' && *(cp1 + 1) == '{')
{
cp1 += 2; /* skip ${ */
cp3 = env_name;
while ((cp1 < eob) && !(*cp1 == '}' || *cp1 == ':'))
*cp3++ = *cp1++;
*cp3 = '\0'; /* terminate */
/* default substitution */
if (*cp1 == ':' && *(cp1 + 1) == '-')
{
cp1 += 2; /* skip :- */
cp3 = env_default;
while ((cp1 < eob) && (*cp1 != '}'))
*cp3++ = *cp1++;
*cp3 = '\0'; /* terminate */
}
else
{
while ((cp1 < eob) && (*cp1 != '}'))
cp1++;
}
if (*cp1 != '}')
{
dotconf_warning(configfile, DCLOG_WARNING, ERR_PARSE_ERROR,
"Unbalanced '{'");
}
else
{
cp1++; /* skip } */
if ((env_value = getenv(env_name)) != NULL)
{
strncat(cp2, env_value, eos - cp2);
cp2 += strlen(env_value);
}
else
{
strncat(cp2, env_default, eos - cp2);
cp2 += strlen(env_default);
}
}
}
*cp2++ = *cp1++;
}
*cp2 = '\0'; /* terminate buffer */
free(str);
return strdup(tmp_value);
}
int dotconf_warning(configfile_t *configfile, int type, unsigned long errnum, const char *fmt, ...)
{
va_list args;
int retval = 0;
va_start(args, fmt);
if (configfile->errorhandler != 0) /* an errorhandler is registered */
{
char msg[CFG_BUFSIZE];
vsnprintf(msg, CFG_BUFSIZE, fmt, args);
retval = configfile->errorhandler(configfile, type, errnum, msg);
}
else /* no errorhandler, do-it-yourself */
{
retval = 0;
fprintf(stderr, "%s:%ld: ", configfile->filename, configfile->line);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
}
va_end(args);
return retval;
}
void dotconf_register_options(configfile_t *configfile, const configoption_t * options)
{
int num = configfile->config_option_count;
#define GROW_BY 10
/* resize memoryblock for options blockwise */
if (configfile->config_options == NULL)
{
configfile->config_options = malloc(sizeof(void *) * (GROW_BY + 1));
}
else
{
if ( !(num % GROW_BY) )
configfile->config_options = realloc(configfile->config_options,
sizeof(void *) * (num + GROW_BY));
}
#undef GROW_BY
/* append new options */
configfile->config_options[configfile->config_option_count] = options;
configfile->config_options[ ++configfile->config_option_count ] = 0;
}
void dotconf_callback(configfile_t *configfile, callback_types type, dotconf_callback_t callback)
{
switch(type)
{
case ERROR_HANDLER:
configfile->errorhandler = (dotconf_errorhandler_t) callback;
break;
case CONTEXT_CHECKER:
configfile->contextchecker = (dotconf_contextchecker_t) callback;
break;
default:
break;
}
}
int dotconf_continue_line(char *buffer, size_t length)
{
/* ------ match [^\\]\\[\r]\n ------------------------------ */
char *cp1 = buffer + length - 1;
if (length < 2)
return 0;
if (*cp1-- != '\n')
return 0;
if (*cp1 == '\r')
cp1--;
if (*cp1-- != '\\')
return 0;
cp1[1] = 0; /* strip escape character and/or newline */
return (*cp1 != '\\');
}
int dotconf_get_next_line(char *buffer, size_t bufsize, configfile_t *configfile)
{
char *cp1, *cp2;
char buf2[CFG_BUFSIZE];
int length;
if (configfile->eof)
return 1;
cp1 = fgets(buffer, CFG_BUFSIZE, configfile->stream);
if (!cp1)
{
configfile->eof = 1;
return 1;
}
configfile->line++;
length = strlen(cp1);
while ( dotconf_continue_line(cp1, length) )
{
cp2 = fgets(buf2, CFG_BUFSIZE, configfile->stream);
if (!cp2)
{
fprintf(stderr, "[dotconf] Parse error. Unexpected end of file at "
"line %ld in file %s\n", configfile->line, configfile->filename);
configfile->eof = 1;
return 1;
}
configfile->line++;
strcpy(cp1 + length - 2, cp2);
length = strlen(cp1);
}
return 0;
}
char *dotconf_get_here_document(configfile_t *configfile, const char *delimit)
{
/* it's a here-document: yeah, what a cool feature ;) */
unsigned int limit_len;
char here_string;
char buffer[CFG_BUFSIZE];
char *here_doc = 0;
char here_limit[9]; /* max length for here-document delimiter: 8 */
struct stat finfo;
int offset = 0;
if (configfile->size <= 0)
{
if (stat(configfile->filename, &finfo))
{
dotconf_warning(configfile, DCLOG_EMERG, ERR_NOACCESS,
"[emerg] could not stat currently read file (%s)\n",
configfile->filename);
return NULL;
}
configfile->size = finfo.st_size;
}
/*
* allocate a buffer of filesize bytes; should be enough to
* prevent buffer overflows
*/
here_doc = malloc(configfile->size); /* allocate buffer memory */
memset(here_doc, 0, configfile->size);
here_string = 1;
limit_len = snprintf(here_limit, 9, "%s", delimit);
while (!dotconf_get_next_line(buffer, CFG_BUFSIZE, configfile))
{
if (!strncmp(here_limit, buffer, limit_len - 1))
{
here_string = 0;
break;
}
offset += snprintf( (here_doc + offset), configfile->size - offset - 1, "%s", buffer);
}
if (here_string)
dotconf_warning(configfile, DCLOG_WARNING, ERR_PARSE_ERROR, "Unterminated here-document!");
here_doc[offset-1] = '\0'; /* strip newline */
return (char *)realloc(here_doc, offset);
}
const char *dotconf_invoke_command(configfile_t *configfile, command_t *cmd)
{
const char *error = 0;
if ( !configfile->contextchecker
|| !(error = configfile->contextchecker(cmd, cmd->option->context)) )
{
if (!error)
error = cmd->option->callback(cmd, configfile->context);
}
return error;
}
configoption_t *dotconf_find_command(configfile_t *configfile, const char *command)
{
configoption_t *option;
int i = 0, mod = 0, done = 0;
for (option = 0, mod = 0; configfile->config_options[mod] && !done; mod++)
for (i = 0; configfile->config_options[mod][i].name[0]; i++)
{
if (!configfile->cmp_func(name,
configfile->config_options[mod][i].name, CFG_MAX_OPTION))
{
option = (configoption_t *) &configfile->config_options[mod][i];
/* TODO: this could be flagged: option overwriting by modules */
done = 1;
break; /* found it; break out */
}
}
/* handle ARG_NAME fallback */
if ( (option && option->name[0] == 0)
|| configfile->config_options[mod - 1][i].type == ARG_NAME)
{
option = (configoption_t *) &configfile->config_options[mod - 1][i];
}
return option;
}
char *dotconf_read_arg(configfile_t *configfile, char **line)
{
int sq = 0, dq = 0; /* single quote, double quote */
int done;
char *cp1 = *line;
char *cp2, *eos;
char buf[CFG_MAX_VALUE];
memset(buf, 0, CFG_MAX_VALUE);
done = 0;
cp2 = buf;
eos = cp2 + CFG_MAX_VALUE - 1;
if (*cp1 == '#' || !*cp1)
return NULL;
/* skip all whitespace between 2 arguments */
while ( isspace((int)cp1[0]) && (*cp1 != '\0'))
cp1++;
while ((*cp1 != '\0') && (cp2 != eos) && !done)
{
switch (*cp1)
{
case '\'': /* single quote */
if (dq)
break; /* already double quoting, break out */
if (sq)
sq--; /* already single quoting, clear state */
else if (!sq)
sq++; /* set state for single quoting */
break;
case '"': /* double quote */
if (sq)
break; /* already single quoting, break out */
if (dq)
dq--; /* already double quoting, clear state */
else if (!dq)
dq++; /* set state for double quoting */
break;
case '\\': /* protected chars */
if (!cp1[1]) /* dont protect NUL */
break;
*cp2++ = *(++cp1);
cp1++; /* skip the protected one */
continue;
break;
default:
break;
}
/* unquoted space: start a new option argument */
if (isspace((int)*cp1) && !dq && !sq)
{
*cp2 = '\0'; /* terminate current argument */
break;
}
/* unquoted, unescaped comment-hash ; break out */
else if (*cp1 == '#' && !dq && !sq)
{
*cp2 = '\0';
break;
}
/* not space or quoted:eat it; dont take quote if quoting */
else if ( (!isspace((int)*cp1) && !dq && !sq && *cp1 != '"' && *cp1 != '\'')
|| (dq && (*cp1 != '"')) || (sq && *cp1 != '\'') )
{
*cp2++ = *cp1;
}
cp1++;
}
*line = cp1;
/* FIXME: escaping substitutes does not work
Subst ${HOME} \$\{HOME\}
BOTH! will be substituted, which is somewhat wrong, ain't it ?? :-(
*/
if ( (configfile->flags & DONT_SUBSTITUTE) == DONT_SUBSTITUTE )
return buf[0] ? strdup(buf) : NULL;
return buf[0] ? dotconf_substitute_env(configfile, strdup(buf)) : NULL;
}
const char *dotconf_handle_command(configfile_t *configfile, char *buffer)
{
char *cp1, *cp2; /* generic char pointer */
char *eob; /* end of buffer; end of string */
int i; /* generic counter, mod holds the module index */
int memory_cleanup;
const char *error; /* error message as return by callback */
command_t command; /* command structure */
configoption_t *option; /* matched option from config_options */
i = memory_cleanup = 0;
error = 0;
/* TODO????: insert 'skipped_line' callback code */
/* clear fields */
memset(&command, 0, sizeof(command_t));
name[0] = 0;
/* initialize char pointer */
cp1 = buffer;
eob = cp1 + strlen(cp1); /* calculate end of buffer */
/* skip any whitspace of indented lines */
while ((cp1 < eob) && (isspace((int)*cp1)))
cp1++;
/* ignore comments and empty lines */
if (!cp1 || !*cp1 || *cp1 == '#' || *cp1 == '\n' || *cp1 == EOF)
return NULL;
/* skip line if it only contains whitespace */
if (cp1 == eob)
return NULL;
/* get first token: read the name of a possible option */
cp2 = name;
while ((*cp1 != '\0') && (!isspace((int)*cp1)))
*cp2++ = *cp1++;
*cp2 = '\0';
option = dotconf_find_command(configfile, name);
if (!option || option->callback == 0)
{
dotconf_warning(configfile, DCLOG_INFO, ERR_UNKNOWN_OPTION,
"Unknown Config-Option: '%s'", name);
return NULL;
}
/* fill in the command_t structure with values we already know */
command.name = option->type == ARG_NAME ? name : option->name;
command.option = option;
command.context = configfile->context;
command.configfile = configfile;
command.data.list = (char **)calloc(CFG_VALUES, sizeof(char *));
if (option->type == ARG_RAW)
{
/* if it is an ARG_RAW type, save some time and call the
callback now */
command.data.str = cp1;
}
else if (option->type == ARG_STR)
{
/* check if it's a here-document and act accordingly */
char *cp3 = cp1;
/* skip whitespace */
while ((cp3 < eob) && (*cp3 != '\0') && (isspace((int)*cp3)))
cp3++;
if (!strncmp("<<", cp3, 2)) /* here document magic bytes :) */
{
command.data.str = dotconf_get_here_document(configfile, cp3 + 2);
command.arg_count = 1;
memory_cleanup = !!command.data.str;
}
}
if ( !(option->type == ARG_STR && command.data.str != 0) )
{
/* skip whitespace */
while ((cp1 < eob) && (*cp1 != '\0') && (isspace((int)*cp1)))
cp1++;
command.arg_count = 0;
while ( command.arg_count < (CFG_VALUES - 1)
&& (command.data.list[command.arg_count] = dotconf_read_arg(configfile, &cp1)) )
{
command.arg_count++;
}
/* skip whitespace again */
while ((cp1 < eob) && (*cp1 != '\0') && (isspace((int)*cp1)))
cp1++;
if (command.arg_count && command.data.list[command.arg_count-1] && *cp1)
command.data.list[command.arg_count++] = strdup(cp1);
/* has an option entry been found before or do we have to use a fallback? */
if ((option->name && option->name[0] > 32) || option->type == ARG_NAME)
{
/* found it, now check the type of args it wants */
switch (option->type)
{
case ARG_TOGGLE:
{
/* the value is true if the argument is Yes, On or 1 */
if (command.arg_count < 1)
{
dotconf_warning(configfile, DCLOG_WARNING, ERR_WRONG_ARG_COUNT,
"Missing argument to option '%s'", name);
goto out;
}
command.data.value = CFG_TOGGLED(command.data.list[0]);
break;
}
case ARG_INT:
{
if (command.arg_count < 1)
{
dotconf_warning(configfile, DCLOG_WARNING, ERR_WRONG_ARG_COUNT,
"Missing argument to option '%s'", name);
goto out;
}
sscanf(command.data.list[0], "%li", &command.data.value);
break;
}
case ARG_STR:
{
if (command.arg_count < 1)
{
dotconf_warning(configfile, DCLOG_WARNING, ERR_WRONG_ARG_COUNT,
"Missing argument to option '%s'", name);
goto out;
}
command.data.str = command.data.list[0];
break;
}
case ARG_NAME: /* fall through */
case ARG_LIST:
case ARG_NONE:
case ARG_RAW: /* this has been handled before */
default:
break; //win32 edit
}
}
}
error = dotconf_invoke_command(configfile, &command);
out:
if (command.option->type == ARG_STR && memory_cleanup)
free(command.data.str);
else
{
for (i = 0; i < command.arg_count; i++)
free(command.data.list[i]);
}
free(command.data.list);
return error;
}
const char *dotconf_command_loop_until_error(configfile_t *configfile)
{
char buffer[CFG_BUFSIZE];
while ( !(dotconf_get_next_line(buffer, CFG_BUFSIZE, configfile)) )
{
const char *error = dotconf_handle_command(configfile, buffer);
if ( error )
return error;
}
return NULL;
}
int dotconf_command_loop(configfile_t *configfile)
{
/* ------ returns: 0 for failure -- !0 for success ------------------------------------------ */
char buffer[CFG_BUFSIZE];
while ( !(dotconf_get_next_line(buffer, CFG_BUFSIZE, configfile)) )
{
const char *error = dotconf_handle_command(configfile, buffer);
if ( error != NULL )
{
if ( dotconf_warning(configfile, DCLOG_ERR, 0, error) )
return 0;
}
}
return 1;
}
configfile_t *dotconf_create(char *fname, const configoption_t * options,
context_t *context, unsigned long flags)
{
configfile_t *new = 0;
char *dc_env;
if (access(fname, R_OK))
{
fprintf(stderr, "Error opening configuration file '%s'\n", fname);
return NULL;
}
new = calloc(1, sizeof(configfile_t));
if (!(new->stream = fopen(fname, "r")))
{
fprintf(stderr, "Error opening configuration file '%s'\n", fname);
free(new);
return NULL;
}
new->flags = flags;
new->filename = strdup(fname);
new->includepath = malloc(CFG_MAX_FILENAME);
new->includepath[0] = 0x00;
/* take includepath from environment if present */
if ((dc_env = getenv(CFG_INCLUDEPATH_ENV)) != NULL)
{
snprintf(new->includepath, CFG_MAX_FILENAME, "%s", dc_env);
}
new->context = context;
dotconf_register_options(new, dotconf_options);
dotconf_register_options(new, options);
if ( new->flags & CASE_INSENSITIVE )
new->cmp_func = strncasecmp;
else
new->cmp_func = strncmp;
return new;
}
void dotconf_cleanup(configfile_t *configfile)
{
if (configfile->stream)
fclose(configfile->stream);
if (configfile->filename)
free(configfile->filename);
if (configfile->config_options)
free(configfile->config_options);
if (configfile->includepath)
free(configfile->includepath);
free(configfile);
}
/* ------ internal utility function that verifies if a character is in the WILDCARDS list -- */
int dotconf_is_wild_card(char value)
{
int retval = 0;
int i;
int wildcards_len = strlen(WILDCARDS);
for (i=0;i<wildcards_len;i++)
{
if (value == WILDCARDS[i])
{
retval = 1;
break;
}
}
return retval;
}
/* ------ internal utility function that calls the appropriate routine for the wildcard passed in -- */
int dotconf_handle_wild_card(command_t* cmd, char wild_card, char* path, char* pre, char* ext)
{
int retval = 0;
switch (wild_card)
{
case '*':
retval = dotconf_handle_star(cmd,path,pre,ext);
break;
case '?':
retval = dotconf_handle_question_mark(cmd,path,pre,ext);
break;
default:
retval = -1;
}
return retval;
}
/* ------ internal utility function that frees allocated memory from dotcont_find_wild_card -- */
void dotconf_wild_card_cleanup(char* path, char* pre)
{
if (path != NULL)
{
free(path);
}
if (pre != NULL)
{
free(pre);
}
}
/* ------ internal utility function to check for wild cards in file path -- */
/* ------ path and pre must be freed by the developer ( dotconf_wild_card_cleanup) -- */
int dotconf_find_wild_card(char* filename, char* wildcard, char** path, char** pre, char** ext)
{
int retval = -1;
int prefix_len = 0;
int tmp_count = 0;
char* tmp = 0;
int found_path = 0;
int len = strlen(filename);
if (wildcard != NULL && len > 0 && path != NULL && pre != NULL && ext != NULL )
{
prefix_len = strcspn(filename,WILDCARDS); /* find any wildcard in WILDCARDS */
if ( prefix_len < len ) /* Wild card found */
{
tmp = filename + prefix_len;
tmp_count = prefix_len + 1;
while ( tmp != filename && *(tmp) != '/' )
{
tmp--;
tmp_count--;
}
if ( *(tmp) == '/' )
{
*path = (char*)malloc(tmp_count+1);
found_path = 1;
} else
*path = (char*)malloc(1);
*pre = (char*)malloc((prefix_len-(tmp_count-(found_path?0:1)))+1);
if ( *path && *pre )
{
if (found_path)
strncpy(*path,filename,tmp_count);
(*path)[tmp_count] = '\0';
strncpy(*pre,(tmp+(found_path?1:0)),
(prefix_len-(tmp_count-(found_path?0:1))));
(*pre)[(prefix_len-(tmp_count-(found_path?0:1)))] = '\0';
*ext = filename + prefix_len;
*wildcard = (**ext);
(*ext)++;
retval = prefix_len;
}
}
}
return retval;
}
/* ------ internal utility function that compares two stings from back to front -- */
int dotconf_strcmp_from_back(const char* s1, const char* s2)
{
int retval = 0;
int i,j;
int len_1 = strlen(s1);
int len_2 = strlen(s2);
for (i=len_1,j=len_2;(i>=0 && j>=0);i--,j--)
{
if (s1[i] != s2[j])
{
retval = -1;
break;
}
}
return retval;
}
/* ------ internal utility function that determins if a string matches the '?' criteria -- */
int dotconf_question_mark_match(char* dir_name, char* pre, char* ext)
{
int retval = -1;
int dir_name_len = strlen(dir_name);
int pre_len = strlen(pre);
int ext_len = strlen(ext);
int w_card_check = strcspn(ext,WILDCARDS);
if ( (w_card_check < ext_len) && (strncmp(dir_name,pre,pre_len) == 0)
&& (strcmp(dir_name,".") != 0 ) && (strcmp(dir_name,"..") != 0) )
{
retval = 1; /* Another wildcard found */
} else {
if ((dir_name_len >= pre_len) &&
(strncmp(dir_name,pre,pre_len) == 0) &&
(strcmp(dir_name,".") != 0 ) &&
(strcmp(dir_name,"..") != 0))
{
retval = 0; /* Matches no other wildcards */
}
}
return retval;
}
/* ------ internal utility function that determins if a string matches the '*' criteria -- */
int dotconf_star_match(char* dir_name, char* pre, char* ext)
{
int retval = -1;
int dir_name_len = strlen(dir_name);
int pre_len = strlen(pre);
int ext_len = strlen(ext);
int w_card_check = strcspn(ext,WILDCARDS);
if ( (w_card_check < ext_len) && (strncmp(dir_name,pre,pre_len) == 0)
&& (strcmp(dir_name,".") != 0 ) && (strcmp(dir_name,"..") != 0) )
{
retval = 1; /* Another wildcard found */
} else {
if ((dir_name_len >= (ext_len + pre_len)) &&
(dotconf_strcmp_from_back(dir_name,ext) == 0) &&
(strncmp(dir_name,pre,pre_len) == 0) &&
(strcmp(dir_name,".") != 0 ) &&
(strcmp(dir_name,"..") != 0))
{
retval = 0; /* Matches no other wildcards */
}
}
return retval;
}
/* ------ internal utility function that determins matches for filenames with -- */
/* ------ a '?' in name and calls the Internal Include function on that filename -- */
int dotconf_handle_question_mark(command_t* cmd, char* path, char* pre, char* ext)
{
configfile_t *included;
DIR* dh = 0;
struct dirent* dirptr = 0;
char new_pre[CFG_MAX_FILENAME];
char already_matched[CFG_MAX_FILENAME];
char wc = '\0';
char* new_path = 0;
char* wc_path = 0;
char* wc_pre = 0;
char* wc_ext = 0;
int pre_len;
int new_path_len;
int name_len = 0;
int alloced = 0;
int match_state = 0;
pre_len = strlen(pre);
if ((dh = opendir(path)) != NULL)
{
while ( (dirptr = readdir(dh)) != NULL )
{
match_state = dotconf_question_mark_match(dirptr->d_name,pre,ext);
if (match_state >= 0)
{
name_len = strlen(dirptr->d_name);
new_path_len = strlen(path) + name_len + strlen(ext) + 1;
if ( !alloced )
{
if ((new_path = (char*)malloc(new_path_len)) == NULL )
{
return -1;
}
alloced = new_path_len;
} else {
if ( new_path_len > alloced )
{
if ( realloc(new_path,new_path_len) == NULL )
{
free(new_path);
return -1;
}
}
}
if (match_state == 1)
{
strncpy(new_pre,dirptr->d_name,(name_len > pre_len)?(pre_len+1):pre_len);
new_pre[(name_len > pre_len)?(pre_len+1):pre_len] = '\0';
sprintf(new_path,"%s%s%s",path,new_pre,ext);
if (strcmp(new_path,already_matched) == 0)
{
continue; /* Already searched this expression */
} else {
strcpy(already_matched,new_path);
}
if (dotconf_find_wild_card(new_path,&wc,&wc_path,&wc_pre,&wc_ext) >= 0)
{
if ( dotconf_handle_wild_card(cmd,wc,wc_path,wc_pre,wc_ext) < 0)
{
dotconf_warning(cmd->configfile, DCLOG_WARNING, ERR_INCLUDE_ERROR,
"Error occured while processing wildcard %c\n"
"Filename is '%s'\n", wc, new_path);
free(new_path);
dotconf_wild_card_cleanup(wc_path,wc_pre);
return -1;
}
dotconf_wild_card_cleanup(wc_path,wc_pre);
continue;
}
}
sprintf(new_path,"%s%s",path,dirptr->d_name);
if (access(new_path, R_OK))
{
dotconf_warning(cmd->configfile, DCLOG_WARNING, ERR_INCLUDE_ERROR,
"Cannot open %s for inclusion.\n"
"IncludePath is '%s'\n", new_path, cmd->configfile->includepath);
return -1;
}
included = dotconf_create(new_path, cmd->configfile->config_options[1],
cmd->configfile->context, cmd->configfile->flags);
if (included)
{
included->errorhandler = cmd->configfile->errorhandler;
included->contextchecker = cmd->configfile->contextchecker;
dotconf_command_loop(included);
dotconf_cleanup(included);
}
}
}
closedir(dh);
free(new_path);
}
return 0;
}
/* ------ internal utility function that determins matches for filenames with --- */
/* ------ a '*' in name and calls the Internal Include function on that filename -- */
int dotconf_handle_star(command_t* cmd, char* path, char* pre, char* ext)
{
configfile_t *included;
DIR* dh = 0;
struct dirent* dirptr = 0;
char new_pre[CFG_MAX_FILENAME];
char new_ext[CFG_MAX_FILENAME];
char already_matched[CFG_MAX_FILENAME];
char wc = '\0';
char* new_path = 0;
char* s_ext = 0;
char* t_ext = 0;
char* sub = 0;
char* wc_path = 0;
char* wc_pre = 0;
char* wc_ext = 0;
int pre_len;
int new_path_len;
int name_len = 0;
int alloced = 0;
int match_state = 0;
int t_ext_count = 0;
int sub_count = 0;
pre_len = strlen(pre);
memset(already_matched,0,CFG_MAX_FILENAME);
s_ext = ext;
while (dotconf_is_wild_card(*s_ext)) /* remove trailing wild-cards proceeded by * */
{
s_ext++;
}
t_ext = s_ext;
while(t_ext != NULL && !(dotconf_is_wild_card(*t_ext)) && *t_ext != '\0')
{
t_ext++; /* find non-wild-card string */
t_ext_count++;
}
strncpy(new_ext,s_ext,t_ext_count);
new_ext[t_ext_count] = '\0';
if ((dh = opendir(path)) != NULL)
{
while ( (dirptr = readdir(dh)) != NULL )
{
sub_count = 0;
t_ext_count = 0;
match_state = dotconf_star_match(dirptr->d_name,pre,s_ext);
if (match_state >= 0)
{
name_len = strlen(dirptr->d_name);
new_path_len = strlen(path) + name_len + strlen(s_ext) + 1;
if ( !alloced )
{
if ((new_path = (char*)malloc(new_path_len)) == NULL )
{
return -1;
}
alloced = new_path_len;
} else {
if ( new_path_len > alloced )
{
if ( realloc(new_path,new_path_len) == NULL )
{
free(new_path);
return -1;
}
}
}
if (match_state == 1)
{
if ((sub = strstr((dirptr->d_name+pre_len),new_ext)) == NULL)
{
continue;
}
while (sub != dirptr->d_name)
{
sub--;
sub_count++;
}
if (sub_count + t_ext_count > name_len)
{
continue;
}
strncpy(new_pre,dirptr->d_name,(sub_count+t_ext_count));
new_pre[sub_count+t_ext_count] = '\0';
strcat(new_pre,new_ext);
sprintf(new_path,"%s%s%s",path,new_pre,t_ext);
if (strcmp(new_path,already_matched) == 0)
{
continue; /* Already searched this expression */
} else {
strcpy(already_matched,new_path);
}
if (dotconf_find_wild_card(new_path,&wc,&wc_path,&wc_pre,&wc_ext) >= 0)
{
if ( dotconf_handle_wild_card(cmd,wc,wc_path,wc_pre,wc_ext) < 0)
{
dotconf_warning(cmd->configfile, DCLOG_WARNING, ERR_INCLUDE_ERROR,
"Error occured while processing wildcard %c\n"
"Filename is '%s'\n", wc, new_path);
free(new_path);
dotconf_wild_card_cleanup(wc_path,wc_pre);
return -1;
}
dotconf_wild_card_cleanup(wc_path,wc_pre);
continue;
}
}
sprintf(new_path,"%s%s",path,dirptr->d_name);
if (access(new_path, R_OK))
{
dotconf_warning(cmd->configfile, DCLOG_WARNING, ERR_INCLUDE_ERROR,
"Cannot open %s for inclusion.\n"
"IncludePath is '%s'\n", new_path, cmd->configfile->includepath);
return -1;
}
included = dotconf_create(new_path, cmd->configfile->config_options[1],
cmd->configfile->context, cmd->configfile->flags);
if (included)
{
included->errorhandler = cmd->configfile->errorhandler;
included->contextchecker = cmd->configfile->contextchecker;
dotconf_command_loop(included);
dotconf_cleanup(included);
}
}
}
closedir(dh);
free(new_path);
}
return 0;
}
/* ------ callbacks of the internal option (Include, IncludePath) ------------------------------- */
DOTCONF_CB(dotconf_cb_include)
{
char *filename = 0;
configfile_t *included;
char wild_card;
char* path = 0;
char* pre = 0;
char* ext = 0;
if (cmd->configfile->includepath
&& cmd->data.str[0] != '/' && cmd->configfile->includepath[0] != '\0')
{
/* relative file AND include path is used */
int len, inclen;
char *sl;
inclen = strlen(cmd->configfile->includepath);
if (( len = (strlen(cmd->data.str) + inclen + 1)) == CFG_MAX_FILENAME)
{
dotconf_warning(cmd->configfile, DCLOG_WARNING, ERR_INCLUDE_ERROR,
"Absolute filename too long (>%d)", CFG_MAX_FILENAME);
return NULL;
}
if (cmd->configfile->includepath[inclen - 1] == '/')
sl = "";
else
{
sl = "/";
len++;
}
filename = malloc(len);
snprintf(filename, len, "%s%s%s",
cmd->configfile->includepath, sl, cmd->data.str);
}
else /* fully qualified, or no includepath */
filename = strdup(cmd->data.str);
/* Added wild card support here */
if (dotconf_find_wild_card(filename,&wild_card,&path,&pre,&ext) >= 0)
{
if ( dotconf_handle_wild_card(cmd,wild_card,path,pre,ext) < 0)
{
dotconf_warning(cmd->configfile, DCLOG_WARNING, ERR_INCLUDE_ERROR,
"Error occured while attempting to process %s for inclusion.\n"
"IncludePath is '%s'\n", filename, cmd->configfile->includepath);
}
dotconf_wild_card_cleanup(path,pre);
free(filename);
return NULL;
}
if (access(filename, R_OK))
{
dotconf_warning(cmd->configfile, DCLOG_WARNING, ERR_INCLUDE_ERROR,
"Cannot open %s for inclusion.\n"
"IncludePath is '%s'\n", filename, cmd->configfile->includepath);
free(filename);
return NULL;
}
included = dotconf_create(filename, cmd->configfile->config_options[1],
cmd->configfile->context, cmd->configfile->flags);
if (included)
{
included->contextchecker = (dotconf_contextchecker_t) cmd->configfile->contextchecker;
included->errorhandler = (dotconf_errorhandler_t) cmd->configfile->errorhandler;
dotconf_command_loop(included);
dotconf_cleanup(included);
}
free(filename);
return NULL;
}
DOTCONF_CB(dotconf_cb_includepath)
{
char *env = getenv(CFG_INCLUDEPATH_ENV);
/* environment overrides configuration file setting */
if (!env)
snprintf(cmd->configfile->includepath, CFG_MAX_FILENAME, "%s", cmd->data.str);
return NULL;
}
/* vim:set ts=4: */