[net.sources] BRL's PACXMON program

mike@brl-tgr.ARPA (Michael John Muuss <mike>) (10/31/84)

Enclosed are new version of the 4.2 BSD /etc/getty program,
plus the sources and manual page for BRL's PACXMON program.
Also included is the Melbourn code for autobauding if you
don't have a PACX.

echo 'sh - gettytab'
sed 's/^X//' <<'________This_Is_The_END________' >>gettytab
X#	@(#)gettytab	4.5 (Berkeley) 83/08/08
X
X#
X# Most of the table entries here are just copies of the
X# old getty table, it is by no means certain, or even likely,
X# then any of them are optimal for any purpose whatever.
X# Nor is it likely that more than a couple are even correct
X#
X
X#
X# The default gettytab entry, used to set defaults for all other
X# entries, and in cases where getty is called with no table name
X#
Xdefault:\
X	:ap:cb:ce:ck:\
X	:im=\r\n\r\nBRL VAX UNIX [4.2 BSD] (%h)\r\n\r\r\n\r:sp#1200:
X
XP|PACX:\
X	:gp:to#60:
X
XA|Auto-baud:\
X	:ab:sp#2400:f0#040:
X
X#
X# Fixed speed entries
X#
X#	the "std.NNN" names are known to the special case
X#	portselector code in getty, however they can
X#	be assigned to any table desired.
X#
Xa|std.110|110-baud:\
X	:nd#1:cd#1:uc:sp#110:
Xb|std.134|134.5-baud:\
X	:ep:nd#1:cd#2:ff#1:td#1:sp#134:nl:
X1|std.150|150-baud:\
X	:ep:nd#1:cd#2:td#1:fd#1:sp#150:nl:lm=\E\72\6\6\17login\72 :
Xc|std.300|300-baud:\
X	:nd#1:cd#1:sp#300:
Xd|std.600|600-baud:\
X	:nd#1:cd#1:sp#600:
Xf|std.1200|1200-baud:\
X	:sp#1200:
X6|std.2400|2400-baud:\
X	:sp#2400:
X7|std.4800|4800-baud:\
X	:sp#4800:
X2|std.9600|9600-baud:\
X	:sp#9600:
X
X#
X# Dial in rotary tables, speed selection via 'break'
X#
X0|d300|Dial-300:\
X	:nx=d1200:cd#2:sp#300:
Xd1200|Dial-1200:\
X	:nx=d150:fd#1:sp#1200:
Xd150|Dial-150:\
X	:nx=d110:lm@:tc=150-baud:
Xd110|Dial-110:\
X	:nx=d300:tc=300-baud:
X
X#
X# Odd special case terminals
X#
X-|tty33|asr33|Pity the poor user of this beast:\
X	:tc=110-baud:
X
X4|Console|Console Decwriter II:\
X	:nd@:cd@:rw:tc=300-baud:
X
Xi|Interdata console:\
X	:uc:sp#0:
X
Xl|lsi chess terminal:\
X	:sp#300:
X
X#
X# Fast dialup terminals, 1200/300 rotary (can start either way)
X#
X3|D1200|Fast-Dial-1200:\
X	:nx=D300:tc=1200-baud:
X5|D300|Fast-Dial-300:\
X	:nx=D1200:tc=300-baud:
X
X#
X# Wierdo special case for fast crt's with hardcopy devices
X#
X8|T9600|CRT with hardcopy:\
X	:nx=T300:tc=9600-baud:
X9|T300|CRT with hardcopy (300):\
X	:nx=T9600:tc=300-baud:
X
X#
X# Plugboard, and misc other terminals
X#
Xp|P9600|Plugboard-9600:\
X	:nx=P300:tc=9600-baud:
Xq|P300|Plugboard-300:\
X	:nx=P1200:tc=300-baud:
Xr|P1200|Plugboard-1200:\
X	:nx=P9600:tc=1200-baud:
X
X#
X# XXXX Port selector
X#
Xs|Port Selector:\
X	:ps:sp#1200:
________This_Is_The_END________
echo 'sh - gettytab.c'
sed 's/^X//' <<'________This_Is_The_END________' >>gettytab.c
X/*
X *			G E T T Y T A B . C 
X *
X * $Revision: 1.2 $
X *
X * $Log:	gettytab.c,v $
X * Revision 1.2  83/12/16  01:55:33  rsm
X * Added distinctive RCS header
X * 
X */
X#ifndef lint
Xstatic char RCSid[] = "@(#)$Header: gettytab.c,v 1.2 83/12/16 01:55:33 rsm BRL $";
X#endif
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)gettytab.c	4.2 (Berkeley) 83/09/25";
X#endif
X
X#include <ctype.h>
X
X#define	TABBUFSIZ	512
X
Xstatic	char *tbuf;
Xint	hopcount;	/* detect infinite loops in termcap, init 0 */
Xchar	*skip();
Xchar	*getstr();
Xchar	*decode();
X
X/*
X * Get an entry for terminal name in buffer bp,
X * from the termcap file.  Parse is very rudimentary;
X * we just notice escaped newlines.
X */
Xgetent(bp, name)
X	char *bp, *name;
X{
X	register char *cp;
X	register int c;
X	register int i = 0, cnt = 0;
X	char ibuf[TABBUFSIZ];
X	char *cp2;
X	int tf;
X
X	tbuf = bp;
X	tf = open("/etc/gettytab", 0);
X	if (tf < 0)
X		return (-1);
X	for (;;) {
X		cp = bp;
X		for (;;) {
X			if (i == cnt) {
X				cnt = read(tf, ibuf, TABBUFSIZ);
X				if (cnt <= 0) {
X					close(tf);
X					return (0);
X				}
X				i = 0;
X			}
X			c = ibuf[i++];
X			if (c == '\n') {
X				if (cp > bp && cp[-1] == '\\'){
X					cp--;
X					continue;
X				}
X				break;
X			}
X			if (cp >= bp+TABBUFSIZ) {
X				write(2,"Gettytab entry too long\n", 24);
X				break;
X			} else
X				*cp++ = c;
X		}
X		*cp = 0;
X
X		/*
X		 * The real work for the match.
X		 */
X		if (namatch(name)) {
X			close(tf);
X			return(nchktc());
X		}
X	}
X}
X
X/*
X * tnchktc: check the last entry, see if it's tc=xxx. If so,
X * recursively find xxx and append that entry (minus the names)
X * to take the place of the tc=xxx entry. This allows termcap
X * entries to say "like an HP2621 but doesn't turn on the labels".
X * Note that this works because of the left to right scan.
X */
X#define	MAXHOP	32
Xnchktc()
X{
X	register char *p, *q;
X	char tcname[16];	/* name of similar terminal */
X	char tcbuf[TABBUFSIZ];
X	char *holdtbuf = tbuf;
X	int l;
X
X	p = tbuf + strlen(tbuf) - 2;	/* before the last colon */
X	while (*--p != ':')
X		if (p<tbuf) {
X			write(2, "Bad gettytab entry\n", 19);
X			return (0);
X		}
X	p++;
X	/* p now points to beginning of last field */
X	if (p[0] != 't' || p[1] != 'c')
X		return(1);
X	strcpy(tcname,p+3);
X	q = tcname;
X	while (q && *q != ':')
X		q++;
X	*q = 0;
X	if (++hopcount > MAXHOP) {
X		write(2, "Getty: infinite tc= loop\n", 25);
X		return (0);
X	}
X	if (getent(tcbuf, tcname) != 1)
X		return(0);
X	for (q=tcbuf; *q != ':'; q++)
X		;
X	l = p - holdtbuf + strlen(q);
X	if (l > TABBUFSIZ) {
X		write(2, "Gettytab entry too long\n", 24);
X		q[TABBUFSIZ - (p-tbuf)] = 0;
X	}
X	strcpy(p, q+1);
X	tbuf = holdtbuf;
X	return(1);
X}
X
X/*
X * Tnamatch deals with name matching.  The first field of the termcap
X * entry is a sequence of names separated by |'s, so we compare
X * against each such name.  The normal : terminator after the last
X * name (before the first field) stops us.
X */
Xnamatch(np)
X	char *np;
X{
X	register char *Np, *Bp;
X
X	Bp = tbuf;
X	if (*Bp == '#')
X		return(0);
X	for (;;) {
X		for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
X			continue;
X		if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
X			return (1);
X		while (*Bp && *Bp != ':' && *Bp != '|')
X			Bp++;
X		if (*Bp == 0 || *Bp == ':')
X			return (0);
X		Bp++;
X	}
X}
X
X/*
X * Skip to the next field.  Notice that this is very dumb, not
X * knowing about \: escapes or any such.  If necessary, :'s can be put
X * into the termcap file in octal.
X */
Xstatic char *
Xskip(bp)
X	register char *bp;
X{
X
X	while (*bp && *bp != ':')
X		bp++;
X	if (*bp == ':')
X		bp++;
X	return (bp);
X}
X
X/*
X * Return the (numeric) option id.
X * Numeric options look like
X *	li#80
X * i.e. the option string is separated from the numeric value by
X * a # character.  If the option is not found we return -1.
X * Note that we handle octal numbers beginning with 0.
X */
Xlong
Xgetnum(id)
X	char *id;
X{
X	register long i, base;
X	register char *bp = tbuf;
X
X	for (;;) {
X		bp = skip(bp);
X		if (*bp == 0)
X			return (-1);
X		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
X			continue;
X		if (*bp == '@')
X			return(-1);
X		if (*bp != '#')
X			continue;
X		bp++;
X		base = 10;
X		if (*bp == '0')
X			base = 8;
X		i = 0;
X		while (isdigit(*bp))
X			i *= base, i += *bp++ - '0';
X		return (i);
X	}
X}
X
X/*
X * Handle a flag option.
X * Flag options are given "naked", i.e. followed by a : or the end
X * of the buffer.  Return 1 if we find the option, or 0 if it is
X * not given.
X */
Xgetflag(id)
X	char *id;
X{
X	register char *bp = tbuf;
X
X	for (;;) {
X		bp = skip(bp);
X		if (!*bp)
X			return (-1);
X		if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
X			if (!*bp || *bp == ':')
X				return (1);
X			else if (*bp == '!')
X				return (0);
X			else if (*bp == '@')
X				return(-1);
X		}
X	}
X}
X
X/*
X * Get a string valued option.
X * These are given as
X *	cl=^Z
X * Much decoding is done on the strings, and the strings are
X * placed in area, which is a ref parameter which is updated.
X * No checking on area overflow.
X */
Xchar *
Xgetstr(id, area)
X	char *id, **area;
X{
X	register char *bp = tbuf;
X
X	for (;;) {
X		bp = skip(bp);
X		if (!*bp)
X			return (0);
X		if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
X			continue;
X		if (*bp == '@')
X			return(0);
X		if (*bp != '=')
X			continue;
X		bp++;
X		return (decode(bp, area));
X	}
X}
X
X/*
X * Tdecode does the grung work to decode the
X * string capability escapes.
X */
Xstatic char *
Xdecode(str, area)
X	register char *str;
X	char **area;
X{
X	register char *cp;
X	register int c;
X	register char *dp;
X	int i;
X
X	cp = *area;
X	while ((c = *str++) && c != ':') {
X		switch (c) {
X
X		case '^':
X			c = *str++ & 037;
X			break;
X
X		case '\\':
X			dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
X			c = *str++;
Xnextc:
X			if (*dp++ == c) {
X				c = *dp++;
X				break;
X			}
X			dp++;
X			if (*dp)
X				goto nextc;
X			if (isdigit(c)) {
X				c -= '0', i = 2;
X				do
X					c <<= 3, c |= *str++ - '0';
X				while (--i && isdigit(*str));
X			}
X			break;
X		}
X		*cp++ = c;
X	}
X	*cp++ = 0;
X	str = *area;
X	*area = cp;
X	return (str);
X}
________This_Is_The_END________
echo 'sh - gettytab.h'
sed 's/^X//' <<'________This_Is_The_END________' >>gettytab.h
X/*
X *			G E T T Y T A B . H 
X *
X * $Revision: 1.2 $
X *
X * $Log:	gettytab.h,v $
X * Revision 1.2  83/12/16  01:55:40  rsm
X * Added distinctive RCS header
X * 
X */
X
X/*	gettytab.h	4.3	83/07/09	*/
X
X/*
X * Getty description definitions.
X */
Xstruct	gettystrs {
X	char	*field;		/* name to lookup in gettytab */
X	char	*defalt;	/* value we find by looking in defaults */
X	char	*value;		/* value that we find there */
X};
X
Xstruct	gettynums {
X	char	*field;		/* name to lookup */
X	long	defalt;		/* number we find in defaults */
X	long	value;		/* number we find there */
X	int	set;		/* we actually got this one */
X};
X
Xstruct gettyflags {
X	char	*field;		/* name to lookup */
X	char	invrt;		/* name existing in gettytab --> false */
X	char	defalt;		/* true/false in defaults */
X	char	value;		/* true/false flag */
X	char	set;		/* we found it */
X};
X
X/*
X * String values.
X */
X#define	NX	gettystrs[0].value
X#define	CL	gettystrs[1].value
X#define IM	gettystrs[2].value
X#define	LM	gettystrs[3].value
X#define	ER	gettystrs[4].value
X#define	KL	gettystrs[5].value
X#define	ET	gettystrs[6].value
X#define	PC	gettystrs[7].value
X#define	TT	gettystrs[8].value
X#define	EV	gettystrs[9].value
X#define	LO	gettystrs[10].value
X#define HN	gettystrs[11].value
X#define HE	gettystrs[12].value
X#define IN	gettystrs[13].value
X#define QU	gettystrs[14].value
X#define XN	gettystrs[15].value
X#define XF	gettystrs[16].value
X#define BK	gettystrs[17].value
X#define SU	gettystrs[18].value
X#define DS	gettystrs[19].value
X#define RP	gettystrs[20].value
X#define FL	gettystrs[21].value
X#define WE	gettystrs[22].value
X#define LN	gettystrs[23].value
X
X/*
X * Numeric definitions.
X */
X#define	IS	gettynums[0].value
X#define	OS	gettynums[1].value
X#define	SP	gettynums[2].value
X#define	ND	gettynums[3].value
X#define	CD	gettynums[4].value
X#define	TD	gettynums[5].value
X#define	FD	gettynums[6].value
X#define	BD	gettynums[7].value
X#define	TO	gettynums[8].value
X#define	F0	gettynums[9].value
X#define	F0set	gettynums[9].set
X#define	F1	gettynums[10].value
X#define	F1set	gettynums[10].set
X#define	F2	gettynums[11].value
X#define	F2set	gettynums[11].set
X#define	PF	gettynums[12].value
X
X/*
X * Boolean values.
X */
X#define	HT	gettyflags[0].value
X#define	NL	gettyflags[1].value
X#define	EP	gettyflags[2].value
X#define	EPset	gettyflags[2].set
X#define	OP	gettyflags[3].value
X#define	OPset	gettyflags[2].set
X#define	AP	gettyflags[4].value
X#define	APset	gettyflags[2].set
X#define	EC	gettyflags[5].value
X#define	CO	gettyflags[6].value
X#define	CB	gettyflags[7].value
X#define	CK	gettyflags[8].value
X#define	CE	gettyflags[9].value
X#define	PE	gettyflags[10].value
X#define	RW	gettyflags[11].value
X#define	XC	gettyflags[12].value
X#define	LC	gettyflags[13].value
X#define	UC	gettyflags[14].value
X#define	IG	gettyflags[15].value
X#define	PS	gettyflags[16].value
X#define	HC	gettyflags[17].value
X#define UB	gettyflags[18].value
X#define AB	gettyflags[19].value
X#define GP	gettyflags[20].value
X
Xint	getent();
Xlong	getnum();
Xint	getflag();
Xchar	*getstr();
X
Xextern	struct gettyflags gettyflags[];
Xextern	struct gettynums gettynums[];
Xextern	struct gettystrs gettystrs[];
Xextern	int hopcount;
________This_Is_The_END________
echo 'sh - init.c'
sed 's/^X//' <<'________This_Is_The_END________' >>init.c
X/*
X *			I N I T . C 
X *
X * $Revision: 1.3 $
X *
X * $Log:	init.c,v $
X * Revision 1.3  84/02/23  21:44:38  rsm
X * Various changes including pacx support and autobauding
X * 
X * Revision 1.2  83/12/16  01:55:46  rsm
X * Added distinctive RCS header
X * 
X */
X#ifndef lint
Xstatic char RCSid[] = "@(#)$Header: init.c,v 1.3 84/02/23 21:44:38 rsm BRL $";
X#endif
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)init.c	4.4 (Berkeley) 83/07/09";
X#endif
X
X/*
X * Getty table initializations.
X *
X * Melbourne getty.
X */
X#include <sgtty.h>
X#include "gettytab.h"
X
Xextern	struct sgttyb tmode;
Xextern	struct tchars tc;
Xextern	struct ltchars ltc;
Xextern	char hostname[];
X
Xstruct	gettystrs gettystrs[] = {
X	{ "nx" },			/* next table */
X	{ "cl" },			/* screen clear characters */
X	{ "im" },			/* initial message */
X	{ "lm", "login: " },		/* login message */
X	{ "er", &tmode.sg_erase },	/* erase character */
X	{ "kl", &tmode.sg_kill },	/* kill character */
X	{ "et", &tc.t_eofc },		/* eof chatacter (eot) */
X	{ "pc", "" },			/* pad character */
X	{ "tt" },			/* terminal type */
X	{ "ev" },			/* enviroment */
X	{ "lo", "/bin/login" },		/* login program */
X	{ "hn", hostname },		/* host name */
X	{ "he" },			/* host name edit */
X	{ "in", &tc.t_intrc },		/* interrupt char */
X	{ "qu", &tc.t_quitc },		/* quit char */
X	{ "xn", &tc.t_startc },		/* XON (start) char */
X	{ "xf", &tc.t_stopc },		/* XOFF (stop) char */
X	{ "bk", &tc.t_brkc },		/* brk char (alt \n) */
X	{ "su", &ltc.t_suspc },		/* suspend char */
X	{ "ds", &ltc.t_dsuspc },	/* delayed suspend */
X	{ "rp", &ltc.t_rprntc },	/* reprint char */
X	{ "fl", &ltc.t_flushc },	/* flush output */
X	{ "we", &ltc.t_werasc },	/* word erase */
X	{ "ln", &ltc.t_lnextc },	/* literal next */
X	{ 0 }
X};
X
Xstruct	gettynums gettynums[] = {
X	{ "is" },			/* input speed */
X	{ "os" },			/* output speed */
X	{ "sp" },			/* both speeds */
X	{ "nd" },			/* newline delay */
X	{ "cd" },			/* carriage-return delay */
X	{ "td" },			/* tab delay */
X	{ "fd" },			/* form-feed delay */
X	{ "bd" },			/* backspace delay */
X	{ "to" },			/* timeout */
X	{ "f0" },			/* output flags */
X	{ "f1" },			/* input flags */
X	{ "f2" },			/* user mode flags */
X	{ "pf" },			/* delay before flush at 1st prompt */
X	{ 0 }
X};
X
Xstruct	gettyflags gettyflags[] = {
X	{ "ht",	0 },			/* has tabs */
X	{ "nl",	1 },			/* has newline char */
X	{ "ep",	0 },			/* even parity */
X	{ "op",	0 },			/* odd parity */
X	{ "ap",	0 },			/* any parity */
X	{ "ec",	1 },			/* no echo */
X	{ "co",	0 },			/* console special */
X	{ "cb",	0 },			/* crt backspace */
X	{ "ck",	0 },			/* crt kill */
X	{ "ce",	0 },			/* crt erase */
X	{ "pe",	0 },			/* printer erase */
X	{ "rw",	1 },			/* don't use raw */
X	{ "xc",	1 },			/* don't ^X ctl chars */
X	{ "lc",	0 },			/* terminal las lower case */
X	{ "uc",	0 },			/* terminal has no lower case */
X	{ "ig",	0 },			/* ignore garbage */
X	{ "ps",	0 },			/* do port selector speed select */
X	{ "hc",	1 },			/* don't set hangup on close */
X	{ "ub", 0 },			/* unbuffered output */
X	{ "ab", 0 },			/* auto-baud detect with '\r' */
X	{ "gp", 0 },			/* connected to Gandalf PACX IV */
X	{ 0 }
X};
________This_Is_The_END________
echo 'sh - main.c'
sed 's/^X//' <<'________This_Is_The_END________' >>main.c
X/*
X *			M A I N . C 
X *
X * $Revision: 1.4 $
X *
X * $Log:	main.c,v $
X * Revision 1.4  84/08/09  01:48:01  dpk
X * Modified getty to pass line number as hostname to login
X * 
X * Revision 1.3  84/02/23  21:44:47  rsm
X * Various changes including pacx support and autobauding
X * 
X * Revision 1.2  83/12/16  01:55:53  rsm
X * Added distinctive RCS header
X * 
X */
X#ifndef lint
Xstatic char RCSid[] = "@(#)$Header: main.c,v 1.4 84/08/09 01:48:01 dpk BRL $";
X#endif
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)main.c	4.5 (Berkeley) 83/08/01";
X#endif
X
X/*
X * getty -- adapt to terminal speed on dialup, and call login
X *
X * Melbourne getty, June 83, kre.
X */
X
X#include <sgtty.h>
X#include <signal.h>
X#include <ctype.h>
X#include <setjmp.h>
X#include "gettytab.h"
X
Xstruct	sgttyb tmode = {
X	0, 0, CERASE, CKILL, 0
X};
Xstruct	tchars tc = {
X	CINTR, CQUIT, CSTART,
X	CSTOP, CEOF, CBRK,
X};
Xstruct	ltchars ltc = {
X	CSUSP, CDSUSP, CRPRNT,
X	CFLUSH, CWERASE, CLNEXT
X};
X
Xint	crmod;
Xint	upper;
Xint	lower;
Xint	digit;
X
Xchar	hostname[32];
Xchar	name[16];
Xchar	*portselector();
X
X#define	OBUFSIZ		128
X#define	TABBUFSIZ	512
X
Xchar	defent[TABBUFSIZ];
Xchar	defstrs[TABBUFSIZ];
Xchar	tabent[TABBUFSIZ];
Xchar	tabstrs[TABBUFSIZ];
X
Xchar	*env[128];
X
Xchar partab[] = {
X	0001,0201,0201,0001,0201,0001,0001,0201,
X	0202,0004,0003,0205,0005,0206,0201,0001,
X	0201,0001,0001,0201,0001,0201,0201,0001,
X	0001,0201,0201,0001,0201,0001,0001,0201,
X	0200,0000,0000,0200,0000,0200,0200,0000,
X	0000,0200,0200,0000,0200,0000,0000,0200,
X	0000,0200,0200,0000,0200,0000,0000,0200,
X	0200,0000,0000,0200,0000,0200,0200,0000,
X	0200,0000,0000,0200,0000,0200,0200,0000,
X	0000,0200,0200,0000,0200,0000,0000,0200,
X	0000,0200,0200,0000,0200,0000,0000,0200,
X	0200,0000,0000,0200,0000,0200,0200,0000,
X	0000,0200,0200,0000,0200,0000,0000,0200,
X	0200,0000,0000,0200,0000,0200,0200,0000,
X	0200,0000,0000,0200,0000,0200,0200,0000,
X	0000,0200,0200,0000,0200,0000,0000,0201
X};
X
X#define	ERASE	tmode.sg_erase
X#define	KILL	tmode.sg_kill
X#define	EOT	tc.t_eofc
X
Xjmp_buf timeout;
X
Xdingdong()
X{
X
X	alarm(0);
X	signal(SIGALRM, SIG_DFL);
X	longjmp(timeout, 1);
X}
X
Xjmp_buf	intrupt;
X
Xinterrupt()
X{
X
X	signal(SIGINT, interrupt);
X	longjmp(intrupt, 1);
X}
X
Xmain(argc, argv)
X	char *argv[];
X{
X	char *tname;
X	long allflags;
X	extern char *pacx_term;
X	extern char *pacx_line;
X
X	signal(SIGINT, SIG_IGN);
X/*
X	signal(SIGQUIT, SIG_DFL);
X*/
X	gethostname(hostname, sizeof(hostname));
X	if (hostname[0] == '\0')
X		strcpy(hostname, "Amnesiac");
X	gettable("default", defent, defstrs);
X	gendefaults();
X	tname = "default";
X	if (argc > 1)
X		tname = argv[1];
X	for (;;) {
X		int ldisp = OTTYDISC;
X
X		gettable(tname, tabent, tabstrs);
X		if (OPset || EPset || APset)
X			APset++, OPset++, EPset++;
X		setdefaults();
X		ioctl(0, TIOCFLUSH, 0);		/* clear out the crap */
X		if (IS)
X			tmode.sg_ispeed = speed(IS);
X		else if (SP)
X			tmode.sg_ispeed = speed(SP);
X		if (OS)
X			tmode.sg_ospeed = speed(OS);
X		else if (SP)
X			tmode.sg_ospeed = speed(SP);
X		tmode.sg_flags = setflags(0);
X		ioctl(0, TIOCSETP, &tmode);
X		setchars();
X		ioctl(0, TIOCSETC, &tc);
X		ioctl(0, TIOCSETD, &ldisp);
X		if (HC)
X			ioctl(0, TIOCHPCL, 0);
X		if (GP) {
X			extern char *pacx();
X
X			tname = pacx(argv[2]);		/* !!! */
X
X			/* Drop connection if name is null */
X			if (tname == 0)  {
X				tmode.sg_ispeed = tmode.sg_ospeed = 0;
X				ioctl(0, TIOCSETP, &tmode);
X				exit(1);
X			}
X			continue;
X		}
X 		if (AB) {
X 			extern char *autobaud();
X 
X 			tname = autobaud();
X 			continue;
X 		}
X		if (PS) {
X			tname = portselector();
X			continue;
X		}
X		if (CL && *CL)
X			putpad(CL);
X		edithost(HE);
X		if (IM && *IM)
X			putf(IM);
X		if (setjmp(timeout)) {
X			tmode.sg_ispeed = tmode.sg_ospeed = 0;
X			ioctl(0, TIOCSETP, &tmode);
X			exit(1);
X		}
X		if (TO) {
X			signal(SIGALRM, dingdong);
X			alarm(TO);
X		}
X		if (getname()) {
X			alarm(0);
X			signal(SIGALRM, SIG_DFL);
X			if (!(upper || lower || digit))
X				continue;
X			allflags = setflags(2);
X			tmode.sg_flags = allflags & 0xffff;
X			allflags >>= 16;
X			if (crmod || NL)
X				tmode.sg_flags |= CRMOD;
X			if (upper || UC)
X				tmode.sg_flags |= LCASE;
X			if (lower || LC)
X				tmode.sg_flags &= ~LCASE;
X			ioctl(0, TIOCSETP, &tmode);
X			ioctl(0, TIOCSLTC, &ltc);
X			ioctl(0, TIOCLSET, &allflags);
X			putchr('\n');
X			oflush();
X			makeenv(env);
X			signal(SIGINT, SIG_DFL);
X			if( pacx_term != 0 )  {
X				execle(LO, "login", "-t", pacx_term, "-h",
X					pacx_line, name, (char *)0, env);
X			} else {
X				execle(LO, "login", name, (char *)0, env);
X			}
X			exit(1);
X		}
X		alarm(0);
X		signal(SIGALRM, SIG_DFL);
X		signal(SIGINT, SIG_IGN);
X		if (NX && *NX)
X			tname = NX;
X	}
X}
X
Xgetname()
X{
X	register char *np;
X	register c;
X	char cs;
X
X	/*
X	 * Interrupt may happen if we use CBREAK mode
X	 */
X	if (setjmp(intrupt)) {
X		signal(SIGINT, SIG_IGN);
X		return (0);
X	}
X	signal(SIGINT, interrupt);
X	tmode.sg_flags = setflags(0);
X	ioctl(0, TIOCSETP, &tmode);
X	tmode.sg_flags = setflags(1);
X	prompt();
X	if (PF > 0) {
X		oflush();
X		sleep(PF);
X		PF = 0;
X	}
X	ioctl(0, TIOCSETP, &tmode);
X	crmod = 0;
X	upper = 0;
X	lower = 0;
X	digit = 0;
X	np = name;
X	for (;;) {
X		oflush();
X		if (read(0, &cs, 1) <= 0)
X			exit(0);
X		if ((c = cs&0177) == 0)
X			return (0);
X		if (c == EOT)
X			exit(1);
X		if (c == '\r' || c == '\n' || np >= &name[16])
X			break;
X
X		if (c >= 'a' && c <= 'z')
X			lower++;
X		else if (c >= 'A' && c <= 'Z') {
X			upper++;
X		} else if (c == ERASE || c == '#' || c == '\b') {
X			if (np > name) {
X				np--;
X				if (tmode.sg_ospeed >= B1200)
X					puts("\b \b");
X				else
X					putchr(cs);
X			}
X			continue;
X		} else if (c == KILL || c == '@') {
X			putchr(cs);
X			putchr('\r');
X			if (tmode.sg_ospeed < B1200)
X				putchr('\n');
X			/* this is the way they do it down under ... */
X			else if (np > name)
X				puts("                                     \r");
X			prompt();
X			np = name;
X			continue;
X		} else if (c == ' ')
X			c = '_';
X		else if (c >= '0' && c <= '9')
X			digit++;
X		if (IG && (c < ' ' || c > 0176))
X			continue;
X		*np++ = c;
X		putchr(cs);
X	}
X	signal(SIGINT, SIG_IGN);
X	*np = 0;
X	if (c == '\r')
X		crmod++;
X	if (upper && !lower && !LC || UC)
X		for (np = name; *np; np++)
X			if (isupper(*np))
X				*np = tolower(*np);
X	return (1);
X}
X
Xstatic
Xshort	tmspc10[] = {
X	0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5, 15
X};
X
Xputpad(s)
X	register char *s;
X{
X	register pad = 0;
X	register mspc10;
X
X	if (isdigit(*s)) {
X		while (isdigit(*s)) {
X			pad *= 10;
X			pad += *s++ - '0';
X		}
X		pad *= 10;
X		if (*s == '.' && isdigit(s[1])) {
X			pad += s[1] - '0';
X			s += 2;
X		}
X	}
X
X	puts(s);
X	/*
X	 * If no delay needed, or output speed is
X	 * not comprehensible, then don't try to delay.
X	 */
X	if (pad == 0)
X		return;
X	if (tmode.sg_ospeed <= 0 ||
X	    tmode.sg_ospeed >= (sizeof tmspc10 / sizeof tmspc10[0]))
X		return;
X
X	/*
X	 * Round up by a half a character frame,
X	 * and then do the delay.
X	 * Too bad there are no user program accessible programmed delays.
X	 * Transmitting pad characters slows many
X	 * terminals down and also loads the system.
X	 */
X	mspc10 = tmspc10[tmode.sg_ospeed];
X	pad += mspc10 / 2;
X	for (pad /= mspc10; pad > 0; pad--)
X		putchr(*PC);
X}
X
Xputs(s)
X	register char *s;
X{
X
X	while (*s)
X		putchr(*s++);
X}
X
Xchar	outbuf[OBUFSIZ];
Xint	obufcnt = 0;
X
Xputchr(cc)
X{
X	char c;
X
X	c = cc;
X	c |= partab[c&0177] & 0200;
X	if (OP)
X		c ^= 0200;
X	if (!UB) {
X		outbuf[obufcnt++] = c;
X		if (obufcnt >= OBUFSIZ)
X			oflush();
X	} else
X		write(1, &c, 1);
X}
X
Xoflush()
X{
X	if (obufcnt)
X		write(1, outbuf, obufcnt);
X	obufcnt = 0;
X}
X
Xprompt()
X{
X
X	putf(LM);
X	if (CO)
X		putchr('\n');
X}
X
Xputf(cp)
X	register char *cp;
X{
X	extern char editedhost[];
X
X	while (*cp) {
X		if (*cp != '%') {
X			putchr(*cp++);
X			continue;
X		}
X		switch (*++cp) {
X
X		case 'h':
X			puts(editedhost);
X			break;
X
X		case '%':
X			putchr('%');
X			break;
X		}
X		cp++;
X	}
X}
________This_Is_The_END________
echo 'sh - pacxmon.8b'
sed 's/^X//' <<'________This_Is_The_END________' >>pacxmon.8b
X.TH PACXMON 8 "2 Mar 1984"
X.UC 4
X.SH NAME
Xpacxmon \- manage Gandalf PACX port selector for getty
X.SH SYNOPSIS
X.B /usr/brl/etc/pacxmon
X.SH DESCRIPTION
X.I Pacxmon
Xlistens to the PACX statistics port
Xand maintains a complete picture of the status of all active connections
Xthrough the PACX at any time.
X.I Pacxmon
Xmay be queried at any time (typically by \fIgetty(8)\fR)
Xabout the status of a given connection.
X.I Getty
Xuses this procedure to automatically determine the speed
Xof an incoming connection, and to discover the location and
Xterminal type (for $TERM) of the terminal making the connection.
X.SH "/etc/pacxports1 FORMAT
XEach entry in this file describes a connection between a PACX port and a
XVAX computer port.  The format is:
X.DS
X.nf
X\fIportnum\fR:\fIVAX port\fR
X.fi
X.DE
Xwhere \fIportnum\fR is the number of the PACX port, expressed
Xas 4 digits, with leading zeros, and
X\fIVAX port\fR is the last component of the
Xterminal line name in /dev.  Example:
X.nf
X0320:ttyh0
X0321:ttyh1
X0322:ttyh2
X.fi
X.SH "/etc/pacxlines1 FORMAT
XEach entry in this file contains information about a terminal \fIline\fR
Xconnected to the PACX port selector.
XThe first character of each entry indicates the enabled/disabled
Xstatus of the PACX \fIline\fR.  A SPACE indicates the \fIline\fR is enabled,
Xand a DASH (minus) indicates a comment.  Format is:
X.DS
X.nf
X\fIline:linenum:TERM:Class:Rate:Initial STTY modes:Location:Voice Phone\fR
X.fi
X.PP
X\fILinenum\fR is the number of the PACX line that the terminal
Xis connected to, \fITERM\fR is the terminal type (in \fI/etc/termcap\fR),
X\fIClass\fR is the terminal class (General, Modem, Restricted, etc),
Xand presently has no effect,
X\fIRate\fR is the charge rate (in units of $0.01/hour), and presently
Xhas no effect,
X\fIInitial STTY modes\fR describe desired deviations from
Xthe standard initial terminal modes,
X\fILocation\fR is a short string giving information about
Xthe physical location of the terminal, and
X\fIVoice Phone\fR is a phone number or extension number near the terminal.
XFor example:
X.nf
X line:702:vis200:General:100:scope tabs:BMD (394) 231:x6678
X\-line:703:vis200:General:100:scope tabs:VLD (394) 117:x6640 Not On Yet!
X.fi
X.SH "/etc/ttys FORMAT
XAll ports under control of
X.I pacxmon
Xmust be listed in /etc/ttys with package ``P'', to trigger the
Xappropriate behavior in
X.I getty
X(via the /etc/gettytab P (PACX) entry of ``:gp:'' to ``get PACX package'').
X.SH FILES
X.nf
X/etc/pacxlines1	description of terminal lines to PACX mapping
X/etc/pacxports1	description of PACX ports to VAX port mapping
X/etc/ttys	enable specific VAX ports for pacxmon control
X/dev/pacxstats	symbolic link to /dev/tty?? from PACX Statistics port
X.fi
X.SH AUTHOR
XRobert S. Miles
X.SH SEE ALSO
Xgetty(8),
Xgettytab(5)
X.SH BUGS
XThe format of the communications between \fIpacxmon\fR and \fIgetty\fR
Xis obscure, and documented only in the source code.
X.PP
XThe format of pacxlines1 and pacxports1 is too complex.  Either
Xall the promised features should be implemented, or the file format
Xshould be simplified.
X.PP
XIf the connection to the PACX Statistics port is not working, or
Xthe modem control on that VAX port is wrong, nothing happens.
X.PP
XThis whole interface to a port selector is very powerful, but
Xexcessively complex, and somewhat fragile.
X.PP
XThere is a compile-time flag (\fIdebug\fR) which will cause
X\fIpacxmon\fR to output a log of all the messages the PACX
Xsends, plus diagnostic information about TCP connections
Xwhich are made.  Consult the source code.
________This_Is_The_END________
echo 'sh - pacxmon.c'
sed 's/^X//' <<'________This_Is_The_END________' >>pacxmon.c
X/*
X *			P A C X M O N . C
X *
X * $Revision: 1.4 $
X *
X *
X *	PACX Software Design:  R. N. Jesse, M. S. Baldwin at Hopkins ECF
X *			       M. J. Muuss, R. S. Miles, D. P. Kingston
X *			       at US Army BRL/BMD
X *
X *	Original implementation:  6-Sept-81 by   Michael S. Baldwin
X *
X *
X *		R E V I S I O N   H I S T O R Y
X *
X *	30 Sep 81 DPK	Pacxmon now re-execs itself inorder to re-read
X *			the /etc/ttys file.  The child inherits the pacxstats
X *			file-descriptor so no pacxstats messages are lost.
X *
X *	10/27/81  MJM	Modified so that garbage on the PACX Statistics Port
X *			is less damaging.
X *
X *	12/03/81  MJM	Modified in an attempt to isolate the recurring
X *			cause of "untimely death of PACXMON" problems.
X *
X *	12/31/81  RNJ	Now ignores several messages which occur during
X *			normal operation.
X *
X *	03/29/82  RSM	Use setjmp/longjmp instead of setexit/reset.
X *
X *	12/01/82  DPK	Upped number of lines (LTABSZ) from 100 to 200
X *			and added checking of return from sbrk().
X *
X *	04/19/83  RSM	LTABSZ now max (for single PACX), 256.  BLST messages
X *			are now ignored.  Ingore hangup signals too.
X *
X *	05/20/83  RSM	First version for 4.1c BSD.
X *
X *	12/21/83  RSM	This version currently under revision for 4.2 BSD....
X */
X#ifndef lint
Xstatic char RCSid[] = "@(#)$Header: pacxmon.c,v 1.4 84/08/07 23:25:33 dpk BRL $ (BRL)";
X#endif
X
X#include <sys/types.h>
X#include <sys/file.h>
X#include <sys/socket.h>
X#include <sys/stat.h>
X#include <sys/time.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <ctype.h>
X#include <sgtty.h>
X#include <signal.h>
X#include <stdio.h>
X
X#define rselect(x)	select(20, x, (int *)0, (int *)0, (struct timeval *)0)
X
X/* port, line and tty table sizes */
X#define	PTABSZ	00400			/* Wastefull */
X#define	LTABSZ	01000			/* Wastefull */
X#define TTABSZ	128
X
X/* Table of PACX port mappings (from /etc/pacxports) */
Xstruct ptab {
X	int	p_line;		/* Line # currently connected */
X	char	p_tty[14];	/* Terminal name */	
X} ptab[PTABSZ];
X
X/* Table of PACX line attributes (from /etc/pacxlines) */
Xstruct ltab {
X	int	l_port;		/* Port # currently connected */
X	int	l_rate;		/* Baud rate at time of connection */
X	time_t	l_time;		/* UNIX Time of connection */
X	char	l_type[18];	/* TERM type */
X	char	l_place[18];	/* Location of terminal */
X} ltab[LTABSZ];
X
X/* Table of PACX terminal names (from /etc/ttys) */
Xstruct ttab {
X	char	t_tty[14];
X} ttab[TTABSZ], *Ettab;
X
X/*** BAUD RATE OF STATISTICS PORT SHOULD BE A PROGRAM OPTION ***/
Xstruct sgttyb pacxstty = { B9600, B9600, 0377, 0377, ANYP };
X
X/*
X * "bauds" maps PACX baud rates to "UNIX" baud rates by indexing
X * the following table by the PACX baud rate code.  Note that
X * currently the PACX does not support 19.2.
X */
Xstruct pbauds {
X	int	b_ubaud;
X	char	*b_sbaud;
X} bauds[] = {
X
X/*	UNIX meaning		   PACX meaning */
X
X	B50,	"50",		/* B00 -> 50 bps */
X	B75,	"75",		/* B01 -> 75 bps */
X	B110,	"110",		/* B02 -> 110 bps */
X	B134,	"134.5",	/* B03 -> 134.5 bps */
X	B150,	"150",		/* B04 -> 150 bps */
X	B300,	"300",		/* B05 -> 300 bps */
X	B600,	"600",		/* B06 -> 600 bps */
X	B1200,	"1200",		/* B07 -> 1200 bps */
X	B1800,	"1800",		/* B08 -> 1800 bps */
X	B0,	"2000",		/* B09 -> 2000 bps */
X	B2400,	"2400",		/* B10 -> 2400 bps */
X	B0,	"3600",		/* B11 -> 3600 bps */
X	B4800,	"4800",		/* B12 -> 4800 bps */
X	B0,	"7200",		/* B13 -> 7200 bps */
X	B9600,	"9600",		/* B14 -> 9600 bps */
X	B200,	"200",		/* B15 -> 200 bps */
X};
X
X/* PACX errors */
Xchar *pacxerr[] = {
X	"Power on",
X	"Failure of Sanity Timer",
X	"Low battery voltage",
X	"Crosspoint memory timeout error",
X	"Two ports being simultaneously monitored",
X	"Port Address Register not cycling",
X	"Comparator error",
X	0,
X	"Statistics List overflow",
X	0,
X	"Failure of USART number 1",
X	"Failure of USART number 2",
X	"Failure of USART number 3",
X	"Failure of USART number 4",
X	"Failure of USART number 5",
X	"Failure of USART number 6",
X	0,
X	0,
X	0,
X	0,
X	"State Sequencer in test mode",
X	"State Sequencer sequence error",
X	"State Sequencer deglitch timer run error"
X};
X
Xchar	statmess[50];		/* Place for the statistics output */
X
Xchar	*pacxfile = "/dev/pacxstats";
Xchar	*ttysfile = "/etc/ttys";
Xchar	*portfile = "/etc/pacxports1";
Xchar	*linefile = "/etc/pacxlines1";
X
Xstruct sockaddr_in sin = { AF_INET };
Xint	debug = 0;
X
Xextern char *index();
Xextern char *malloc();
Xextern char *strncpy();
Xextern FILE *fdopen();
Xextern struct servent *getservbyname();
X
Xmain( argc, argv )
Xint	argc;
Xchar	**argv;
X{
X	register int i;
X	struct servent *servp;
X	int	pacxfd;
X	int	statso;
X
X	for (i = 1; i < argc; i++) {
X		if (strcmp (argv[i], "-d") == 0)
X			debug++;
X		else {
X			fprintf(stderr, "Usage: %s [-d]\n", argv[0]);
X			exit(1);
X		}
X	}
X
X	/* Ignore nasty signals */
X	(void) signal(SIGHUP, SIG_IGN);
X	(void) signal(SIGINT, SIG_IGN);
X	(void) signal(SIGQUIT, SIG_IGN);
X	(void) signal(SIGPIPE, SIG_IGN);
X	(void) signal(SIGTERM, SIG_IGN);
X	(void) signal(SIGTSTP, SIG_IGN);
X
X	/* Device names are all relative to /dev */
X	if( chdir("/dev") < 0 )
X		die("Can't chdir to /dev");
X
X	/* Open pacx statistics port exclusively */
X	if( (pacxfd = open(pacxfile, O_RDONLY|FEXLOCK|FNDELAY)) < 0 )
X		die(pacxfile);
X
X	/* Set characteristics of line */
X	if( stty(pacxfd, &pacxstty) < 0 )
X		die("Can't stty statistics line");
X
X	(void) nice(-5);		/* Give ourselves good priority */
X	(void) setpgrp(0, getpid());	/* Our own process group */
X
X	if(debug)  {
X		time_t	tnow;
X		extern char *ctime();
X
X		(void) time(&tnow);
X		printf("PACXMON (pid %d) started %s", getpid(), ctime(&tnow));
X		(void) fflush(stdout);
X	}
X
X	/* Initialize connection tables */
X	for(i=0; i<LTABSZ; i++)
X		ltab[i].l_port = -1;
X	for(i=0; i<PTABSZ; i++)
X		ptab[i].p_line = -1;
X
X	/*****************************************/
X	/* We don't use UNIX domain IPC since it */
X	/* still doesn't work right in 4.2 BSD!  */
X	/*****************************************/
X
X	if ((servp = getservbyname("ttyinfo", "tcp")) == NULL)
X		die("getservbyname (ttyinfo/tcp) failed");
X
X	/* Settup ability to receive status inquiries */
X	if( (statso = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
X		die("inet socket");
X
X	sin.sin_port = servp->s_port;
X	if( bind(statso, (struct sockaddr *)&sin, sizeof(sin)) == -1 )
X		die("inet bind");
X
X	if( listen(statso, 3) == -1 )
X		die("inet listen");
X
X	/*
X	 * Loop forever waiting for status
X	 * inquiries or PACX messages.
X	 */
X	for(;;)  {
X		int	readfds;
X
X		readfds = (1<<pacxfd) | (1<<statso);
X		if( rselect(&readfds) == -1 )
X			die("select");
X
X		if(readfds & (1<<pacxfd))
X			pacx(pacxfd);
X		if(readfds & (1<<statso))
X			status(statso);
X		(void) fflush(stdout);
X	}
X}
X
X/*
X *			P A C X
X *
X * Process a message from the PACX statistics output.
X */
Xpacx(fd)
Xint fd;
X{
X	int	term;
X	int	port;
X	int	rate;
X	int	nread;
X
X	/* Get the message from the PACX */
X	bzero(statmess, sizeof statmess);
X	nread = read(fd, statmess, sizeof statmess);
X
X	/* Check for error */
X	if( nread < 0 )
X		die("Read error on statistics line");
X
X	/* Check for end of file */
X	if( nread == 0 )
X		die("EOF from statistics line");
X
X	if(debug)
X		(void) write(1, statmess, nread);
X
X	/* Check minimum length */
X	if( nread < 15 )  {
X		err_stat("Short message");
X		return;
X	}
X
X	/* Must start with "HH:MM:SS" */
X	if(
X		! isdigit(statmess[0])	||
X		! isdigit(statmess[1])	||
X		statmess[2] != ':'	||
X		! isdigit(statmess[3])	||
X		! isdigit(statmess[4])	||
X		statmess[5] != ':'	||
X		! isdigit(statmess[6])	||
X		! isdigit(statmess[7])
X	)  {
X		err_stat("Bad format message");
X		return;
X	}
X
X	/* Oh goodie!  Its a connection message of some sort! */
X	if( !strncmp(&statmess[10], "CON", 3) )  {
X
X		/* Update our mapping tables (if needed) */
X		get_ttys();	/* Enabled terminal descriptions */
X		get_ports();	/* Port to Terminal mappings */
X		get_lines();	/* Line descriptions */
X
X		/* See if this is a reasonable length */
X		if( nread < 32 )  {
X			err_stat("Short CON message");
X			return;
X		}
X
X		/* Valid terminal (line), class, and port markers? */
X		if(
X			statmess[14] != 'T' ||
X			statmess[20] != 'C' ||
X			statmess[25] != 'P'
X		) {
X			err_stat("Bad CON message");
X			return;
X		}
X
X		/* Terminal (line) number */
X		term = atoo(&statmess[15]);
X		if( term >= LTABSZ )  {
X			err_stat("Bad Term");
X			return;
X		}
X
X		/* Port number */
X		port = atoo(&statmess[26]);
X		if( port >= PTABSZ )  {
X			err_stat("Bad Port");
X			return;
X		}
X
X		/* Valid baud rate marker? */
X		if( statmess[35] == 'B' )  {
X			rate = atoi(&statmess[36]);
X
X			if( rate > 15 )  {
X				err_stat("Bad Term/Port/Rate");
X				return;
X			}
X		} else {
X		 	/* No baud rate was specified */
X			rate = -1;
X		}
X
X		/* Fill in connection information */
X		(void) time(&ltab[term].l_time);
X		ltab[term].l_port = port;
X		ltab[term].l_rate = rate;
X		ptab[port].p_line = term;
X		return;
X	}
X
X	/* Looks like one of a variety of disconnect messages? */
X	if( !strncmp(&statmess[10], "DIS",  3) )  {
X
X		/* See if this is a reasonable length */
X		if( nread < 32 )  {
X			err_stat("Short DIS message");
X			return;
X		}
X
X		/* Valid terminal (line), class, and port markers? */
X		if(
X			statmess[14] != 'T' ||
X			statmess[20] != 'C' ||
X			statmess[25] != 'P'
X		) {
X			err_stat("Bad DIS message");
X			return;
X		}
X
X		/* Terminal (line) number */
X		term = atoo(&statmess[15]);
X		if( term >= LTABSZ )  {
X			err_stat("Bad Term");
X			return;
X		}
X
X		/* Port number */
X		port = atoo(&statmess[26]);
X		if( port >= 04000 )		/* Queued or USART */
X			return;
X		if( port >= PTABSZ )  {
X			err_stat("Bad Port");
X			return;
X		}
X
X		/* Flag as not connected any longer */
X		ltab[term].l_port = -1;
X		ptab[port].p_line = -1;
X		return;
X	}
X
X	/* Looks like a status message? */
X	if( !strncmp(&statmess[9], "STAT", 4) )  {
X		register int i;
X
X		i = atoi( &statmess[14] );
X		if(
X			i < 0  ||
X			i >= sizeof pacxerr / sizeof(char *)  ||
X			pacxerr[i] == 0
X		)  {
X			fprintf(stderr,"PACX: unknown status %d\n",i);
X			(void) fflush(stderr);
X		} else {
X			fprintf(stderr, "PACX: %s\n", pacxerr[i] );
X			(void) fflush(stderr);
X		}
X		return;
X	}
X
X	/* Valid, but uninteresting messages? */
X	if( 
X	    !strncmp(&statmess[9],  "REST", 4) ||
X	    !strncmp(&statmess[9],  "QTRM", 4) ||
X	    !strncmp(&statmess[9],  "BUSY", 4) ||
X	    !strncmp(&statmess[9],  "UNAS", 4) ||
X	    !strncmp(&statmess[9],  "BLST", 4) ||
X	    !strncmp(&statmess[9],  "UNAV", 4)
X	)  {
X		return;			/* Ingnore */
X	}
X
X	/* If we get here, the message is not understood */
X	fprintf(stderr, "PACX: %s\n", statmess );
X	(void) fflush(stderr);
X}
X
X/*
X *			S T A T U S
X *
X * This cruft is NOT done yet.....
X */
Xstatus(so)
Xint so;
X{
X	static struct sockaddr_in sock;
X	static char ibuf[sizeof(ptab[0].p_tty)];
X	int	slen = sizeof(struct sockaddr_in);
X	register struct ptab *pp;
X	unsigned p;
X	unsigned l;
X	int	ns;
X	int	nread;
X	FILE	*fp;
X	extern char *ctime();
X
X	static struct {
X		char	baud[6];
X		char	term[14];
X		int	port;
X		int	line;
X		time_t	time;
X	} resp;
X
X	bzero(ibuf, sizeof(ibuf));
X	if( (ns = accept(so, (struct sockaddr *)&sock, &slen)) == -1 )  {
X		/* Want to hear about failures? */
X		perror("PACXMON:  accept");
X		return;
X	}
X
X	if(debug)  {
X		printf("PACXMON:  Connection on socket %d from %#X port %d\n",
X			ns, ntohl(sock.sin_addr.s_addr), ntohs(sock.sin_port));
X		(void) fflush(stdout);
X	}
X
X	/* Validate host address?  -- For now don't bother */
X
X	/* Get information request */
X	if( (nread = read(ns, ibuf, sizeof ibuf)) == -1 )  {
X		perror("PACXMON:  socket read");
X		goto out;
X	}
X
X	/* Anything usefull there? */
X	if( nread <= 1 )
X		goto out;
X
X	switch( *ibuf )  {
X
X	case 'L':			/* Line request */
X	case 'T':
X		l = atoo(&ibuf[1]);
X		if(l >= LTABSZ)
X			goto out;
X		p = ltab[l].l_port;
X		break;
X
X	case 'P':			/* Port request */
X		p = atoo(&ibuf[1]);
X		if(p >= PTABSZ)
X			goto out;
X		l = ptab[p].p_line;
X		break;
X
X	default:			/* Terminal name request */
X		l = p = -1;
X		for(pp = &ptab[0]; pp < &ptab[PTABSZ]; pp++)  {
X			if( ! strcmp(pp->p_tty, ibuf) )  {
X				p = pp - &ptab[0];
X				l = pp->p_line;
X				break;
X			}
X		}
X		break;
X	}
X
X	/* If not connected, don't say anything */
X	if( ltab[l].l_port == -1 || ptab[p].p_line == -1 )
X		goto out;
X
X	strncpy(resp.baud, bauds[ltab[l].l_rate].b_sbaud, sizeof resp.baud);
X	strncpy(resp.term, ltab[l].l_type, sizeof resp.term);
X	resp.port = p;
X	resp.line = l;
X	resp.time = ltab[l].l_time;
X	if( write(ns, (char *)&resp, sizeof resp) != sizeof resp )
X		perror("PACXMON:  socket write");
Xout:
X	(void) close(ns);
X	return;
X}
X
X/*
X *			E R R _ S T A T
X *
X * Report error generated by bogus data from statistics port.
X */
Xerr_stat(mess)
Xregister char *mess;
X{
X	fprintf(stderr, "PACX: %s: %s\n", mess, statmess);
X	(void) fflush(stderr);
X}
X
X/*
X *			E R R _ A D V I S E
X *
X * Advise everyone of a non-fatal internal error.
X */
Xerr_advise(mess)
Xregister char *mess;
X{
X	fprintf(stderr, "PACXMON: %s\n", mess);
X	(void) fflush(stderr);
X}
X
X/*
X *			E R R _ L I N E N O
X *
X * Voice the opinion that something is wrong with a particular
X * line in one of our data files.
X */
Xerr_lineno(mess, file, line)
Xregister char *mess;
Xregister char *file;
Xregister int line;
X{
X	fprintf(stderr, "PACXMON: %s: line %d: %s\n", file, line, mess);
X	(void) fflush(stderr);
X}
X
X/*
X *			D I E
X *
X * Some kind of error that we can't recover from has happened.
X * Report about it and then "give up the ship."
X */
Xdie(mess)
Xchar *mess;
X{
X	extern int errno;
X	int saverrno;
X
X	saverrno = errno;
X	fprintf(stderr, "PACXMON: ");
X	(void) fflush(stderr);
X	errno = saverrno;
X	perror(mess);
X	(void) fflush(stderr);
X	fprintf(stderr, "PACXMON: exitting\n");
X	(void) fflush(stderr);
X	exit(1);
X}
X
X/*
X *			A T O O
X *
X * Convert an ascii string to an octal number.
X */
Xatoo(s)
Xregister char *s;
X{
X	register int n = 0;
X
X	while( isdigit(*s) )
X		n = n*8 + *s++ - '0';
X
X	return(n);
X}
X
X/*
X *			G E T _ T T Y S
X *
X * This builds a table of all the enabled terminal names
X * that claim to be cabled to PACX ports.
X */
Xget_ttys()
X{
X	static time_t modtime = 0;
X	register int lineno;
X	register char *cp;
X	struct stat statb;
X	char	lbuf[30];
X	int	c;
X	FILE	*fp;
X
X	if(stat(ttysfile, &statb) < 0)
X		die("Can't stat ttys file");
X
X	if(statb.st_mtime == modtime)
X		return;
X
X	modtime = statb.st_mtime;
X	if( (fp = fopen(ttysfile, "r")) == NULL )
X		die("Can't open ttys file");
X
X	lineno = 0;
X	Ettab = &ttab[0];
X	while( fgets(lbuf, sizeof lbuf, fp) != NULL )  {
X
X		lineno++;
X
X		/* Check for table overrun */
X		if(Ettab >= &ttab[TTABSZ])  {
X			err_advise("Terminal table overflow");
X			break;
X		}
X
X		/* Terminal must be enabled */
X		if(lbuf[0] != '1')
X			continue;
X
X		/* Terminal should be PACX type ('P') */
X		if(lbuf[1] != 'P')
X			continue;
X
X		/* Find the end of the line... */
X		if( (cp = index(lbuf, '\n')) == 0 )  {
X
X			/* Line was longer than expected.  Gobble rest. */
X			while( (c = getc(fp)) != EOF )  {
X				if(c == '\n')
X					break;
X			}
X			err_lineno("too long", ttysfile, lineno);
X			continue;
X		}
X		*cp = 0;
X
X		/* OK.  Copy into our table */
X		(void) strncpy(Ettab->t_tty, &lbuf[2], sizeof Ettab->t_tty);
X		Ettab++;
X	}
X
X	(void) fclose(fp);
X}
X
X/*
X *			G E T _ P O R T S
X *
X * This builds a mapping of PACX port numbers to terminal names.
X */
Xget_ports()
X{
X	static time_t modtime = 0;
X	register struct ttab *tp;
X	register int lineno;
X	register char *cp;
X	struct stat statb;
X	unsigned u;
X	char	lbuf[30];
X	int	c;
X	FILE	*fp;
X
X	if(stat(portfile, &statb) < 0)
X		die("Can't stat port file");
X
X	if(statb.st_mtime == modtime)
X		return;
X
X	modtime = statb.st_mtime;
X	if( (fp = fopen(portfile, "r")) == NULL )
X		die("Can't open port file");
X
X	lineno = 0;
X	while( fgets(lbuf, sizeof lbuf, fp) != NULL )  {
X
X		lineno++;
X
X		/* Find the end of the line... */
X		if( (cp = index(lbuf, '\n')) == 0 )  {
X
X			/* Line was longer than expected.  Gobble rest. */
X			while( (c = getc(fp)) != EOF )  {
X				if(c == '\n')
X					break;
X			}
X			err_lineno("too long", portfile, lineno);
X			continue;
X		}
X		*cp = 0;
X
X		/* Find the colon field separator */
X		if( (cp = index(lbuf, ':')) == 0 )  {
X			err_lineno("no colon", portfile, lineno);
X			continue;
X		}
X		cp++;
X
X		/* Make sure this terminal line is enabled. */
X		for(tp = ttab; tp < Ettab; tp++)  {
X
X			if( strncmp(tp->t_tty, cp, sizeof tp->t_tty) )
X				continue;
X
X			u = atoo(lbuf);
X			if( u >= PTABSZ )  {
X				err_lineno("bad port num", portfile, lineno);
X				break;
X			}
X
X			/* Copy terminal name (null terminated) */
X			(void) strncpy(ptab[u].p_tty,
X				cp, sizeof(ptab[u].p_tty)-1);
X		}
X	}
X
X	(void) fclose(fp);
X}
X
X/*
X *			G E T _ L I N E S
X *
X * This builds a mapping of PACX line numbers to TERM types.
X */
Xget_lines()
X{
X	static time_t modtime = 0;
X	register int lineno;
X	register char *cp;
X	struct stat statb;
X	unsigned u;
X	char	lbuf[200];
X	int	c;
X	FILE	*fp;
X
X	if(stat(linefile, &statb) < 0)
X		die("Can't stat line file");
X
X	if(statb.st_mtime == modtime)
X		return;
X
X	modtime = statb.st_mtime;
X	if( (fp = fopen(linefile, "r")) == NULL )
X		die("Can't open line file");
X
X	lineno = 0;
X	while( fgets(lbuf, sizeof lbuf, fp) != NULL )  {
X
X		lineno++;
X
X		/* Find the end of the line... */
X		if( (cp = index(lbuf, '\n')) == 0 )  {
X
X			/* Line was longer than expected.  Gobble rest. */
X			while( (c = getc(fp)) != EOF )  {
X				if(c == '\n')
X					break;
X			}
X			err_lineno("too long", linefile, lineno);
X			continue;
X		}
X
X		/* For now, skip ALL but valid "line" descriptions. */
X		if( strncmp(lbuf, " line:", 6) )
X			continue;
X
X		u = atoo(&lbuf[6]);
X		if(u >= LTABSZ)  {
X			err_lineno("bad line num", linefile, lineno);
X			continue;
X		}
X		if( (cp = index(&lbuf[6], ':')) == NULL )  {
X			err_lineno("no colon", linefile, lineno);
X			continue;
X		}
X		cp++;
X
X		/* Copy TERM type (null terminated) */
X		(void) strncpy(ltab[u].l_type, cp, sizeof(ltab[u].l_type)-1);
X		if( cp = index(ltab[u].l_type, ':') )
X			*cp = 0;
X	}
X
X	(void) fclose(fp);
X}
________This_Is_The_END________
echo 'sh - subr.c'
sed 's/^X//' <<'________This_Is_The_END________' >>subr.c
X/*
X *			S U B R . C 
X *
X * $Revision: 1.7 $
X *
X * $Log:	subr.c,v $
X * Revision 1.7  84/08/19  02:10:27  dpk
X * Minor change to the printf format for "line xxx", now forces 3 digits.
X * 
X * Revision 1.6  84/08/09  01:47:33  dpk
X * Modified getty to pass line number as hostname to login
X * 
X * Revision 1.5  84/08/07  23:46:26  dpk
X * changed to use getservbyname to find TCP port
X * for PACX info.
X * 
X * Revision 1.4  84/02/23  22:03:09  dpk
X * Changed '\0377' to '\377'.
X * 
X * Revision 1.3  84/02/23  21:45:13  rsm
X * Various changes including pacx support and autobauding
X * 
X * Revision 1.2  83/12/16  01:56:01  rsm
X * Added distinctive RCS header
X * 
X */
X#ifndef lint
Xstatic char RCSid[] = "@(#)$Header: subr.c,v 1.7 84/08/19 02:10:27 dpk BRL $";
X#endif
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)subr.c	4.2 (Berkeley) 83/07/07";
X#endif
X
X/*
X * Melbourne getty.
X */
X#include <sgtty.h>
X#include "gettytab.h"
X
Xextern	struct sgttyb tmode;
Xextern	struct tchars tc;
Xextern	struct ltchars ltc;
X
X/*
X * Get a table entry.
X */
Xgettable(name, buf, area)
X	char *name, *buf, *area;
X{
X	register struct gettystrs *sp;
X	register struct gettynums *np;
X	register struct gettyflags *fp;
X	register n;
X
X	hopcount = 0;		/* new lookup, start fresh */
X	if (getent(buf, name) != 1)
X		return;
X
X	for (sp = gettystrs; sp->field; sp++)
X		sp->value = getstr(sp->field, &area);
X	for (np = gettynums; np->field; np++) {
X		n = getnum(np->field);
X		if (n == -1)
X			np->set = 0;
X		else {
X			np->set = 1;
X			np->value = n;
X		}
X	}
X	for (fp = gettyflags; fp->field; fp++) {
X		n = getflag(fp->field);
X		if (n == -1)
X			fp->set = 0;
X		else {
X			fp->set = 1;
X			fp->value = n ^ fp->invrt;
X		}
X	}
X}
X
Xgendefaults()
X{
X	register struct gettystrs *sp;
X	register struct gettynums *np;
X	register struct gettyflags *fp;
X
X	for (sp = gettystrs; sp->field; sp++)
X		if (sp->value)
X			sp->defalt = sp->value;
X	for (np = gettynums; np->field; np++)
X		if (np->set)
X			np->defalt = np->value;
X	for (fp = gettyflags; fp->field; fp++)
X		if (fp->set)
X			fp->defalt = fp->value;
X		else
X			fp->defalt = fp->invrt;
X}
X
Xsetdefaults()
X{
X	register struct gettystrs *sp;
X	register struct gettynums *np;
X	register struct gettyflags *fp;
X
X	for (sp = gettystrs; sp->field; sp++)
X		if (!sp->value)
X			sp->value = sp->defalt;
X	for (np = gettynums; np->field; np++)
X		if (!np->set)
X			np->value = np->defalt;
X	for (fp = gettyflags; fp->field; fp++)
X		if (!fp->set)
X			fp->value = fp->defalt;
X}
X
Xstatic char **
Xcharnames[] = {
X	&ER, &KL, &IN, &QU, &XN, &XF, &ET, &BK,
X	&SU, &DS, &RP, &FL, &WE, &LN, 0
X};
X
Xstatic char *
Xcharvars[] = {
X	&tmode.sg_erase, &tmode.sg_kill, &tc.t_intrc,
X	&tc.t_quitc, &tc.t_startc, &tc.t_stopc,
X	&tc.t_eofc, &tc.t_brkc, &ltc.t_suspc,
X	&ltc.t_dsuspc, &ltc.t_rprntc, &ltc.t_flushc,
X	&ltc.t_werasc, &ltc.t_lnextc, 0
X};
X
Xsetchars()
X{
X	register int i;
X	register char *p;
X
X	for (i = 0; charnames[i]; i++) {
X		p = *charnames[i];
X		if (p && *p)
X			*charvars[i] = *p;
X		else
X			*charvars[i] = '\377';
X	}
X}
X
Xlong
Xsetflags(n)
X{
X	register long f;
X
X	switch (n) {
X	case 0:
X		if (F0set)
X			return(F0);
X		break;
X	case 1:
X		if (F1set)
X			return(F1);
X		break;
X	default:
X		if (F2set)
X			return(F2);
X		break;
X	}
X
X	f = 0;
X
X	if (AP)
X		f |= ANYP;
X	else if (OP)
X		f |= ODDP;
X	else if (EP)
X		f |= EVENP;
X
X	if (UC)
X		f |= LCASE;
X
X	if (NL)
X		f |= CRMOD;
X
X	f |= delaybits();
X
X	if (n == 1) {		/* read mode flags */
X		if (RW)
X			f |= RAW;
X		else
X			f |= CBREAK;
X		return (f);
X	}
X
X	if (!HT)
X		f |= XTABS;
X
X	if (n == 0)
X		return (f);
X
X	if (CB)
X		f |= CRTBS;
X
X	if (CE)
X		f |= CRTERA;
X
X	if (PE)
X		f |= PRTERA;
X
X	if (EC)
X		f |= ECHO;
X
X	if (XC)
X		f |= CTLECH;
X
X	return (f);
X}
X
Xstruct delayval {
X	unsigned	delay;		/* delay in ms */
X	int		bits;
X};
X
X/*
X * below are random guesses, I can't be bothered checking
X */
X
Xstruct delayval	crdelay[] = {
X	1,		CR1,
X	2,		CR2,
X	3,		CR3,
X	83,		CR1,
X	166,		CR2,
X	0,		CR3,
X};
X
Xstruct delayval nldelay[] = {
X	1,		NL1,		/* special, calculated */
X	2,		NL2,
X	3,		NL3,
X	100,		NL2,
X	0,		NL3,
X};
X
Xstruct delayval	bsdelay[] = {
X	1,		BS1,
X	0,		0,
X};
X
Xstruct delayval	ffdelay[] = {
X	1,		FF1,
X	1750,		FF1,
X	0,		FF1,
X};
X
Xstruct delayval	tbdelay[] = {
X	1,		TAB1,
X	2,		TAB2,
X	3,		XTABS,		/* this is expand tabs */
X	100,		TAB1,
X	0,		TAB2,
X};
X
Xdelaybits()
X{
X	register f;
X
X	f  = adelay(CD, crdelay);
X	f |= adelay(ND, nldelay);
X	f |= adelay(FD, ffdelay);
X	f |= adelay(TD, tbdelay);
X	f |= adelay(BD, bsdelay);
X	return (f);
X}
X
Xadelay(ms, dp)
X	register ms;
X	register struct delayval *dp;
X{
X	if (ms == 0)
X		return (0);
X	while (dp->delay && ms > dp->delay)
X		dp++;
X	return (dp->bits);
X}
X
Xchar	editedhost[32];
X
Xedithost(pat)
X	register char *pat;
X{
X	register char *host = HN;
X	register char *res = editedhost;
X
X	if (!pat)
X		pat = "";
X	while (*pat) {
X		switch (*pat) {
X
X		case '#':
X			if (*host)
X				host++;
X			break;
X
X		case '@':
X			if (*host)
X				*res++ = *host++;
X			break;
X
X		default:
X			*res++ = *pat;
X			break;
X
X		}
X		if (res == &editedhost[sizeof editedhost - 1]) {
X			*res = '\0';
X			return;
X		}
X		pat++;
X	}
X	if (*host)
X		strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
X	else
X		*res = '\0';
X	editedhost[sizeof editedhost - 1] = '\0';
X}
X
Xstruct speedtab {
X	int	speed;
X	int	uxname;
X} speedtab[] = {
X	50,	B50,
X	75,	B75,
X	110,	B110,
X	134,	B134,
X	150,	B150,
X	200,	B200,
X	300,	B300,
X	600,	B600,
X	1200,	B1200,
X	1800,	B1800,
X	2400,	B2400,
X	4800,	B4800,
X	9600,	B9600,
X	19200,	EXTA,
X	19,	EXTA,		/* for people who say 19.2K */
X	38400,	EXTB,
X	38,	EXTB,
X	7200,	EXTB,		/* alternative */
X	0
X};
X
Xspeed(val)
X{
X	register struct speedtab *sp;
X
X	if (val <= 15)
X		return(val);
X
X	for (sp = speedtab; sp->speed; sp++)
X		if (sp->speed == val)
X			return (sp->uxname);
X	
X	return (B300);		/* default in impossible cases */
X}
X
Xmakeenv(env)
X	char *env[];
X{
X	static char termbuf[128] = "TERM=";
X	register char *p, *q;
X	register char **ep;
X	char *index();
X
X	ep = env;
X	if (TT && *TT) {
X		strcat(termbuf, TT);
X		*ep++ = termbuf;
X	}
X	if (p = EV) {
X		q = p;
X		while (q = index(q, ',')) {
X			*q++ = '\0';
X			*ep++ = p;
X			p = q;
X		}
X		if (*p)
X			*ep++ = p;
X	}
X	*ep = (char *)0;
X}
X
X/*
X * This speed select mechanism is written for the Develcon DATASWITCH.
X * The Develcon sends a string of the form "B{speed}\n" at a predefined
X * baud rate. This string indicates the user's actual speed.
X * The routine below returns the terminal type mapped from derived speed.
X */
Xstruct	portselect {
X	char	*ps_baud;
X	char	*ps_type;
X} portspeeds[] = {
X	{ "B110",	"std.110" },
X	{ "B134",	"std.134" },
X	{ "B150",	"std.150" },
X	{ "B300",	"std.300" },
X	{ "B600",	"std.600" },
X	{ "B1200",	"std.1200" },
X	{ "B2400",	"std.2400" },
X	{ "B4800",	"std.4800" },
X	{ "B9600",	"std.9600" },
X	{ 0 }
X};
X
Xchar *
Xportselector()
X{
X	char c, baud[20], *type = "default";
X	register struct portselect *ps;
X	int len;
X
X	alarm(5*60);
X	for (len = 0; len < sizeof (baud) - 1; len++) {
X		if (read(0, &c, 1) <= 0)
X			break;
X		c &= 0177;
X		if (c == '\n' || c == '\r')
X			break;
X		if (c == 'B')
X			len = 0;	/* in case of leading garbage */
X		baud[len] = c;
X	}
X	baud[len] = '\0';
X	for (ps = portspeeds; ps->ps_baud; ps++)
X		if (strcmp(ps->ps_baud, baud) == 0) {
X			type = ps->ps_type;
X			break;
X		}
X	sleep(2);	/* wait for connection to complete */
X	return (type);
X}
X
X/*
X * This auto-baud speed select machanism is written for the Micom 600
X * portselector. Selection is done by looking at how the character '\r'
X * is garbled at the different speeds.
X */
X#include <sys/time.h>
X
Xchar *
Xautobaud()
X{
X	int rfds;
X	struct timeval timeout;
X	char c, *type = "2400-baud";
X	int null = 0;
X
X	ioctl(0, TIOCFLUSH, &null);
X	rfds = 1 << 0;
X	timeout.tv_sec = 5;
X	timeout.tv_usec = 0;
X	if(select(32, &rfds, (int *)0, (int *)0, &timeout) <= 0)
X		return(type);
X	if(read(0, &c, sizeof(char)) != sizeof(char))
X		return(type);
X	timeout.tv_sec = 0;
X	timeout.tv_usec = 20;
X	(void) select(32, (int *)0, (int *)0, (int *)0, &timeout);
X	ioctl(0, TIOCFLUSH, &null);
X	switch(c&0377){
X
X	case 0200:		/* 300-baud */
X		type = "300-baud";
X		break;
X
X	case 0346:		/* 1200-baud */
X		type = "1200-baud";
X		break;
X
X	case  015:		/* 2400-baud */
X	case 0215:
X		type = "2400-baud";
X		break;
X
X	default:		/* 4800-baud */
X		type = "4800-baud";
X		break;
X
X	case 0377:		/* 9600-baud */
X		type = "9600-baud";
X		break;
X	}
X	return(type);
X}
X
X/* This was hacked up very fast.  Please excuse stupidities... */
X
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
X
X#define NORMAL		ret
X#define AUTOBAUD	"Auto-baud"
X#define DROP		((char *)0)
X
Xextern	struct servent *getservbyname();
Xchar	*pacx_term = 0;
Xchar	*pacx_line = "line ???";
X
Xchar *
Xpacx(tty)
Xchar *tty;
X{
X	struct servent *servp;
X	static char ret[6+6];
X	static struct sockaddr_in sin = { AF_INET };
X	static struct {
X		char	baud[6];
X		char	term[14];
X		int	port;
X		int	line;
X		time_t	time;
X	} resp;
X	static	char line_str[32];
X	int	so;
X	time_t	tnow;
X
X	pacx_term = 0;
X	if ((servp = getservbyname("ttyinfo", "tcp")) == 0)
X		return(AUTOBAUD);
X
X	if( (so = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
X		return(AUTOBAUD);
X
X	/*
X	 *  The following assumes a lot...  This is the address
X	 *  of the "loopback" interface.  A portable and fast way
X	 *  to get to the current host.  We can't rely on gethostid.
X	 */
X	sin.sin_addr.s_addr = htonl(0x7f000001);
X	sin.sin_port = servp->s_port;
X
X	if( connect(so, &sin, sizeof sin) == -1 )  {
X		close(so);
X		return(AUTOBAUD);
X	}
X	if( write(so, tty, strlen(tty)) != strlen(tty) )  {
X		close(so);
X		return(DROP);
X	}
X	if( read(so, (char *)&resp, sizeof resp) != sizeof resp )  {
X		close(so);
X		return(DROP);
X	}
X	close(so);
X
X	(void) time(&tnow);
X	if(tnow - resp.time > 10)
X		return(DROP);
X	strncpy(ret, resp.baud, sizeof(resp.baud));
X	ret[sizeof(resp.baud)] = 0;
X	strcat(ret, "-baud");
X	resp.term[sizeof(resp.term)-1] = 0;
X	pacx_term = resp.term;
X	sprintf (line_str, "line %03o", resp.line);
X	pacx_line = line_str;
X	return(NORMAL);
X}
________This_Is_The_END________