/* * config.c - Read configuration files * * Copyright (C) 1997-2003 Gero Kuhlmann * * 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 #include #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); }