#include "pcdefs.h"
#include "config.h"
#include "funcdef.h"
#include "../defs.h"

static int _fastcall Sissep(int);
static int _fastcall Sconfile(char *);
static int _fastcall Scontoken(int);
static void _fastcall Serrline(int);

extern char _near shared_buf[];

/* my own stdio routines */
#define BUFSIZE	512		/* This takes stack space */
#define EOF -1
struct file {
	int fd;
	unsigned char *fcp, *ecp;
	char buf[BUFSIZE];
	int lineno;
};

struct file *current;

struct conf Scon;
unsigned long nameserver;
extern int neterrno;

#define getc(fp)	((fp)->fcp < (fp)->ecp ? (int)*(fp)->fcp++ : next_char(fp))

static int
next_char(struct file *fp)
{
    int i;

    if (fp->fcp < fp->ecp)
	return *fp->fcp++;
    fp->fcp = fp->buf;
    i = read(fp->fd, fp->buf, BUFSIZE);
    if (i > 0) {
	fp->ecp = fp->buf + i;
	return *fp->fcp++;
    }
    return EOF;
}

static int
		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		/* must be < BUFSIZE */
#define NUMSPECS 130            /* last CON option for a machine specific option */
/*
*   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
#define CONPORT 	129
#define CONFTPBAK	130
/*
*  above this line are per machine entries, below are configuration entries
*/
#define CONNDOM     (NUMSPECS+1)
#define CONMASK     (NUMSPECS+2)
#define CONMYIP     (NUMSPECS+3)
#define CONHPF      (NUMSPECS+4)
#define CONPSF      (NUMSPECS+5)
#define CONTEKF     (NUMSPECS+6)
#define CONJTIME    (NUMSPECS+7)
#define CONME       (NUMSPECS+8)
#define CONCCOL     (NUMSPECS+9)
#define CONHW       (NUMSPECS+10)
#define CONADDR     (NUMSPECS+11)
#define CONIOA      (NUMSPECS+12)
#define CONDEF      (NUMSPECS+13)
#define CONCKEYS    (NUMSPECS+14)
#define CONINT      (NUMSPECS+15)
#define CONBIOS     (NUMSPECS+16)
#define CONTEK      (NUMSPECS+17)
#define CONVIDEO    (NUMSPECS+18)
#define CONFTP      (NUMSPECS+19)
#define CONRCP      (NUMSPECS+20)
#define CONPASS     (NUMSPECS+21)
#define CONCAP      (NUMSPECS+22)
#define CONTTYPE    (NUMSPECS+23)
#define CONNSTYPE   (NUMSPECS+24)
#define CONFROM     (NUMSPECS+25)
#define CONARPTO    (NUMSPECS+26)
#define CONZONE     (NUMSPECS+27)
#define CONDOMTO    (NUMSPECS+28)
#define CONKBFILE   (NUMSPECS+29)
#define CONWIRE     (NUMSPECS+30)
#define CONCURSORTOP    (NUMSPECS+31)
#define CONCURSORBOTTOM (NUMSPECS+32)
#define CONWINDOWGOAWAY (NUMSPECS+33)
#define CONAUTOSCROLL   (NUMSPECS+34)
#define CONCLOCK        (NUMSPECS+35)
#define CONBROADCAST    (NUMSPECS+36)
#define CONOUTPUTMAP    (NUMSPECS+37)
#define CONBEEP         (NUMSPECS+38)
#define CONSERVICES		(NUMSPECS+39)

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 ==15==*/
	"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 ==20==*/
	"nbcolor",			/* normal background color */
	"bfcolor",			/* blink foreground color */
	"bbcolor",			/* blink background color */
	"ufcolor",			/* underline foreground color */
	"ubcolor",			/* underline background color ==25==*/
	"rfcolor",			/* reverse foreground color */
	"rbcolor",			/* reverse background color */
	"clearsave",		/* clear screen saves lines */
	"port",				/* TCP port to go for */
	"ftpoptions",       /* Options for ftp command line ==30==*/
/*
*  following are one-time entries, above are part of the data structure
*/
        "domainretry",      /* # of retries */
	"netmask",			/* subnetting mask */
	"myip",				/* local machine's IP # */
	"hpfile",			/* HPGL output file */
        "psfile",           /* postscript output file ==35==*/
        "tekfile",          /* tektronix output file */
	"timeslice",		/* timer slice for multi-tasking */
	"myname",			/* identifying info */
	"concolor",			/* console colors */
        "hardware",         /* network hardware ==40==*/
        "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 ==45==*/
        "bios",             /* use BIOS screen */
		"tek",				/* tektronix graphics */
		"video",			/* type of video hardware */
		"ftp",				/* enable ftp? */
        "rcp",              /* enable rcp? ==50==*/
        "passfile",         /* password file name */
		"capfile",			/* capture file name */
		"termtype",			/* terminal type */
		"nameservertype",	/* nameserver type */
        "copyfrom",         /* copy from another machine ==55==*/
        "arptime",          /* time-out for ARPs */
		"zone",				/* NBP zone for Macs */
		"domaintime",		/* time-out for DOMAIN */
		"keyfile",			/* file for keyboard map */
        "wire",             /* Thin or thick ether? Needed for 3c503 ==60==*/
        "cursortop",        /* Starting line for the cursor */
		"cursorbottom",		/* Ending line for the cursor */
		"windowgoaway",		/* Whether to wait for a keypress after a window closes, or not */
		"autoscroll",		/* Do we automatically scroll in scrollback */
        "clock",            /* Display the clock? ==65==*/
		"broadcast",        /* IP broadcast address */
		"outputfile",       /* File for the output mapping definition */
        "beep",             /* musical note display */
		"services",			/* specify path to services file */
	0,
    }
;

/* Stuff for WATTCP */
#define WAT_INIT	1000
#define WAT_PRINT	1001
#define WAT_MYIP	1002
#define WAT_NETMASK	1003
#define WAT_NAMESERVER	1004
#define WAT_DOMAINS	1005
#define WAT_GATEWAY	1006
#define WAT_INCLUDE	1007
#define WAT_BOOTP	1008
#define WAT_BOOTPTO	1009
#define WAT_ETHIP	1010
#define WAT_HOSTNAME	1011
#define WAT_SOCKDELAY	1012
#define WAT_MSS		1013
#define WAT_COOKIE	1014

char *WATkeyw[] = {
	"",
	"print",	/* Print this string */
	"my_ip",
	"netmask",
	"nameserver",
	"domainslist",
	"gateway",
	"include",
	"bootp",
	"bootpto",
	"ethip",
	"hostname",
	"sockdelay",
	"mss",
	"cookie",
	0
};

/************************************************************************/
/*  Sconfigure
*   read in the hosts file into our in-memory data structure.
*   Handle everything by keyword, see docs for specifications about file.
*/
int
Sconfigure(cp, wattcp)
char	_far *cp;
char wattcp;
{
	int c, retval;
	struct file file;

	Sspace = shared_buf;
	position = constate = inquote = 0;   /* state vars */	
	if (wattcp) {
	    constate = WAT_INIT;
	}

	file.fcp = 0;
	file.ecp = 0;
	file.lineno = 0;
	file.fd = open(cp, 0x40);
	if (file.fd < 0) {
	    if (wattcp != 1) {
		dfputs("Init: can't open ");
		memcpy(shared_buf, cp, 128);
		dfputs(shared_buf);
		dfputs("\r\n");
	    }
	    neterrno = NETERR_NOTLOADED;
	    return -1;
	}

	retval = 0;
	while (!retval) {
		c = getc(&file);
		if (c == '#' && !inquote) {
			while (c != EOF && c != '\n' && c != '\r')
				/* skip to EOL */
				c = getc(&file);
		}
		if (c == '\r')
			continue;
		if (c == '\n')
			file.lineno++;
		current = &file;
		retval = Scontoken(c);	/* add character to token */
	}

	close(file.fd);
	if (retval == EOF)		/* EOF is normal end */
		return(0);
	else {
		neterrno = NETERR_BADARGS;
		return -1;
	}
}

/************************************************************************/
/*  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.
*/

static void _fastcall
Serrline(n)
int n;
{

	dfputs("Config file: error ");
	printd(n);
	dfputs(" in line ");
	printd(current->lineno+1);
	dfputs("\r\n");
}

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

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

	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++] = (char)c; /* include in current string */
		return 0;
	}

	while (position &&
	    (Sspace[position - 1] == ' ' || Sspace[position - 1] == '\t'))
	    position--;
	Sspace[position] = '\0';
	if (!position)
	    return 0;

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

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

	return(retval);
}

/************************************************************************/
/*  Sconfile
*   take the characters read from the file and parse them for keywords
*   which require configuration action.
*/
static int _fastcall
Sconfile(s)
char *s;
{
	int i;

	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;
			cipn = inet_addr(s);
			if (cipn == -1)
				Serrline(906);
			break;

		case CONGATE:
			i = atoi(s);
			if (i && i < ngate) {
				ngate = i;
				(void) setgate(0, 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;
				nameserver = 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:
		case WAT_NETMASK:
			constate = constate >= WAT_INIT ? WAT_INIT : 0;
			Scon.snetmask = inet_addr(s);
			if (Scon.snetmask == -1)
				Serrline(907);
			break;

		case CONMYIP:
		case WAT_MYIP:
			constate = constate >= WAT_INIT ? WAT_INIT : 0;
			if (!stricmp(s, "bootp")) {
				Scon.flags |= H_BOOTP;
			} else if (!stricmp(s, "rarp")) {
				Scon.flags |= H_RARP;
			} else if ((Scon.myip = inet_addr(s)) == -1)
				Serrline(908);
			else
				Scon.flags |= H_MYIP;
			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
			/*
			Scon.irqnum = xtoi(s);
			*/
			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
			Scon.address = (u_char)xtoi(s);
			constate = 0;
			break;

		case CONIOA:
#ifdef DEBUG
printf("CONIOA: %s\n", s);
#endif
			Scon.ioaddr = (u_char)xtoi(s);
			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
#ifdef notdef
			if (s[position - 1] == '.')
				s[--position] = 0;
			Scon.domain = (unsigned char *)malloc(position + 1);
			memcpy(Scon.domain, s, position + 1);
#endif
			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;

		case CONARPTO:
			i = atoi(s);
			if (i)
				Scon.arptime = i;
			constate = 0;
			break;

		case CONBROADCAST:
			Scon.broadcast = inet_addr(s);
			constate = 0;
			break;

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

		case WAT_PRINT:
			dfputs(s);
			dfputs("\r\n");
			constate = WAT_INIT;
			break;

		case WAT_NAMESERVER:
			if ((cipn = inet_addr(s)) == -1)
				Serrline(908);
			/* TODO: store nameserver somewhere */
			constate = WAT_INIT;
			break;
		case WAT_DOMAINS:
			constate = WAT_INIT;
			break;
		case WAT_GATEWAY:
			if ((cipn = inet_addr(s)) == -1)
				Serrline(908);
			else
				(void) setgate(0, cipn);
			constate = WAT_INIT;
			break;
		case WAT_INCLUDE:
			if (*s == '?') {
			    s++;
			    (void)Sconfigure(s, 2);
			} else if (Sconfigure(s, 2)) {
			    return 1;
			}
			constate = WAT_INIT;
			break;
		case WAT_BOOTP:
		case WAT_BOOTPTO:
		case WAT_ETHIP:
		case WAT_SOCKDELAY:
		case WAT_MSS:
		case WAT_COOKIE:

		default:
			if (constate >= WAT_INIT)
			    constate = WAT_INIT;
			else
			    constate = 0;
			break;
	}
	return 0;
}

/**************************************************************************/
/*  Sissep
*   is the character a valid separator for the hosts file?
*   separators are white space, special chars and :;=
*
*/
static int _fastcall
Sissep(c)
int c;
{
	static char found_eq = 0;
	if (constate >= WAT_INIT) {
	    if (!found_eq && c == '=') {
		found_eq = 1;
		return 1;
	    }
	    if (c == '\n') {
		found_eq = 0;
		return 1;
	    }
	    return 0;
	}
	if (c < 33 || c == ':' || c == ';' || c == '=')
		return(1);
	return(0);
}
