#include <malloc.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>

#include "dpconf.h"

int linenum;

char TOK_SEP[] = " \t";
#define issep(c) strchr(TOK_SEP, (c))

char *
skip_sep(s)
char *s;
{
    while (*s && issep(*s))
	s++;
    return s;
}

char *
next_tok(path, s)
char *path,
     *s;
{
    int inst = 0;
    char *s0 = s,
	 *s1 = s,
	 ch;
    s = skip_sep(s);
    if (*s == '#') {
	*s1++ = *s++;
	*s1 = '\0';
	return ++s;
    }
    for ( s1 = s ; ; s++)
	if ((!inst && strchr(TOK_SEP, *s)) || !*s) {
	    if (inst)
		yyerror(path, "Unterminated string: %s\n", s0);
	    *s1 = '\0';
	    return ++s;
	}
	else
	    switch (*s) {
	     case '\\':
		s++;
		if (isdigit(*s)) {
		    int i = 3;
		    ch = 0;
		    for ( ; *s && isdigit(*s) && i-- > 0 ; s++ )
			ch = (ch << 3) | (*s - '0');
		}
		else {
		    switch (*s) {
		     case 'b': ch = '\b'; break;
		     case 'f': ch = '\f'; break;
		     case 'n': ch = '\n'; break;
		     case 'r': ch = '\r'; break;
		     case 't': ch = '\t'; break;
		     default:  ch = *s;   break;
		    }
		}
		*s1++ = ch;
		break;
	     case '"':
		inst = !inst;
		break;
	     default:
		*s1++ = *s;
		break;
	    }
}

sprascii(b, s)
char *b,
     *s;
{
    char c;
    while (c = *s++)
	if (!isascii(c)) {
	    (void)sprintf(b, "\\%03o", c & 0xff);
	    b += 4;
	}
	else if (!isprint(c)) {
	    (void)sprintf(b, "^%c", c ^ 0x40);
	    b += 2;
	}
	else
	    *b++ = c;
    *b = '\0';
}

static fln;

char *
fgetln(buf, bufsz, f)
char *buf;
int bufsz;
FILE *f;
{
    char *e, *b;
    b = fgets(buf, bufsz, f);
    if (b) {
	e = buf + strlen(buf) - 1;
	if (*e == '\n') {
	    fln++;
	    *e = '\0';
	}
    }
    else {
	fln = 0;
	buf[0] = '\0';
	if (ferror(f))
	    perror("read");
    }
    return b;
}

parse_ln(path, vv, t)
char *path;
var_val *vv;
char *t;
{
    int nv = 0;
    char *nt;
    for ( ; *t ; t = nt) {
	t = skip_sep(t);
	if (!*t || *t == '#')
	    break;
	nt = next_tok(path, t);
	vv->var = malloc((unsigned)strlen(t)+1);
	(void)strcpy(vv->var, t);
	if (vv->val = strchr(vv->var, '=')) {
	    *(vv->val)++ = '\0';
	    nv++;
	    vv++;
	}
	else {
	    char buf[256];
	    sprascii(buf, t);
	    yyerror(path, "Bad configuration option \"%s\"\n", buf);
	}
    }
    return nv;
}

#define	MAXCONFLN	1024
#define	MAXVARS		32

var_val *
readconf(path, cf)
char *path;
FILE *cf;
{
    static char lnbuf[MAXCONFLN];
    static var_val vv[MAXVARS+1];
    var_val *vp;
    int n;
    int nv = 0;
    char *l;
    int line = 0;

    /*
     * De-allocate variable/value pairs from last time.
     */
    for (vp = vv ; vp->var ; vp++) {
	(void)free(vp->var);
	vp->var = vp->val = (char *)0;
	vp->used = 0;
    }
    vp = vv;
    linenum = 0;

    /*
     * Skip over any blank or comment lines..
     */
    l = skip_sep(lnbuf);
    while (*l == '#' || *l == '\0') {
	if (!fgetln(lnbuf, sizeof(lnbuf), cf))
	     return (var_val *)0;
	l = skip_sep(lnbuf);
    }

    linenum = fln;
    /*
     * Definitions must start in column 0.
     */
    if (l != lnbuf) {
	yyerror(path, "Config entries must start in the first column: %s",
		lnbuf);
    }
	
    /*
     * We should be here with l pointing to a non-comment, non-blank line,
     * starting in column 0.  Process lines until we either run into
     * a blank line, EOF, or another line starting in column 0.
     */
    do {
	if (*l != '#') {
	    /*
	     * Parse anything but a comment line.
	     *
	     * If we have read a line (line > 0), and something is in the
	     * first column, it is a new definition.
	     */
	    if (line && l == lnbuf)
		break;
	    n = parse_ln(path, vp, l);
	    nv += n;
	    vp += n;
	    line++;
	}
	if (!fgetln(lnbuf, sizeof(lnbuf), cf))
	    break;
	l = skip_sep(lnbuf);
    } while (*l);

    return nv ? vv : (var_val *)0;
}
