/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:conn.c 12.0$ */
/* $ACIS:conn.c 12.0$ */
/* $Source: /ibm/acis/usr/src/usr.bin/uucp/RCS/conn.c,v $ */

#ifndef lint
static char *rcsid = "$Header:conn.c 12.0$";
#endif

#ifndef lint
static char sccsid[] = "@(#)conn.c	5.10 (Berkeley) 1/24/86";
#endif

#include <signal.h>
#include "uucp.h"
#include <setjmp.h>
#include <ctype.h>
#include <errno.h>
#ifdef	USG
#include <termio.h>
#include <fcntl.h>
#endif
#ifndef	USG
#include <sgtty.h>
#endif
#ifdef BSD4_2
#include <sys/time.h>
#else
#include <time.h>
#endif

#define MAXC 1000

extern jmp_buf Sjbuf;
jmp_buf Cjbuf;
extern int errno, onesys;
extern char *sys_errlist[];
extern char MaxGrade, DefMaxGrade;

/* Parity control during login procedure */
#define	P_ZERO	0
#define	P_ONE	1
#define	P_EVEN	2
#define	P_ODD	3

#define ABORT -2

char 	*AbortOn = NULL;
char	par_tab[128];	/* must be power of two */
int	linebaudrate;	/* used for the sleep test in pk1.c */
int next_fd = -1;	/* predicted fd to close interrupted opens */

char *PCP = "PCP";	/* PC Pursuit device type */
/*
 *	catch alarm routine for "expect".
 */
alarmtr()
{
	signal(SIGALRM, alarmtr);
	if (next_fd >= 0) {
		if (close(next_fd))
			logent("FAIL", "ACU LINE CLOSE");
		next_fd = -1;
	}
	longjmp(Sjbuf, 1);
}

/* This template is for seismo to call ihnp4 
 * the 3 lines marked ---> will be overwritten for the appropriate city
 */
#define PCP_BAUD	3
#define PCP_PHONE	4
#define PCP_CALLBACK	8
#define PCP_CITY	10
#define PCP_RPHONE	12
#define NPCFIELDS	15

static char *PCFlds[] = {
	"PC-PURSUIT",
	"Any",
	"ACU",
	"1200",
	CNULL,	/* <--- **** Welcome to Telenet PC Pursuit ***** */
	"ABORT",
	"Good",	/* Abort of Good bye! */
	")", 	/* <--- Enter your 7-digit phone number (xxx-xxxx) */
	CNULL,	/* ---> 528-1234 */
	"call?", 	/* <--- Which city do you wish to call? */
	CNULL,	/* ---> CHICAGO */
	")", 	/* <--- Enter the phone number you wish to call (xxx-xxxx) */
	CNULL,	/* ---> 690-7171 */
	"R)?", 	/* <--- You are #1 in the queue. Do you want to wait, or Restart (Y/N/R)? */
	"Y",
	CNULL 	/* <--- .....Good Bye! */
};

static char PCP_brand[20];

/*
 *	place a telephone call to system and login, etc.
 *
 *	return codes:
 *		CF_SYSTEM: don't know system
 *		CF_TIME: wrong time to call
 *		CF_DIAL: call failed
 *		CF_NODEV: no devices available to place call
 *		CF_LOGIN: login/password dialog failed
 *
 *		>0  - file no.  -  connect ok
 */

int Dcf = -1;
char *Flds[MAXC/10];
char LineType[10];
extern int LocalOnly;

conn(system)
char *system;
{
	int nf;
	char info[MAXC], wkpre[NAMESIZE], file[NAMESIZE];
	register FILE *fsys;
	int fcode = 0;

	nf = 0;

	fsys = fopen(SYSFILE, "r");
	ASSERT(fsys != NULL, "CAN'T OPEN", SYSFILE, 0);

	DEBUG(4, "finds (%s) called\n", system);
keeplooking:
	while((nf = finds(fsys, system, info, Flds)) > 0) {
		strncpy(LineType, Flds[F_LINE], 10);
		if (LocalOnly) {
			if (strcmp("TCP", LineType)
				&& strcmp("DIR", LineType)
				&& strcmp("LOCAL", LineType) ) {
					fcode = CF_TIME;
					continue;
			}
		}
		sprintf(wkpre, "%c.%.*s", CMDPRE, SYSNSIZE, Rmtname);
		if (!onesys && MaxGrade != DefMaxGrade &&
			!iswrk(file, "chk", Spool, wkpre))  {
				fcode = CF_TIME;
				continue;
		}
		/* For GTE's PC Pursuit */
		if (snccmp(LineType, PCP) == SAME) {
			FILE *dfp;
			int status;
			static struct Devices dev;
			dfp = fopen(DEVFILE, "r");
			ASSERT(dfp != NULL, "Can't open", DEVFILE, 0);
			while ((status=rddev(dfp, &dev)) != FAIL
				&& strcmp(PCP, dev.D_type) != SAME)
					;
			fclose(dfp);
			if (status == FAIL)
				continue;
			if (mlock(PCP) == FAIL) {
				fcode = CF_NODEV;
				logent("DEVICE", "NO");
				continue;
			}
			PCFlds[PCP_BAUD] = dev.D_class;
			PCFlds[PCP_PHONE] = dev.D_calldev;
			PCFlds[PCP_CALLBACK] = dev.D_arg[D_CHAT];
			PCFlds[PCP_CITY] = Flds[F_CLASS];
			PCFlds[PCP_RPHONE] = Flds[F_PHONE];
			strncpy(PCP_brand, dev.D_brand, sizeof(PCP_brand));
			if ((fcode = getto(PCFlds)) < 0)
				continue;
			Dcf = fcode;
			fcode = login(NPCFIELDS, PCFlds, Dcf);
			clsacu(); /* Hang up, they'll call back */
			if (fcode != SUCCESS) {
				fcode = CF_DIAL;
				continue;
			}
			Flds[F_CLASS] = dev.D_class;
			Flds[F_PHONE] = dev.D_line;
			
		} /* end PC Pursuit */
		if ((fcode = getto(Flds)) > 0) 
			break;
	}

	if (nf <= 0) {
		fclose(fsys);
		return fcode ? fcode : nf;
	}

	Dcf = fcode;

	if (fcode >= 0 && snccmp(LineType, PCP) == SAME) {
		AbortOn = "Good";	/* .... Good Bye */
		fcode = expect("****~300", Dcf);
		if (fcode != SUCCESS) {
			DEBUG(4, "\nexpect timed out\n", CNULL);
			fcode = CF_DIAL;
		}
	}
	if (fcode >= 0) {
		DEBUG(4, "login %s\n", "called");
		fcode = login(nf, Flds, Dcf);
	}
	if (fcode < 0) {
		clsacu();
		if (fcode == ABORT) {
			fcode = CF_DIAL;
			goto  keeplooking;
		} else {
			fclose(fsys);
			return CF_LOGIN;
		}
	}
	fclose(fsys);
	fioclex(Dcf);
	return Dcf;
}

/*
 *	connect to remote machine
 *
 *	return codes:
 *		>0  -  file number - ok
 *		FAIL  -  failed
 */

getto(flds)
register char *flds[];
{
	register struct condev *cd;
	int nulldev(), diropn();
	char *line;

	DEBUG(4, "getto: call no. %s ", flds[F_PHONE]);
	DEBUG(4, "for sys %s\n", flds[F_NAME]);

	if (snccmp(flds[F_LINE], "LOCAL") == SAME)
		line = "ACU";
	else
		line = flds[F_LINE];
#ifdef DIALINOUT
	if (snccmp(line, "ACU") != SAME)
		reenable();
#endif DIALINOUT
	CU_end = nulldev;
	if (snccmp(line, PCP) == SAME) {
		for(cd = condevs; cd->CU_meth != NULL; cd++) {
			if (snccmp(PCP_brand, cd->CU_brand) == SAME) {
				CU_end = cd->CU_clos;
				return diropn(flds);
			}
		}
		logent(PCP_brand, "UNSUPPORTED ACU TYPE");
	} else {
		for (cd = condevs; cd->CU_meth != NULL; cd++) {
			if (snccmp(cd->CU_meth, line) == SAME) {
				DEBUG(4, "Using %s to call\n", cd->CU_meth);
				return (*(cd->CU_gen))(flds);
			}
		}
		DEBUG(1, "Can't find %s, assuming DIR\n", flds[F_LINE]);
	}
	return diropn(flds);	/* search failed, so use direct */
}

/*
 *	close call unit
 *
 *	return codes:  none
 */

int (*CU_end)() = nulldev;
clsacu()
{
	/* make *sure* Dcf is no longer exclusive.
	 * Otherwise dual call-in/call-out modems could get stuck.
	 * Unfortunately, doing this here is not ideal, but it is the
	 * easiest place to put the call.
	 * Hopefully everyone honors the LCK protocol, of course
	 */
#ifdef	TIOCNXCL
	if (!IsTcpIp && Dcf >= 0 && ioctl(Dcf, TIOCNXCL, STBNULL) < 0)
		DEBUG(5, "clsacu ioctl %s\n", sys_errlist[errno]);
#endif
	if  (setjmp(Sjbuf))
		logent(Rmtname, "CLOSE TIMEOUT");
	else {
		signal(SIGALRM, alarmtr);
		alarm(20);
		(*(CU_end))(Dcf);
		alarm(0);
	}
	if (close(Dcf) == 0) {
		DEBUG(4, "fd %d NOT CLOSED by CU_clos\n", Dcf);
		logent("clsacu", "NOT CLOSED by CU_clos");
	}
	Dcf = -1;
	CU_end = nulldev;
}

/*
 *	expand phone number for given prefix and number
 */

exphone(in, out)
register char *in, *out;
{
	FILE *fn;
	char pre[MAXPH], npart[MAXPH], tpre[MAXPH], p[MAXPH];
	char buf[BUFSIZ];
	register char *s1;

	if (!isascii(*in) || !isalpha(*in)) {
		strcpy(out, in);
		return;
	}

	s1=pre;
	while (isascii(*in) && isalpha(*in))
		*s1++ = *in++;
	*s1 = '\0';
	s1 = npart;
	while (*in != '\0')
		*s1++ = *in++;
	*s1 = '\0';

	tpre[0] = '\0';
	if ((fn = fopen(DIALFILE, "r")) == NULL)
		DEBUG(2, "CAN'T OPEN %s\n", DIALFILE);
	else {
		while (cfgets(buf, BUFSIZ, fn)) {
			if (sscanf(buf, "%s%s", p, tpre) != 2)
				continue;
			if (strcmp(p, pre) == SAME)
				goto found;
			tpre[0] = '\0';
		}
		DEBUG(2, "CAN'T FIND dialcodes prefix '%s'\n", pre);
	found:;
		fclose(fn);
	}

	strcpy(out, tpre);
	strcat(out, npart);
}

/*
 *	read and decode a line from device file
 *
 *	return code - FAIL at end-of file; 0 otherwise
 */

rddev(fp, dev)
register struct Devices *dev;
FILE *fp;
{
	register int na;

	if (!cfgets(dev->D_argbfr, sizeof(dev->D_argbfr), fp))
		return FAIL;
	na = getargs(dev->D_argbfr, dev->D_arg, 20);
	ASSERT(na >= 4, "BAD DEVICE ENTRY", dev->D_argbfr, 0);
	if (na == 4) {
		dev->D_brand = "";
		na++;
	}
	dev->D_speed = atoi(fdig(dev->D_class));
	dev->D_numargs = na;
	return 0;
}

/*
 *	set system attribute vector
 *
 *	return codes:
 *		>0  -  number of arguments in vector - succeeded
 *		CF_SYSTEM  -  system name not found
 *		CF_TIME  -  wrong time to call
 */

finds(fsys, sysnam, info, flds)
char *sysnam, info[], *flds[];
FILE *fsys;
{
	int na;
	int fcode = 0;

	/* format of fields
	 *	0 name;
	 *	1 time
	 *	2 acu/hardwired
	 *	3 speed
	 *	etc
	 */
	while (cfgets(info, MAXC, fsys) != NULL) {
		na = getargs(info, flds, MAXC/10);
		if (strncmp(sysnam, flds[F_NAME], MAXBASENAME) != SAME)
			continue;
		if (ifdate(flds[F_TIME]) != FAIL)
			/*  found a good entry  */
			return na;
		DEBUG(2, "Wrong time ('%s') to call\n", flds[F_TIME]);
		fcode = CF_TIME;
	}
	return fcode ? fcode : CF_SYSTEM;
}

/*
 *	do login conversation
 *
 *	return codes:  SUCCESS  |  FAIL
 */

login(nf, flds, fn)
register char *flds[];
int nf, fn;
{
	register char *want, *altern;
	int k, ok;

	ASSERT(nf > 4, "TOO FEW LOG FIELDS", CNULL, nf);
	if (setjmp(Cjbuf))
		return FAIL;
	AbortOn = NULL;
	for (k = F_LOGIN; k < nf; k += 2) {
		want = flds[k];
		ok = FAIL;
		while (ok != SUCCESS) {
			altern = index(want, '-');
			if (altern != NULL)
				*altern++ = '\0';
			if (strcmp(want, "ABORT") == 0) {
				AbortOn = flds[k+1];
				DEBUG(4, "ABORT ON: %s\n", AbortOn);
				goto nextfield;
			}
			DEBUG(4, "wanted \"%s\"\n", want);
			ok = expect(want, fn);
			DEBUG(4, "got: %s\n", ok ? "?" : "that");
			if (ok == FAIL) {
				if (altern == NULL) {
					logent("LOGIN", _FAILED);
					return FAIL;
				}
				want = index(altern, '-');
				if (want != NULL)
					*want++ = '\0';
				sendthem(altern, fn);
			} else
				if (ok == ABORT) {
					logent("LOGIN ABORTED", _FAILED);
					return ABORT;
				}
		}
		sleep(1);
		if (k+1 < nf)
			sendthem(flds[k+1], fn);
nextfield: ;
	}
	return SUCCESS;
}


/* conditional table generation to support odd speeds */
struct sg_spds {int sp_val, sp_name;} spds[] = {
#ifdef B50
	{  50,	 B50},
#endif
#ifdef B75
	{  75,	 B75},
#endif
#ifdef B110
	{ 110,	B110},
#endif
#ifdef B150
	{ 150,	B150},
#endif
#ifdef B200
	{ 200,	B200},
#endif
#ifdef B300
	{ 300,  B300},
#endif
#ifdef B600
	{600,	B600},
#endif
#ifdef B1200
	{1200, B1200},
#endif
#ifdef B1800
	{1800, B1800},
#endif
#ifdef B2000
	{2000, B2000},
#endif
#ifdef B2400
	{2400, B2400},
#endif
#ifdef B3600
	{3600, B3600},
#endif
#ifdef B4800
	{4800, B4800},
#endif
#ifdef B7200
	{7200, B7200},
#endif
#ifdef B9600
	{9600, B9600},
#endif
#ifdef B19200
	{19200, B19200},
#endif
#ifdef EXTA
	{19200, EXTA},
#endif
	{0, 0}
};

/*
 *	set speed/echo/mode...
 *
 *	return codes:  none
 */

fixline(tty, spwant)
int tty, spwant;
{
#ifdef	USG
	struct termio ttbuf;
#else	!USG
	struct sgttyb ttbuf;
#endif !USG
	register struct sg_spds *ps;
	int speed = -1;

	for (ps = spds; ps->sp_val; ps++)
		if (ps->sp_val == spwant)
			speed = ps->sp_name;
	ASSERT(speed >= 0, "BAD SPEED", CNULL, speed);
#ifdef	USG
	if (ioctl(tty, TCGETA, &ttbuf) < 0)
		return FAIL;
	/* ttbuf.sg_flags = (ANYP|RAW);
	ttbuf.sg_ispeed = ttbuf.sg_ospeed = speed; */
	ttbuf.c_iflag = (ushort)0;
	ttbuf.c_oflag = (ushort)0;
	ttbuf.c_cflag = (speed|CS8|HUPCL|CREAD);
	ttbuf.c_lflag = (ushort)0;
	ttbuf.c_cc[VMIN] = 6;
	ttbuf.c_cc[VTIME] = 1;
	if (ioctl(tty, TCSETA, &ttbuf) < 0)
		return FAIL;
#else	!USG
	if (ioctl(tty, TIOCGETP, &ttbuf) < 0)
		return FAIL;
	ttbuf.sg_flags = (ANYP|RAW);
	ttbuf.sg_ispeed = ttbuf.sg_ospeed = speed;
	if (ioctl(tty, TIOCSETP, &ttbuf) < 0)
		return FAIL;
#endif
#ifndef	USG
	if (ioctl(tty, TIOCHPCL, STBNULL) < 0)
		return FAIL;
	if (ioctl(tty, TIOCEXCL, STBNULL) < 0)
		return FAIL;
#endif
	linebaudrate = spwant;
	return SUCCESS;
}

#define MR 100

/*
 *	look for expected string
 *
 *	return codes:
 *		0  -  found
 *		FAIL  -  lost line or too many characters read
 *		some character  -  timed out
 */

expect(str, fn)
register char *str;
int fn;
{
	char rdvec[MR];
	register char *rp = rdvec, *strptr;
	int kr, cnt_char;
	char nextch;
	int timo = MAXMSGTIME;

	if (*str == '\0' || strcmp(str, "\"\"") == SAME)
		return SUCCESS;
	/* Cleanup str, convert \0xx strings to one char  */
	for (strptr = str; *strptr; strptr++) {
		if (*strptr == '\\')
			switch(*++strptr) {
			case 's':
				DEBUG(5, "BLANK\n", CNULL);
				*strptr = ' ';
				break;
			default:
				strptr--;  /* back up to backslash */
				sscanf(strptr + 1,"%o", &cnt_char);
				DEBUG(6, "BACKSLASHED %02xH\n", cnt_char);
				*strptr = (char) (cnt_char);
				strcpy(&strptr[1], &strptr[4]);
			}
	}

	strptr = index(str, '~');
	if (strptr != NULL) {
		*strptr++ = '\0';
		timo = atoi(strptr);
		if (timo <= 0)
			timo = MAXMSGTIME;
	}

	if (setjmp(Sjbuf))
		return FAIL;
	signal(SIGALRM, alarmtr);
	alarm(timo);
	*rp = 0;
	while (notin(str, rdvec)) {
		int c;
		if(ExpectHopeful && AbortOn != NULL && !notin(AbortOn, rdvec)) {
			DEBUG(1, "Call aborted on '%s'\n", AbortOn);
			alarm(0);
			return ABORT;
		}
		kr = read(fn, &nextch, 1);
		if (kr <= 0) {
			alarm(0);
			if (ExpectHopeful) {
				DEBUG(4, "lost line kr - %d\n, ", kr);
				logent("LOGIN", "LOST LINE");
			} else {
				DEBUG(4, "Lost line, read returns %d\n, ", kr);
			}
			return FAIL;
		}
		c = nextch & 0177;
		if (c == '\0')
			continue;
		DEBUG(4, (isprint(c) || isspace(c)) ? "%c" : "\\%03o", c);
		*rp++ = c;
		if (rp >= rdvec + MR) {
			register char *p;
			for (p = rdvec+MR/2; p < rp; p++)
				*(p-MR/2) = *p;
			rp -= MR/2;
		}
		*rp = '\0';
	}
	alarm(0);
	return SUCCESS;
}


/*
 * Determine next file descriptor that would be allocated.
 * This permits later closing of a file whose open was interrupted.
 * It is a UNIX kernel problem, but it has to be handled.
 * unc!smb (Steve Bellovin) probably first discovered it.
 */
getnextfd()
{
	close(next_fd = open("/", 0));
}

/*
 *	send line of login sequence
 *
 *	return codes:  none
 */
sendthem(str, fn)
register char *str;
int fn;
{
	register char *strptr;
	int i, n, cr = 1;
	register char c;
	static int p_init = 0;

	DEBUG(5, "send \"%s\"\n", str);

	if (!p_init) {
		p_init++;
		bld_partab(P_EVEN);
	}

	if (prefix("BREAK", str)) {
		sscanf(&str[5], "%1d", &i);
		if (i <= 0 || i > 10)
			i = 3;
		/* send break */
		genbrk(fn, i);
		return;
	}

	if (prefix("PAUSE", str)) {
		sscanf(&str[5], "%1d", &i);
		if (i <= 0 || i > 10)
			i = 3;
		/* pause for a while */
		sleep((unsigned)i);
		return;
	}

	if (strcmp(str, "EOT") == SAME) {
		p_chwrite(fn, '\04');
		return;
	}

	/* Send a '\n' */
	if (strcmp(str, "LF") == SAME) {
		p_chwrite(fn, '\n');
		return;
	}

	/* Send a '\r' */
	if (strcmp(str, "CR") == SAME) {
		p_chwrite(fn, '\r');
		return;
	}

	/* Set parity as needed */
	if (strcmp(str, "P_ZERO") == SAME) {
		bld_partab(P_ZERO);
		return;
	}
	if (strcmp(str, "P_ONE") == SAME) {
		bld_partab(P_ONE);
		return;
	}
	if (strcmp(str, "P_EVEN") == SAME) {
		bld_partab(P_EVEN);
		return;
	}
	if (strcmp(str, "P_ODD") == SAME) {
		bld_partab(P_ODD);
		return;
	}

	/* If "", just send '\r' */
	if (strcmp(str, "\"\"") == SAME) {
		p_chwrite(fn, '\r');
		return;
	}

	strptr = str;
	while ((c = *strptr++) != '\0') {
		if (c == '\\') {
			switch(*strptr++) {
			case '\0':
				DEBUG(5, "TRAILING BACKSLASH IGNORED\n", CNULL);
				--strptr;
				continue;
			case 's':
				DEBUG(5, "BLANK\n", CNULL);
				c = ' ';
				break;
			case 'd':
				DEBUG(5, "DELAY\n", CNULL);
				sleep(1);
				continue;
			case 'n':
				DEBUG(5, "NEW LINE\n", CNULL);
				c = '\n';
				break;
			case 'r':
				DEBUG(5, "RETURN\n", CNULL);
				c = '\r';
				break;
			case 'b':
				if (isdigit(*strptr)) {
					i = (*strptr++ - '0');
					if (i <= 0 || i > 10)
						i = 3;
				} else
					i = 3;
				/* send break */
				genbrk(fn, i);
				if (*strptr == '\0')
					cr = 0;
				continue;
			case 'c':
				if (*strptr == '\0') {
					DEBUG(5, "NO CR\n", CNULL);
					cr = 0;
				} else
					DEBUG(5, "NO CR - IGNORED NOT EOL\n", CNULL);
				continue;
#define isoctal(x)	((x >= '0') && (x <= '7'))
			default:
				if (isoctal(strptr[-1])) {
					i = 0;
					n = 0;
					--strptr;
					while (isoctal(*strptr) && ++n <= 3)
						i = i * 8 + (*strptr++ - '0');
					DEBUG(5, "\\%o\n", i);
					p_chwrite(fn, (char)i);
					continue;
				}
			}
		}
		p_chwrite(fn, c);
	}

	if (cr)
		p_chwrite(fn, '\r');
	return;
}

p_chwrite(fd, c)
int fd;
char c;
{
	c = par_tab[c&0177];
	if (write(fd, &c, 1) != 1) {
		logent(sys_errlist[errno], "BAD WRITE");
		longjmp(Cjbuf, 2);
	}
}

/*
 * generate parity table for use by p_chwrite.
 */
bld_partab(type)
int type;
{
	register int i, j, n;

	for (i = 0; i < sizeof(par_tab); i++) {
		n = 0;
		for (j = i&(sizeof(par_tab)-1); j; j = (j-1)&j)
			n++;
		par_tab[i] = i;
		if (type == P_ONE
		 || (type == P_EVEN && (n&01) != 0)
		 || (type == P_ODD && (n&01) == 0))
			par_tab[i] |= sizeof(par_tab);
	}
}

/*
 *	check for occurrence of substring "sh"
 *
 *	return codes:
 *		0  -  found the string
 *		1  -  not in the string
 */
notin(sh, lg)
register char *sh, *lg;
{
	while (*lg != '\0') {
		if (wprefix(sh, lg))
			return 0;
		else
			lg++;
	}
	return 1;
}

/*
 *	Allow multiple date specifications separated by ','.
 */
ifdate(p)
register char *p;
{
	register char *np;
	register int ret, g;
	int rtime, i;

	/*  pick up retry time for failures  */
	/*  global variable Retrytime is set here  */
	if ((np = index(p, ';')) == NULL) {
		Retrytime = RETRYTIME;
	} else {
		i = sscanf(np+1, "%d", &rtime);
		if (i < 1 || rtime < 0)
			rtime = 5;
		Retrytime  = rtime * 60;
	}

	ret = FAIL;
	MaxGrade = '\0';
	do {
		np = strpbrk(p, ",|");	/* prefer , but allow | for compat */
		if (np)
			*np = '\0';
		g = ifadate(p);
		DEBUG(11,"ifadate returns %o\n", g);
		if (g != FAIL) {
			ret = SUCCESS;
			if (g > MaxGrade)
				MaxGrade = g;
		}
		if (np)
			*np = ',';
		p = np + 1;
	} while (np);
	if (MaxGrade == '\0')
		MaxGrade = DefMaxGrade;
	return ret;
}

/*
 *	this routine will check a string (string)
 *	like "MoTu0800-1730" to see if the present
 *	time is within the given limits.
 *	SIDE EFFECT - Retrytime is set
 *
 *	return codes:
 *		0  -  not within limits
 *		1  -  within limits
 */

ifadate(string)
char *string;
{
	static char *days[]={
		"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", 0
	};
	time_t clock;
	register char *s = string;
	int i, tl, th, tn, dayok=0;
	struct tm *localtime();
	struct tm *tp;
	char *p, MGrade;

	if ((p = index(s, '/')) == NULL)
		MGrade = DefMaxGrade;
	else
		MGrade = p[1];

	time(&clock);
	tp = localtime(&clock);
	while (isascii(*s) && isalpha(*s)) {
		for (i = 0; days[i]; i++) {
			if (prefix(days[i], s))
				if (tp->tm_wday == i)
					dayok = 1;
		}

		if (prefix("Wk", s))
			if (tp->tm_wday >= 1 && tp->tm_wday <= 5)
				dayok = 1;
		if (prefix("Any", s))
			dayok = 1;
		if (prefix("Evening", s)) {
			/* Sat or Sun */
			if (tp->tm_wday == 6 || tp->tm_wday == 0
				|| tp->tm_hour >= 17 || tp->tm_hour < 8)
					dayok = 1;
		}
		if (prefix("Night", s)) {
			if (tp->tm_wday == 6  /* Sat */
				|| tp->tm_hour >= 23 || tp->tm_hour < 8
					/* Sunday before 5pm */
				|| (tp->tm_wday == 0 && tp->tm_hour < 17))
					dayok = 1;
		}
		if (prefix("NonPeak", s)) { /* For Tymnet and PC Pursuit */
			/* Sat or Sun */
			if (tp->tm_wday == 6 || tp->tm_wday == 0
				|| tp->tm_hour >= 18 || tp->tm_hour < 7)
					dayok = 1;
		}
		s++;
	}

	if (dayok == 0 && s != string)
		return FAIL;
	i = sscanf(s, "%d-%d", &tl, &th);
  	if (i < 2)
  		return MGrade;
	tn = tp->tm_hour * 100 + tp->tm_min;
  	if (th < tl) { 		/* crosses midnight */
  		if (tl <= tn || tn < th)
  			return MGrade;
  	} else {
		if (tl <= tn && tn < th)
			return MGrade;
	}
	return FAIL;
}

/*
 *	find first digit in string
 *
 *	return - pointer to first digit in string or end of string
 */
char *
fdig(cp)
register char *cp;
{
	register char *c;

	for (c = cp; *c; c++)
		if (*c >= '0' && *c <= '9')
			break;
	return c;
}

/*
 * Compare strings:  s1>s2: >0  s1==s2: 0  s1<s2: <0
 * Strings are compared as if they contain all capital letters.
 */
snccmp(s1, s2)
register char *s1, *s2;
{
	char c1, c2;

	if (islower(*s1))
		c1 = toupper(*s1);
	else
		c1 = *s1;
	if (islower(*s2))
		c2 = toupper(*s2);
	else
		c2 = *s2;

	while (c1 == c2) {
		if (*s1++ == '\0')
			return 0;
		s2++;
		if (islower(*s1))
			c1 = toupper(*s1);
		else
			c1 = *s1;
		if (islower(*s2))
			c2 = toupper(*s2);
		else
			c2 = *s2;
	}
	return c1 - c2;
}

/*
 * Compare strings:  s1>s2: >0  s1==s2: 0  s1<s2: <0
 * Strings are compared as if they contain all capital letters.
 */
sncncmp(s1, s2, n)
register char *s1, *s2;
register int n;
{
	char c1, c2;

	if (islower(*s1))
		c1 = toupper(*s1);
	else
		c1 = *s1;
	if (islower(*s2))
		c2 = toupper(*s2);
	else
		c2 = *s2;

	while ( --n >= 0 && c1 == c2) {
		if (*s1++ == '\0')
			return 0;
		s2++;
		if (islower(*s1))
			c1 = toupper(*s1);
		else
			c1 = *s1;
		if (islower(*s2))
			c2 = toupper(*s2);
		else
			c2 = *s2;
	}
	return n<0 ? 0 : (c1 - c2);
}
/*
 * do chat script
 * occurs after local port is opened,
 * before 'dialing' the other machine.
 */
dochat(dev, flds, fd)
register struct Devices *dev;
char *flds[];
int fd;
{
	register int i;
	register char *p;
	char bfr[sizeof(dev->D_argbfr)];

	if (dev->D_numargs <= 5)
		return(0);
	DEBUG(4, "dochat called %d\n", dev->D_numargs);
	for (i = 0; i < dev->D_numargs-5; i++) {
		sprintf(bfr, dev->D_arg[D_CHAT+i], flds[F_PHONE]);
		if (strcmp(bfr, dev->D_arg[D_CHAT+i])) {
			p = malloc((unsigned)strlen(bfr)+1);
			if (p != NULL) {
				strcpy(p, bfr);
				dev->D_arg[D_CHAT+i] = p;
			}
		}
	}
	/* following is a kludge because login() arglist is a kludge */
	i = login(dev->D_numargs, &dev->D_arg[D_CHAT-5], fd);
	/*
	 * If login() last did a sendthem(), must pause so things can settle.
	 * But don't bother if chat failed.
	 */
	if (i == 0 && (dev->D_numargs&01))
		sleep(2);
	return(i);
}

/*
 *	fix kill/echo/raw on line
 *
 *	return codes:  none
 */
fixmode(tty)
register int tty;
{
#ifdef	USG
	struct termio ttbuf;
#else	!USG
	struct sgttyb ttbuf;
#endif	!USG
	register struct sg_spds *ps;
	int speed;

	if (IsTcpIp)
		return;
#ifdef	USG
	ioctl(tty, TCGETA, &ttbuf);
	ttbuf.c_iflag = ttbuf.c_oflag = ttbuf.c_lflag = (ushort)0;
	speed = ttbuf.c_cflag &= (CBAUD);
	ttbuf.c_cflag |= (CS8|CREAD);
	ttbuf.c_cc[VMIN] = 6;
	ttbuf.c_cc[VTIME] = 1;
	ioctl(tty, TCSETA, &ttbuf);
#else	!USG
	ioctl(tty, TIOCGETP, &ttbuf);
	ttbuf.sg_flags = (ANYP | RAW);
	ioctl(tty, TIOCSETP, &ttbuf);
	speed = ttbuf.sg_ispeed;
	ioctl(tty, TIOCEXCL, STBNULL);
#endif	!USG

	for (ps = spds; ps->sp_val; ps++)
		if (ps->sp_name == speed) {
			linebaudrate = ps->sp_val;
			DEBUG(9,"Incoming baudrate is %d\n", linebaudrate);
			return;
		}
	ASSERT(linebaudrate >= 0, "BAD SPEED", CNULL, speed);
}
