#include "stdio.h"
#include "string.h"
#include "config.h"

void * malloc();

static int
		lineno,			/* line number in hosts file */
		position,		/* position for scanning string */
		constate,		/* state for config file parser */
		inquote;		/* flag, inside quotes now */

static char	*Sspace;
static unsigned long cipn = 0L;		/* current ip number */
static int	nname = 32767;
static int	ngate = 32767;

#define BFSIZE 256
#define	NUMSPECS 128
/*
*   States for config file reading state machine.
*   One for each type of keyword and some for controlling.
*/

#define	CONNAME	101
#define CONHOST	102
#define CONIP	103
#define CONGATE	104
#define CONCOLOR 105
#define CONBKSP	106
#define CONBKSC	107
#define CONRETR	108
#define CONWIND	109
#define CONSEG	110
#define CONMTU	111
#define CONNS	112
#define CONTO	113
#define CONCRMAP 114
#define CONDUP  115
#define CONWRAP 116
#define CONWIDE 117
#define CONFONT 118
#define CONFSIZE 119
#define CONNF 120
#define CONNB 121
#define CONBF 122
#define CONBB 123
#define CONUF 124
#define CONUB 125
#define CONRF 126
#define CONRB 127
#define CONCLMODE 128
/*
*  above this line are per machine entries, below are configuration entries
*/
#define CONDOMTO 129
#define CONNDOM  130
#define	CONMASK	131
#define CONMYIP	132
#define CONHPF  133
#define CONPSF  134
#define CONTEKF 135
#define CONJTIME 136
#define CONME	137
#define CONCCOL	138
#define CONHW	139
#define CONADDR	140
#define CONIOA	141
#define CONDEF  142
#define CONCKEYS 143
#define CONINT	144
#define CONBIOS	145
#define CONTEK	146
#define CONVIDEO	147
#define CONFTP	148
#define CONRCP	149
#define CONPASS	150
#define CONCAP	151
#define CONDOMS	152

char *Skeyw[] =
{
		"",	
		"name",				/* name of session */
		"host",				/* name of host */
		"hostip",			/* IP number */
		"gateway",			/* gateway level */
		"color",			/* color code  ==5== */
		"erase",			/* value to use for backspace */
		"scrollback",		/* how many lines to backscroll */
		"retrans",			/* initial retrans time */
		"rwin",			/* window to allow for this host */
		"maxseg",		/* maximum transfer size (in) ==10== */
		"mtu",				/* transfer unit (out) */
		"nameserver",			/* name server level */
		"contime",		/* timeout for opening connection */
		"crmap",		/* map for Berkeley 4.3 compatibility */
		"duplex",		/* half duplex for IBM machines */
		"vtwrap",			/* should VT wrap? */
		"vtwidth",			/* width of VT100 screen */
		"font",			/* font to use, when given a choice */
		"fsize",			/* font size, in points */
		"nfcolor",			/* normal foreground color */
		"nbcolor",			/* normal background color */
		"bfcolor",			/* blink foreground color */
		"bbcolor",			/* blink background color */
		"ufcolor",			/* underline foreground color */
		"ubcolor",			/* underline background color */
		"rfcolor",			/* reverse foreground color */
		"rbcolor",			/* reverse background color */
		"clearsave",			/* clear screen saves lines */

/*
*  following are one-time entries, above are part of the data structure
*/
		"domaintime",			/* time-out for DOMAIN */
		"domainretry",			/* # of retries */
		"netmask",			/* subnetting mask */
		"myip",				/* local machine's IP # */
		"hpfile",			/* HPGL output file */
		"psfile",			/* postscript output file */
		"tekfile",			/* tektronix output file */
		"timeslice",		/* timer slice for multi-tasking */
		"myname",			/* identifying info ==15==*/
		"concolor",			/* console colors */
		"hardware",			/* network hardware */
		"address",			/* Address of hardware */
		"ioaddr",			/* ioaddress of hardware */
		"domain",			/* default domain for lookup */
		"commandkeys",			/* use command keys on mac */
		"interrupt",			/* interrupt request 3 or 5 */
		"bios",				/* use BIOS screen */
		"tek",				/* tektronix graphics ==20==*/
		"video",			/* type of video hardware */
		"ftp",				/* enable ftp? */
		"rcp",				/* enable rcp? */
		"passfile",			/* password file name */
		"capfile",			/* capture file name */
		"domainslist",			/* list of default domains */
		""
};

/************************************************************************/
/*  Sreadhosts
*   read in the hosts file into our in-memory data structure.
*   Handle everything by keyword, see docs for specifications about file.
*/
static int
Sreadhosts(cp, dp)
char	*cp;
struct conf	*dp;
{
	FILE *fp;
	int c, retval;
	char	buf[BFSIZE];

	Sspace = buf;
	position = constate = inquote = lineno = 0;   /* state vars */	

	if (NULL == (fp = fopen(cp, "r"))) {
		Serrline(900);
		return(1);
	}

	retval = 0;
	while (!retval) {
		c = fgetc(fp);
		if (c == '#' && !inquote) {
			while (c != EOF && c != '\n' && c != '\r')
				/* skip to EOL */
				c = fgetc(fp);
		}
		if (c == '\n' || c == '\r')
			lineno++;
		retval = Scontoken(c, dp);	/* add character to token */
	}

	fclose(fp);
	if (retval == EOF)		/* EOF is normal end */
		return(0);
	else
		return(retval);
}

/************************************************************************/
/*  Serrline
*   prints the line number of the host file error and posts the event
*   for the line number error and posts the hosts file error.
*/

Serrline(n)
int n;
{

	fprintf(stderr, "Config file: error %d in line %4d\n", n, lineno+1);
}

/************************************************************************/
/* Scontoken
*  tokenize the strings which get passed to Sconfile.
*  Handles quotes and uses separators:  <33, ;:=
*/
Scontoken(c, dp)
int c;
struct conf	*dp;
{
	int retval;

	if (c == EOF) {
		if (position) {
			Sspace[position] = '\0';
			Sconfile(Sspace, dp);
		}
		return(-1);
	}

	if (position > BFSIZE - 1) {
		Serrline(903);
		return(1);
	}

	if (c == '"' ) {
		if (!inquote)		/* beginning of quotes */
			inquote = 1;
		else
			inquote = 0;	/* end of quotes */
		return 0;
	}

	if (inquote || !Sissep(c)) {
		if (c == '\n') { /* check for EOL inside quotes */
			Serrline(904);
			return(1);
		}
		Sspace[position++] = c; /* include in current string */
		return 0;
	} else if (!position)
		return 0;

	Sspace[position] = '\0';

	retval = Sconfile(Sspace, dp);	/* pass the token along */

	position = 0;
	inquote = 0;
	Sspace[0] = '\0';

	return(retval);
}

extern void Ssetgate(unsigned long);
/************************************************************************/
/*  Sconfile
*   take the characters read from the file and parse them for keywords
*   which require configuration action.
*/
Sconfile(s, dp)
char *s;
struct conf	*dp;
{
	int i,a,b,c,d;

	switch (constate) {
		case 0:					/* lookup keyword */
			for (i = 1; *Skeyw[i]; i++) {
				if (!stricmp(Skeyw[i], s)) {
					constate = 100 + i;
					return 0;
				}
			}
			/* not in list */
			break;

		case CONNAME:
#ifdef DEBUG
printf("CONNAME: %s\n", s);
#endif
			constate = 0;
			break;

		case CONHOST:
#ifdef DEBUG
printf("CONHOST: %s\n", s);
#endif
			constate = 0;
			break;

		case CONIP:
#ifdef DEBUG
printf("CONIP: %s\n", s);
#endif
			constate = 0;
			if (4 == sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d)) {
				cipn = (unsigned long)(unsigned char)d << 24
				    | (unsigned long)(unsigned char)c << 16
				    | (unsigned long)
				    ((unsigned int)(unsigned char)b << 8 |
				    (unsigned int)(unsigned char)a);
			} else
				Serrline(906);
			break;

		case CONGATE:
			i = atoi(s);
			if (i && i < ngate) {
				ngate = i;
				(void) Ssetgate(cipn);
			}
			constate = 0;
			break;

		case CONCOLOR:
#ifdef DEBUG
printf("CONCOLOR: %s\n", s);
#endif
			constate = 0;
			break;

		case CONNF:
#ifdef DEBUG
printf("CONNF: %s\n", s);
#endif
			constate = 0;
			break;
		case CONNB:
#ifdef DEBUG
printf("CONNB: %s\n", s);
#endif
			constate = 0;
			break;
		case CONRF:
		case CONBF:
#ifdef DEBUG
printf("CONRF/CONBF: %s\n", s);
#endif
			constate = 0;
			break;
		case CONRB:
		case CONBB:
#ifdef DEBUG
printf("CONRB/CONBB: %s\n", s);
#endif
			constate = 0;
			break;
		case CONUF:
#ifdef DEBUG
printf("CONUF: %s\n", s);
#endif
			constate = 0;
			break;
		case CONUB:
#ifdef DEBUG
printf("CONUB: %s\n", s);
#endif
			constate = 0;
			break;

		case CONBKSP:
#ifdef DEBUG
printf("CONBKSP: %s\n", s);
#endif
			constate = 0;
			break;

		case CONBKSC:
#ifdef DEBUG
printf("CONBKSC: %s\n", s);
#endif
			constate = 0;
			break;

		case CONRETR:
#ifdef DEBUG
printf("CONRETR: %s\n", s);
#endif
			constate = 0;
			break;

		case CONWIND:
#ifdef DEBUG
printf("CONWIND: %s\n", s);
#endif
			constate = 0;
			break;

		case CONSEG:
#ifdef DEBUG
printf("CONSEG: %s\n", s);
#endif
			constate = 0;
			break;

		case CONMTU:
#ifdef DEBUG
printf("CONMTU: %s\n", s);
#endif
			constate = 0;
			break;

		case CONNS:
#ifdef DEBUG
printf("CONNS: %s\n", s);
#endif
			i = atoi(s);
			if (i && i < nname) {
				nname = i;
				(void) Ssetname(cipn);
			}
			constate = 0;
			break;

		case CONTO:
#ifdef DEBUG
printf("CONTO: %s\n", s);
#endif
			constate = 0;
			break;

		case CONCRMAP:
#ifdef DEBUG
printf("CONCRMAP: %s\n", s);
#endif
			constate = 0;
			break;

		case CONDUP:
#ifdef DEBUG
printf("CONDUP: %s\n", s);
#endif
			constate = 0;
			break;

		case CONWRAP:
#ifdef DEBUG
printf("CONWRAP: %s\n", s);
#endif
			constate = 0;
			break;

		case CONCLMODE:
#ifdef DEBUG
printf("CONCLMODE: %s\n", s);
#endif
			constate = 0;
			break;

		case CONFONT:
#ifdef DEBUG
printf("CONFONT: %s\n", s);
#endif
			constate = 0;
			break;

		case CONFSIZE:
#ifdef DEBUG
printf("CONFSIZE: %s\n", s);
#endif
			constate = 0;
			break;

		case CONWIDE:
#ifdef DEBUG
printf("CONWIDE: %s\n", s);
#endif
			constate = 0;
			break;

/*
*  now the one-time entries
*  Generally this information goes into the "Scon" structure for later
*  retrieval by other routines.
*
*/
		case CONMASK:
			constate = 0;
			if (4 == sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d)) {
				dp->snetmask = (unsigned long)
				    (unsigned char)d << 24
				    | (unsigned long)(unsigned char)c << 16
				    | (unsigned long)
				    ((unsigned int)(unsigned char)b << 8 |
				    (unsigned int)(unsigned char)a);
			} else
				Serrline(907);
			break;

		case CONMYIP:
			constate = 0;
			if (!stricmp(s, "bootp")) {
				dp->flags |= H_BOOTP;
			} else if (!stricmp(s, "rarp")) {
				dp->flags |= H_RARP;
			} else if (4 == sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d)) {
				dp->myip = (unsigned long)(unsigned char)d << 24
				    | (unsigned long)(unsigned char)c << 16
				    | (unsigned long)
				    ((unsigned int)(unsigned char)b << 8 |
				    (unsigned int)(unsigned char)a);
			} else
				Serrline(908);
			break;
		case CONME:				/* what my name is  */
#ifdef DEBUG
printf("CONME: %s\n", s);
#endif
#ifdef notdef
			strncpy(Scon.me,s,30);
			Scon.me[30] = '\0';
#endif
			constate = 0;
			break;

		case CONHW:
#ifdef DEBUG
printf("CONHW: %s\n", s);
#endif
#ifdef notdef
			i = strlen(s);
			if (i > 9) i = 9;
			s[i] = '\0';
			i--;
			while (i--)
				s[i] = tolower(s[i]);
			strcpy(Scon.hw,s);
#endif
			constate = 0;
			break;

		case CONINT:			/* what IRQ to use */
#ifdef DEBUG
printf("CONINT: %s\n", s);
#endif
#ifdef notdef
			sscanf(s,"%x",&i);
			Scon.irqnum = i;
#endif
			constate = 0;
			break;

		case CONBIOS:
#ifdef DEBUG
printf("CONBIOS: %s\n", s);
#endif
			constate = 0;
			break;

		case CONADDR:
#ifdef DEBUG
printf("CONADDR: %s\n", s);
#endif
			sscanf(s,"%x",&i);
			dp->address = i;
			constate = 0;
			break;

		case CONIOA:
#ifdef DEBUG
printf("CONIOA: %s\n", s);
#endif
			sscanf(s,"%x",&i);
			dp->ioaddr = i;
			constate = 0;
			break;

		case CONTEK:
#ifdef DEBUG
printf("CONTEK: %s\n", s);
#endif
			constate = 0;
			break;

		case CONVIDEO:
#ifdef DEBUG
printf("CONVIDEO: %s\n", s);
#endif
			constate = 0;
			break;

		case CONCCOL:
#ifdef DEBUG
printf("CONCCOL: %s\n", s);
#endif
			constate = 0;
			break;

		case CONFTP:
#ifdef DEBUG
printf("CONFTP: %s\n", s);
#endif
			constate = 0;
			break;

		case CONRCP:
#ifdef DEBUG
printf("CONRCP: %s\n", s);
#endif
			constate = 0;
			break;

		case CONPASS:
#ifdef DEBUG
printf("CONPASS: %s\n", s);
#endif
			constate = 0;
			break;

		case CONDEF:
#ifdef DEBUG
printf("CONDEF: len = %d, %s\n", position, s);
#endif
			if (s[position - 1] == '.')
				s[--position] = 0;
			dp->domain = malloc(position + 1);
			(void) memcpy(dp->domain, s, position + 1);
			constate = 0;
			break;

		case CONDOMS:
			/* If we already have a domain, leave it. */
			if (dp->domain) {
				constate = 0;
				break;
			}
			if (s[position - 1] == '.')
				s[--position] = 0;
			for (i = 0; i < position; i++)
				if (s[i] == ',') {
					s[i] = 0;
					position = i;
					break;
				}
			dp->domain = malloc(position + 1);
			(void) memcpy(dp->domain, s, position + 1);
			constate = 0;
			break;

		case CONCAP:
#ifdef DEBUG
printf("CONCAP: %s\n", s);
#endif
			constate = 0;
			break;

		case CONDOMTO:			/* DOMAIN timeout value */
#ifdef DEBUG
printf("CONDOMTO: %s\n", s);
#endif
#ifdef notdef
			i = atoi(s);
			if (i > 1)
				Scon.domto = i;
#endif
			constate = 0;
			break;

		case CONNDOM:			/* DOMAIN number of retries */
#ifdef DEBUG
printf("CONNDOM: %s\n", s);
#endif
#ifdef notdef
			i = atoi(s);
			if (i > 1)
				Scon.ndom = i;
#endif
			constate = 0;
			break;

		case CONHPF:			/* File name for HP dump */
#ifdef DEBUG
printf("CONHPF: %s\n", s);
#endif
			constate = 0;
			break;

		case CONPSF:			/* File name for PS dump */
#ifdef DEBUG
printf("CONPSF: %s\n", s);
#endif
			constate = 0;
			break;

		case CONTEKF:			/* File name for Tek dump */
#ifdef DEBUG
printf("CONTEKF: %s\n", s);
#endif
			constate = 0;
			break;

		default:
			constate = 0;
			break;
	}
	return 0;
}

/**************************************************************************/
/*  Sissep
*   is the character a valid separator for the hosts file?
*   separators are white space, special chars and :;=
*
*/
Sissep(c)
int c;
{

	if (c < 33 || c == ':' || c == ';' || c == '=')
		return(1);
	return(0);
}

int
configure(cp, dp)
char	*cp;
struct conf	*dp;
{
	char	*getenv();

	if (!cp)
		cp = getenv("CONFIG.TEL");
	if (!cp)
		cp = getenv("CONFIGTEL");

	if (!cp) {
		fputs("Error: CONFIGTEL not defined\n", stderr);
		return -1;
	}
	if (Sreadhosts(cp, dp) < 0) {
		fprintf(stderr, "Error parsing CONFIGTEL file %s\n", cp);
		return -1;
	}
	return 0;
}
