/*
 * Copyright 1993,1994 Globetrotter Software, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Globetrotter Software not be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Globetrotter Software makes
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 *
 * GLOBETROTTER SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
 * EVENT SHALL GLOBETROTTER SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 *
 * Author:  Jim McBeath, Globetrotter Software, jimmc@globes.com
 */
/* conf.c - generic routines for reading configuration files
 *
 * Jim McBeath, November 18, 1993
 */

#include "htimp.h"
#include <stdio.h>
#include <ctype.h>

OpInfo *
ImpFindOp(optab,name)
OpInfo *optab;
char *name;
{
	int i;

	for (i=0; optab[i].name; i++) {
		if (strcmp(name,optab[i].name)==0)
			return optab+i;
	}
	return (OpInfo *)0;
}

int	/* status: 0 if OK, 1 if there was no string or bad string there */
ImpReadConfStrStr(sp,rp)
char **sp;	/* ptr to pointer to start of string to read - we update this */
char **rp;	/* pointer to string gets returned here */
{
	char *s = *sp;
	char *p, *r;
	int quoted;
	int n;
	
	if (!*s) return 1;
	if (*s=='"') {
		/* quoted string, look for end quote, plus backquotes */
		p = r = s;
		s++;
		while (*s) {
			if (*s=='"') {
				s++;
				break;
			}
			if (*s=='\\') switch(*++s) {
			case '\n': s++; break;
			case '\\': *p++ = '\\'; s++; break;
			case 'n': *p++ = '\n'; s++; break;
			case 't': *p++ = '\t'; s++; break;
			case '0': case '1': case '2': case '3': case '4':
			case '5': case '6': case '7': case '8': case '9':
				n = *s++ - '0';
				if (isdigit(*s)) {
					n = n*8 + *s++ - '0';
					if (isdigit(*s))
						n = n*8 + *s++ - '0';
				}
				*p++ = n; break;
			default: *p++ = *s++; break;
			} else {
				*p++ = *s++;
			}
		}
		*p = 0;
	}
	else {
		r = s;
		while (*s && !isspace(*s)) s++;
	}
	if (*s)
		*s++ = 0;
	*sp = s;
	*rp = r;
	return 0;
}

int	/* status: 0 if OK, 1 if there was no int there */
ImpReadConfStrInt(sp,np)
char **sp;	/* ptr to pointer to start of number - we update this */
int *np;	/* int value gets returned here */
{
	char *s;
	int n;
	int negflag;

	s = *sp;
	if (*s=='-') {
		negflag = 1;
		s++;
	} else {
		negflag = 0;
	}
	if (!isdigit(*s))
		return 1;
	n = 0;
	while (isdigit(*s)) {
		n = n*10 + ((*s++)-'0');
	}
	if (negflag)
		n = -n;
	*sp = s;
	*np = n;
	return 0;	/* OK */
}

void
ImpFatalStringRequired(cmd,filename)
char *cmd;
char *filename;
{
	ImpFatal("Command \"%s\" in file %s requires string arg\n",
			cmd,filename);
	/* NOTREACHED */
}

void
ImpFatalIntRequired(cmd,filename)
char *cmd;
char *filename;
{
	ImpFatal("Command \"%s\" in file %s requires int arg\n",
			cmd,filename);
	/* NOTREACHED */
}

void
ImpFatalNonNegIntRequired(cmd,filename)
char *cmd;
char *filename;
{
	ImpFatal("Command \"%s\" in file %s requires non-negative int arg\n",
			cmd,filename);
	/* NOTREACHED */
}

void
ImpConfLine(cmd,filename,optab,data)
char *cmd;
char *filename;
OpInfo *optab;
void *data;
{
	char *p, *e;
	OpInfo *oi;
	int n;

	if (!cmd) return;
	while (isspace(*cmd)) cmd++;
	if (!*cmd) return;
	if (*cmd=='#') return;
	for (e=cmd; *e&&!isspace(*e); e++) {
		/* make command lower case, scan to next space */
		if (isupper(*e))
			*e = tolower(*e);
	}
	if (*e) {
		*e++ = 0;
	}
	oi = ImpFindOp(optab,cmd);
	if (!oi) {
		ImpFatal("Unknown command \"%s\" in file %s\n",cmd,filename);
		/* NOTREACHED */
	}
	while (isspace(*e)) e++;	/* skip whitespace */
	switch (oi->callstyle) {
	case CALL_S:	/* one string */
		if (ImpReadConfStrStr(&e,&p))
			ImpFatalStringRequired(cmd,filename);
			/* NOTREACHED */
		(*(oi->func))(p);
		break;
	case CALL_DS:	/* data pointer and string */
		if (ImpReadConfStrStr(&e,&p))
			ImpFatalStringRequired(cmd,filename);
			/* NOTREACHED */
		(*(oi->func))(data,p);
		break;
	case CALL_I:	/* integer */
		if (ImpReadConfStrInt(&e,&n))
			ImpFatalIntRequired(cmd,filename);
			/* NOTREACHED */
		(*(oi->func))(n);
		break;
	case CALL_N:	/* natural number, i.e. non-negative integer */
		if (ImpReadConfStrInt(&e,&n))
			ImpFatalIntRequired(cmd,filename);
			/* NOTREACHED */
		if (n<0)
			ImpFatalNonNegIntRequired(cmd,filename);
			/* NOTREACHED */
		(*(oi->func))(n);
		break;
	case CALL_DI:	/* data and integer */
		if (ImpReadConfStrInt(&e,&n))
			ImpFatalIntRequired(cmd,filename);
			/* NOTREACHED */
		(*(oi->func))(data,n);
		break;
	case CALL_DN:	/* data and non-neg int */
		if (ImpReadConfStrInt(&e,&n))
			ImpFatalIntRequired(cmd,filename);
			/* NOTREACHED */
		if (n<0)
			ImpFatalNonNegIntRequired(cmd,filename);
			/* NOTREACHED */
		(*(oi->func))(data,n);
		break;
	default:
		ImpFatal(
	    "Internal error: bad callstyle for rc command \"%s\" in file %s\n",
				cmd,filename);
		/* NOTREACHED */
	}
}

ImpConfFile(filename,optab,data)
char *filename;
OpInfo *optab;
void *data;		/* data to pass to subfuncs */
{
	FILE *f;
	static char *line=0;
	int linealloc=0;
#define LINECHUNKSIZE 500
	char *linep;
	int l;
	int needmore;

	f = fopen(filename,"r");
	if (!f) {
		ImpFatal("+Can't open conf file %s\n",filename);
		/* NOTREACHED */
	}
	if (!line) {
		linealloc = 2*LINECHUNKSIZE;
		line = ImpMalloc(linealloc);
	}
	while (!feof(f)) {
		needmore = 1;
		l = 0;
		while (!feof(f) && needmore && fgets(line+l,LINECHUNKSIZE,f)) {
			l = strlen(line);
			if (l+LINECHUNKSIZE>=linealloc) {
				linealloc += LINECHUNKSIZE;
				line = ImpRealloc(line,linealloc);
			}
			if (l>0 && line[l-1]=='\n')
				line[--l] = 0;	/* throw out trailing newline */
			if (l>0 && line[l-1]=='\\') { /* look for line cont. */
				line[--l] = 0;
				needmore = 1;
			} else
				needmore = 0;
		}
		if (l>0 && line[l-1]=='\\')	/* \ at end of file */
			line[--l] = 0;
		if (l>0)
			ImpConfLine(line,filename,optab,data);
	}
	fclose(f);
}

/* end */
