Files
retrobsd/tools/virtualmips/config.c
2014-04-09 14:27:18 +01:00

316 lines
8.5 KiB
C

/*
* Parsing INI-style configuration files. The routines are taken and
* modified from SMB source code (http://samba.anu.edu.au/cifs).
*
* Copyright (C) 2009-2012 Serge Vakulenko <serge@vak.ru>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "config.h"
static const char *confname;
static char *bufr;
static int bsize;
static char *cursec;
/*
* Scan to the end of a comment.
*/
static int eat_comment (FILE *fp)
{
int c;
c = getc (fp);
while (c > 0 && c != '\n')
c = getc (fp);
return c;
}
/*
* Skip whitespaces to end of line.
*/
static int eat_whitespace (FILE *fp)
{
int c;
c = getc (fp);
while (isspace(c) && c != '\n')
c = getc (fp);
return c;
}
/*
* Search for continuation backshash, starting from line end,
* When found, return it's index.
* When no continuation, return -1.
*/
static int find_continuation (char *line, int pos)
{
pos--;
while (pos >= 0 && isspace (line [pos]))
pos--;
if (pos >= 0 && line[pos] == '\\')
return pos;
/* No continuation. */
return -1;
}
/*
* Scan a parameter name (or name and value pair) and pass the value (or
* values) to function pfunc().
*/
static void parse_parameter (FILE *fp,
void (*pfunc) (void*, char*, char*, char*),
void *arg,
int c)
{
int i = 0; /* position withing bufr */
int end = 0; /* bufr[end] is current end-of-string */
int vstart = 0; /* starting position of the parameter */
/* Loop until we found the start of the value */
while (vstart == 0) {
/* Ensure there's space for next char */
if (i > (bsize-2)) {
bsize += 1024;
bufr = realloc (bufr, bsize);
if (! bufr) {
fprintf (stderr, "%s: malloc failed\n", confname);
exit (-1);
}
}
switch (c) {
case '=':
if (end == 0) {
fprintf (stderr, "%s: invalid parameter name\n", confname);
exit (-1);
}
bufr[end++] = '\0';
i = end;
vstart = end;
bufr[i] = '\0';
break;
case ';': /* comment line */
case '#':
c = eat_comment (fp);
case '\n':
i = find_continuation (bufr, i);
if (i < 0) {
/* End of line, but no assignment symbol. */
bufr[end]='\0';
fprintf (stderr, "%s: bad line, ignored: `%s'\n",
confname, bufr);
return;
}
end = ((i > 0) && (bufr[i-1] == ' ')) ? (i-1) : (i);
c = getc (fp);
break;
case '\0':
case EOF:
bufr[i] = '\0';
fprintf (stderr, "%s: unexpected end-of-file at %s: func\n",
confname, bufr);
exit (-1);
default:
if (isspace (c)) {
bufr[end] = ' ';
i = end + 1;
c = eat_whitespace (fp);
} else {
bufr[i++] = c;
end = i;
c = getc (fp);
}
break;
}
}
/* Now parse the value */
c = eat_whitespace (fp);
while (c > 0) {
if (i > (bsize-2)) {
bsize += 1024;
bufr = realloc (bufr, bsize);
if (! bufr) {
fprintf (stderr, "%s: malloc failed\n", confname);
exit (-1);
}
}
switch(c) {
case '\r':
c = getc (fp);
break;
case ';': /* comment line */
case '#':
c = eat_comment (fp);
case '\n':
i = find_continuation (bufr, i);
if (i < 0)
c = 0;
else {
for (end=i; (end >= 0) && isspace (bufr[end]); end--)
;
c = getc (fp);
}
break;
default:
bufr[i++] = c;
if (! isspace (c))
end = i;
c = getc (fp);
break;
}
}
bufr[end] = '\0';
pfunc (arg, cursec, bufr, &bufr [vstart]);
}
/*
* Scan a section name and remember it in `cursec'.
*/
static void parse_section (FILE *fp)
{
int c, i, end;
/* We've already got the '['. Scan past initial white space. */
c = eat_whitespace (fp);
i = 0;
end = 0;
while (c > 0) {
if (i > (bsize-2)) {
bsize += 1024;
bufr = realloc (bufr, bsize);
if (! bufr) {
fprintf (stderr, "%s: malloc failed\n", confname);
exit (-1);
}
}
switch (c) {
case ']': /* found the closing bracked */
bufr[end] = '\0';
if (end == 0) {
fprintf (stderr, "%s: empty section name\n", confname);
exit (-1);
}
/* Register a section. */
if (cursec)
free (cursec);
cursec = strdup (bufr);
eat_comment (fp);
return;
case '\n':
i = find_continuation (bufr, i);
if (i < 0) {
bufr [end] = 0;
fprintf (stderr, "%s: invalid line: '%s'\n",
confname, bufr);
exit (-1);
}
end = ((i > 0) && (bufr[i-1] == ' ')) ? (i-1) : (i);
c = getc (fp);
break;
default:
if (isspace (c)) {
bufr[end] = ' ';
i = end + 1;
c = eat_whitespace (fp);
} else {
bufr[i++] = c;
end = i;
c = getc (fp);
}
break;
}
}
}
/*
* Process the named parameter file
*/
void conf_parse (const char *filename,
void (*pfunc) (void*, char*, char*, char*),
void *arg)
{
FILE *fp;
int c;
confname = filename;
fp = fopen (filename, "r");
if (! fp) {
fprintf (stderr, "%s: unable to open config file\n", filename);
exit (-1);
}
bsize = 1024;
bufr = (char*) malloc (bsize);
if (! bufr) {
fprintf (stderr, "%s: malloc failed\n", confname);
fclose (fp);
exit (-1);
}
/* Parse file. */
c = eat_whitespace (fp);
while (c > 0) {
switch (c) {
case '\n': /* blank line */
c = eat_whitespace (fp);
break;
case ';': /* comment line */
case '#':
c = eat_comment (fp);
break;
case '[': /* section header */
parse_section (fp);
c = eat_whitespace (fp);
break;
case '\\': /* bogus backslash */
c = eat_whitespace (fp);
break;
default: /* parameter line */
parse_parameter (fp, pfunc, arg, c);
c = eat_whitespace (fp);
break;
}
}
fclose (fp);
if (cursec) {
free (cursec);
cursec = 0;
}
free (bufr);
bufr = 0;
bsize = 0;
}