diff options
Diffstat (limited to 'src/common/config.c')
-rw-r--r-- | src/common/config.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/src/common/config.c b/src/common/config.c new file mode 100644 index 000000000..99961f147 --- /dev/null +++ b/src/common/config.c @@ -0,0 +1,347 @@ +/* + * config.c + * Functions for the manipulation of configuration files. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.7 2002/04/02 14:27:11 badbytes + * Final finishes. + * + * Revision 1.6 2002/01/27 19:23:03 mp292 + * Fixed a bug in parameter checking. + * + * Revision 1.5 2002/01/26 18:42:15 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.4 2002/01/21 21:07:56 mp292 + * Parameter checking was missing in some functions. + * + * Revision 1.3 2001/12/07 09:38:03 badbytes + * Tested. + * + * Revision 1.2 2001/12/06 15:43:50 badbytes + * config.c compiles. Proceeding to test it. + * + * Revision 1.1 2001/11/22 01:20:27 mp292 + * Functions for dealing with configuration files. + * + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include "config.h" +#include "log.h" + +/* open configuration file for reading */ +FILE *open_config(const unsigned char *filename) +{ + FILE *f; + + if (filename) /* non-NULL filename */ + { + if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) == strlen(filename)) /* filename consists of legal characters only */ + { + f = fopen(filename, "r"); + + return f; + } /* filename consists of legal characters only */ + else /* illegal values in filename */ + { + return NULL; + } /* illegal values in filename */ + } /* non-NULL filename */ + else /* NULL filename */ + return NULL; +} + +/* close configuration file */ +int close_config(FILE *f) +{ + int retval = 0; + + if (f) /* valid file descriptor */ + { + retval = fclose(f); + + return retval; + } /* valid file descriptor */ + else + return -1; +} + +/* parse the config file and obtain the required option values */ +int parse_config(FILE *f, config_opt_t *option) +{ + unsigned char keyword[CONFIG_KEYWORD_MAXLEN+1]; /* for storing the option keyword */ + + unsigned char *buffer = NULL; /* option value */ + size_t buflen = 0; + + char *errtest = NULL; /* used for testing correctness of strtol() etc. */ + + unsigned int i_keyword = 0; /* current position within keyword */ + unsigned int i_buf = 0; /* current position within buffer */ + + char c=0; /* input char */ + + unsigned int state=0; /* internal state + * 0 - trying to find a keyword + * 1 - reading a keyword + * 2 - keyword read and recognized, looking for the option value + * 3 - reading the option value + * 4 - option value read + * 5 - inside a comment + */ + + int retval=0; /* return value */ + + int lineno=1; /* current line number */ + int curopt=-1; /* current option, as an indexed in config_opt_t */ + int i; + + if ( (f==NULL) || (option==NULL) ) /* invalid parameters */ + return -1; + + fseek(f,0,SEEK_SET); /* make sure we start at the beginning of file */ + + for (;;) /* infinite loop */ + { + c = getc(f); + + if ((c == '\n') || (c == EOF)) + { + if (state == 1) /* reading a keyboard */ + { + log(LOG_ERR,"Error parsing the configuration file on line %d.", lineno); + i_keyword = 0; + state = 0; + retval = -1; + break; + } /* reading a keyboard */ + else if (state == 2) /* keyword read and recognized */ + { + log(LOG_ERR,"Error parsing option %s on line %d.",option[curopt].keyword, lineno); + i_keyword = 0; + state = 0; + option[curopt].err=-1; + retval = -1; + break; + } /* keyboard read and recognized */ + else if (state == 3) /* reading the option value */ + { + buffer[i_buf++] = 0; /* add NULL character to terminate the string */ + state = 4; + /* conversion and copying the value into config_opt_t is done later on */ + } /* reading the option value */ + else if (state == 5) /* reached end of comment */ + state = 0; + + if (c == EOF) + { + log(LOG_DEBUG,"parse_config() : Reached eof on line %d.",lineno); + break; + } + else + { + log(LOG_DEBUG,"parse_config() : Reached eol on line %d.", lineno); + lineno++; + } + } + else if ( (state==0) && (c == '#') ) /* lines beginning with # are ignored */ + { + log(LOG_DEBUG,"parse_config() : Line %d begins with #.",lineno); + state = 5; + } + else if ( (state==0) && (isspace(c)) ) /* leading whitespace is ignored */ + ; + else if ( (state==1) && (isspace(c)) ) /* have apparently read in all of the keyword */ + { + keyword[i_keyword++] = 0; + curopt = -1; + for (i=0;option[i].keyword != NULL;i++) /* try and identify the keyword */ + { + if (!strncmp(keyword,option[i].keyword,CONFIG_KEYWORD_MAXLEN)) + { + curopt = i; + break; + } + } /* try and identify the keyword */ + + if (curopt == -1) /* can't recognise the keyword */ + { + log(LOG_ERR,"Error parsing the configuration file. Cannot recognize keyword %s on line %d.",keyword,lineno); + retval=-1; + break; + } + else + state = 2; + } + else if ( (state==2) && (isspace(c)) ) /* whitespace separating keyword and value is ignored */ + ; + else if ( (state==3) && (isspace(c)) ) /* have apparently finished reading the option value */ + { + buffer[i_buf++]=0; + state = 4; + } + else /* all other characters */ + { + if (state == 0) /* first character of the keyword */ + { + log(LOG_DEBUG, "parse_config() : %c is the start of a keyword on line %d.",c,lineno); + state = 1; + i_keyword = 0; + keyword[i_keyword++] = c; + } + else if (state == 1) /* keep on reading the keyword */ + { + log(LOG_DEBUG,"parse_config() : %c is a character in the keyword on line %d.",c,lineno); + if (i_keyword < CONFIG_KEYWORD_MAXLEN) /* check for buffer overflow */ + keyword[i_keyword++] = c; + else + { + log(LOG_ERR,"Error parsing the configuration file. Keyword on line %d exceeds %d characters.",lineno,CONFIG_KEYWORD_MAXLEN); + retval=-1; + break; + } + } + else if (state == 2) /* first character of the value */ + { + log(LOG_DEBUG,"parse_config() : %c is the first character of the option value on line %d.",c,lineno); + state = 3; + i_buf=0; + buflen = CONFIG_VALUE_MAXLEN+1; /* allocate memory for the value buffer */ + buffer = (char *)malloc(buflen); + if (!buffer) + { + log(LOG_ERR,"Could not allocate memory."); + retval=-1; + break; + } else + buffer[i_buf++]=c; + } + else if (state == 3) /* keep on reading the value */ + { + log(LOG_DEBUG,"parse_config() : %c is a character in the value of the keyword on line %d.",c,lineno); + if (i_buf >= buflen) + { + log(LOG_ERR,"Length of keyword value on line %u exceeds the length limit (%u).",lineno, CONFIG_VALUE_MAXLEN); + retval=-1; + break; + } + + buffer[i_buf++]=c; + } + else if (state == 5) + ; /* character is part of a comment, skip */ + else /* unexpected error */ + { + log(LOG_ERR,"Unexpected error while parsing the configuration file."); + log(LOG_DEBUG,"parse_config() : Encountered a non-delimiter character while not in states 0,1,2 or 3!"); + break; + } + } + + if (state==4) /* convert the value of the option to the appropriate type and write into OPT */ + { + switch(option[curopt].r_type) /* consider each type separately */ + { + case CONFIG_TYPE_STRING: + /* resize the buffer to fit the data exactly */ + buffer = (char *)realloc(buffer,i_buf); + if (!buffer) + { + log(LOG_ERR,"Could not allocate memory."); + return -1; + } + option[curopt].r.str = buffer; + option[curopt].err = 1; + break; + + case CONFIG_TYPE_CHAR: + option[curopt].r.c = *buffer; + option[curopt].err = 1; + break; + + case CONFIG_TYPE_INT: + errtest = NULL; + option[curopt].r.i = (int)strtol(buffer,&errtest,0); + if ((unsigned char *)errtest == buffer) + { + log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno); + option[curopt].err = -1; + if (buffer) + free(buffer); + return -1; + } + else + option[curopt].err = 1; + break; + + case CONFIG_TYPE_LONG: + errtest = NULL; + option[curopt].r.l = strtol(buffer,&errtest,0); + if ((unsigned char *)errtest == buffer) + { + log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno); + option[curopt].err = -1; + if (buffer) + free(buffer); + return -1; + } + else + option[curopt].err = 1; + break; + + case CONFIG_TYPE_DOUBLE: + errtest = NULL; + option[curopt].r.d = strtod(buffer,&errtest); + if ((unsigned char *)errtest == buffer) + { + log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno); + option[curopt].err = -1; + if (buffer) + free(buffer); + return -1; + } + else + option[curopt].err = 1; + break; + + default: /* unexpected type */ + log(LOG_ERR, "Error parsing configuration file. Unrecognized option type!"); + if (buffer) + free(buffer); + return -1; + } + + /* clean up */ + if (option[curopt].r_type != CONFIG_TYPE_STRING) + { + if (buffer) + free(buffer); + buflen=0; + } + + state = 0; + curopt = -1; + i_buf=0; + i_keyword=0; + } + + + } /* infinite loop */ + + return retval; +} |