/*
* config.c - Read configuration files
*
* Copyright (C) 1997-2003 Gero Kuhlmann <gero@gkminix.han.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: config.c,v 1.5 2003/03/09 00:43:11 gkminix Exp $
*/
#include <common.h>
#include <nblib.h>
#include "privlib.h"
/*
* Variables private to this module
*/
static int curline; /* current line in input file */
static int errors; /* number of errors found */
static char *inbuf = NULL; /* input buffer pointer */
static char *curpos = NULL; /* current write position in buffer */
static int inbufsize = 0; /* input buffer size */
/*
* Put a character at the end of the input buffer
*/
static void putbuf(c)
char c;
{
#define BUFSIZE 1024
int i;
char *cp;
/* Allocate new line buffer if necessary */
if (inbuf == NULL) {
inbuf = curpos = (char *)nbmalloc(BUFSIZE);
inbufsize = BUFSIZE;
}
/* Check if we have to start with a fresh buffer */
if (curpos == NULL)
curpos = inbuf;
/* Check that we don't get at the end of the input buffer */
i = (curpos - inbuf);
if (i >= (inbufsize - 1)) {
cp = (char *)nbmalloc(inbufsize + BUFSIZE);
memcpy(cp, inbuf, inbufsize);
free(inbuf);
inbuf = cp;
inbufsize += BUFSIZE;
curpos = inbuf + i;
}
/* Finally put new character into line buffer */
*curpos++ = c;
#undef BUFSIZE
}
/*
* Read a line from the setup file
*/
static char *readline(fd)
FILE *fd;
{
int inquote = FALSE;
int backslash = FALSE;
int c = 0;
char *cp;
/* Check for EOF on input file */
if (feof(fd))
return(NULL);
/* Read one line */
while (TRUE) {
/* Reset input line buffer write pointer */
curpos = NULL;
/* Read one line */
while (TRUE) {
if ((c = fgetc(fd)) == EOF)
break;
if (backslash) {
/*
* If preceding character was a backslash, we need
* special handling
*/
switch (c) {
case 'n':
putbuf('\n');
break;
case 'r':
putbuf('\r');
break;
case 't':
putbuf('\t');
break;
case '\n':
/* Ignore newline */
curline++;
break;
default:
putbuf(c);
break;
}
backslash = FALSE;
continue;
}
if (inquote) {
/*
* Within quotes we handle all characters verbatim
* and don't do any processing except backslash which
* has already been taken care of
*/
if (inquote && c == '"')
inquote = FALSE;
if (c == '\n')
curline++;
putbuf(c);
continue;
}
if (c == '#') {
/*
* When encountering a comment sign, skip the rest of
* the line until newline
*/
while ((c = fgetc(fd)) != '\n' && c != EOF)
;
if (c == EOF)
break;
curline++;
continue;
}
/*
* Check for end of line and special characters, and then
* put the character into the input buffer
*/
if (c == '\n') {
curline++;
break;
} else if (c == '"') {
inquote = TRUE;
putbuf(c);
} else if (c == '\\') {
backslash = TRUE;
} else
putbuf(c);
}
putbuf('\0');
/* Remove trailing whitespace */
cp = curpos - 2;
while (cp >= inbuf && (*cp == ' ' || *cp == '\t'))
cp--;
*(++cp) = '\0';
/* Terminate reading if not an empty line */
if (cp > inbuf) {
/* Skip leading whitespace */
for (cp = inbuf; *cp && (*cp == ' ' || *cp == '\t'); cp++)
;
return(cp);
} else if (c == EOF)
return(NULL);
}
}
/*
* Decode one line of the configuration file
*/
static void decode(params, buf)
struct paramdef *params;
char *buf;
{
char *cp, **cpp;
char *arg;
int i, j;
/* Find equal sign and isolate parameter name */
if ((cp = strchr(buf, '=')) == NULL) {
prnerr1("[%d] invalid line", curline);
errors++;
return;
}
arg = cp + 1;
for (--cp; cp > buf && (*cp == ' ' || *cp == '\t'); cp--)
;
*(++cp) = '\0';
/* Find parameter name in list */
for (i = 0; params[i].name != NULL; i++)
if (!strcmp(buf, params[i].name))
break;
if (params[i].name == NULL) {
prnerr1("[%d] invalid parameter", curline);
errors++;
return;
}
/* Decode argument */
while (*arg && (*arg == ' ' || *arg == '\t'))
arg++;
switch (params[i].type) {
case par_string:
if (arg[0] == '"') {
j = strlen(arg);
if (arg[j - 1] != '"') {
prnerr1("[%d] missing end quote", curline);
errors++;
} else {
arg[j - 1] = '\0';
arg++;
}
}
copystr(params[i].valptr.strptr, arg);
break;
case par_bool:
if (!strcmp(arg, "true") || !strcmp(arg, "TRUE"))
*(params[i].valptr.boolptr) = BOOL_TRUE;
else if (!strcmp(arg, "false") || !strcmp(arg, "FALSE"))
*(params[i].valptr.boolptr) = BOOL_FALSE;
else {
prnerr1("[%d] invalid boolean argument", curline);
errors++;
}
break;
case par_int:
j = 0;
if (arg[0] == '0' && toupper(arg[1]) == 'X') {
arg += 2;
for (cp = arg; *cp; cp++)
if (!isxdigit(*cp))
break;
if (!*cp)
j = sscanf(arg, "%x", params[i].valptr.intptr);
} else {
if (*arg == '+') {
arg++;
cp = arg;
} else if (*arg == '-') {
cp = arg + 1;
} else {
cp = arg;
}
while (*cp)
if (!isdigit(*(cp++)))
break;
if (!*cp)
j = sscanf(arg, "%d", params[i].valptr.intptr);
}
if (j != 1) {
prnerr1("[%d] invalid short integer argument", curline);
errors++;
}
break;
case par_long:
j = 0;
if (arg[0] == '0' && toupper(arg[1]) == 'X') {
arg += 2;
for (cp = arg; *cp; cp++)
if (!isxdigit(*cp))
break;
if (!*cp)
j = sscanf(arg, "%lx", params[i].valptr.longptr);
} else {
if (*arg == '+') {
arg++;
cp = arg;
} else if (*arg == '-') {
cp = arg + 1;
} else {
cp = arg;
}
while (*cp)
if (!isdigit(*(cp++)))
break;
if (!*cp)
j = sscanf(arg, "%ld", params[i].valptr.longptr);
}
if (j != 1) {
prnerr1("[%d] invalid long integer argument", curline);
errors++;
}
break;
case par_enum:
/* Find argument in enumeration list */
cpp = params[i].enumlist;
assert(cpp != NULL);
for (j = 1; *cpp != NULL; cpp++, j++)
if (!strcmp(*cpp, arg))
break;
/* Use position number in list as value */
if (*cpp != NULL)
*(params[i].valptr.enumptr) = j;
else {
prnerr2("[%d] invalid argument <%s>", curline, arg);
errors++;
}
break;
case par_proc:
if ((cp = (params[i].valptr.procptr)(params[i].name, arg)) != NULL) {
prnerr2("[%d] %s", curline, cp);
errors++;
}
break;
case par_null:
default:
break;
}
}
/*
* Find section name in section list
*/
static struct sectdef *findsect(sectlist, name)
struct sectdef *sectlist;
const char *name;
{
struct sectdef *cursect;
int len;
/* Scan through section list */
for (cursect = sectlist; cursect->name != NULL; cursect++) {
len = strlen(cursect->name);
if (cursect->name[len - 1] == '*') {
len--;
if (!strncmp(cursect->name, name, len)) {
if (!name[len]) {
prnerr1("[%d] missing subsection name",
curline);
errors++;
} else if (strchr(&(name[len]), ':') != NULL) {
prnerr1("[%d] too many subsection names",
curline);
errors++;
} else
return(cursect);
}
} else if (!strcmp(cursect->name, name))
return(cursect);
}
return(NULL);
}
/*
* Read configuration file
*/
void readconfig(sects, fname)
struct sectdef *sects;
char *fname;
{
struct sectdef *cursect = NULL;
char *sectname = NULL;
char *buf, *cp;
FILE *fd;
/* Nothing to do if we cannot open the setup file */
if (fname == NULL || (fd = fopen(fname, "r")) == NULL)
return;
curline = 0;
errors = 0;
/* Read all lines in input file */
while ((buf = readline(fd)) != NULL) {
if (*buf == '[') {
/* Terminate previous section */
if (cursect != NULL && cursect->endsect != NULL) {
cp = (cursect->endsect)(sectname, cursect);
if (cp != NULL) {
prnerr2("[%d] %s", curline, cp);
errors++;
}
}
/* Clear old section name */
if (sectname != NULL) {
free(sectname);
sectname = NULL;
}
/* Isolate section name and find section record */
if ((cp = strchr(buf, ']')) == NULL) {
prnerr1("[%d] unterminated section name", curline);
errors++;
cursect = NULL;
} else {
buf++;
if (*(cp + 1)) {
prnerr1("[%d] junk after section name",
curline);
errors++;
}
*cp = '\0';
copystr(§name, buf);
cursect = findsect(sects, sectname);
}
/* Startup new section */
if (cursect != NULL && cursect->startsect != NULL) {
cp = (cursect->startsect)(sectname, cursect);
if (cp != NULL) {
prnerr2("[%d] %s", curline, cp);
errors++;
}
}
} else if (sectname == NULL) {
prnerr1("[%d] definition outside of section", curline);
errors++;
} else if (cursect != NULL)
decode(cursect->params, buf);
}
if (cursect != NULL && cursect->endsect != NULL) {
cp = (cursect->endsect)(sectname, cursect);
if (cp != NULL) {
prnerr2("[%d] %s", curline, cp);
errors++;
}
}
/* Free all memory and report about errors */
if (errors > 0) {
prnerr2("found %d errors in file %s, terminating", errors, fname);
exit(EXIT_CONFIG);
}
if (inbuf != NULL) {
free(inbuf);
inbuf = NULL;
}
if (sectname != NULL)
free(sectname);
fclose(fd);
}
/*
* Read a section from a database
*/
void readdb(sect, fname)
struct sectdef *sect;
char *fname;
{
struct sectdef *cursect = NULL;
int foundsect = FALSE;
char *sectname = NULL;
char *buf, *cp;
FILE *fd;
/* Nothing to do if we cannot open the database file */
if (fname == NULL) {
prnerr0("no database file specified in configuration file");
exit(EXIT_NODB);
}
if ((fd = fopen(fname, "r")) == NULL) {
prnerr1("error opening database file %s", fname);
exit(EXIT_NODB);
}
curline = 0;
errors = 0;
/* Read all lines in input file */
while ((buf = readline(fd)) != NULL) {
if (*buf == '[') {
/* Terminate previous section */
if (cursect != NULL && cursect->endsect != NULL)
(cursect->endsect)(sectname, cursect);
/* Clear old section name */
if (sectname != NULL) {
free(sectname);
sectname = NULL;
}
/* Isolate section name and check if section found */
cursect = NULL;
if ((cp = strchr(buf, ']')) == NULL) {
prnerr1("[%d] unterminated section name", curline);
errors++;
} else {
buf++;
if (*(cp + 1)) {
prnerr1("[%d] junk after section name",
curline);
errors++;
}
*cp = '\0';
copystr(§name, buf);
if (!strcmp(sectname, sect->name)) {
if (foundsect) {
prnerr2("[%d] section <%s> multiply defined",
curline, sectname);
errors++;
} else {
cursect = sect;
foundsect = TRUE;
}
}
}
/* Startup new section */
if (cursect != NULL && cursect->startsect != NULL)
(cursect->startsect)(sectname, cursect);
} else if (sectname == NULL) {
prnerr1("[%d] definition outside of section", curline);
errors++;
} else if (cursect != NULL)
decode(cursect->params, buf);
}
if (cursect != NULL && cursect->endsect != NULL)
(cursect->endsect)(sectname, cursect);
/* Free all memory and report about errors */
if (errors > 0) {
prnerr2("found %d errors in file %s, terminating", errors, fname);
exit(EXIT_DB);
}
if (!foundsect) {
prnerr1("section <%s> not found in database", sect->name);
exit(EXIT_NOSYS);
}
if (inbuf != NULL) {
free(inbuf);
inbuf = NULL;
}
if (sectname != NULL)
free(sectname);
fclose(fd);
}
syntax highlighted by Code2HTML, v. 0.9.1