/*
 * User supdup program.
 *
 *	Written Jan. 1985 by David Bridgham.  Much of the code dealing
 * with the network was taken from the telnet user program released
 * with 4.2 BSD UNIX.
 */

/* Define exactly one of TERMCAP or TERMINFO.
 * Link with the appropriate TERMINFO or TERMCAP library.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>

#include <netinet/in.h>

#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <netdb.h>

#ifdef	TERMINFO
#include	<term.h>
#endif	TERMINFO
#ifdef	TERMCAP
#include	"termcap.h"
static char	tspace[256], *aoftspace;
char _bools[NUM_BOOLS];
char *_strs[NUM_STRS];
int _nums[NUM_NUMS];
char outstring[128];
#endif

#define	strip(x)	((x)&0177)

char	ttyobuf[BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;

char	hisopts[256];
char	myopts[256];

int	connected;
int	net;
int	showoptions = 0;
int	options;
int	debug = 0;
#ifdef DEBUG
int	debug_file = 0;
#endif
int	crmod = 0;
int	mask = 0177;
int	high_bits = 0;
char	escape = CTRL(^);

char	line[200];
int	margc;
char	*margv[20];
char	*oldargv0;

jmp_buf	toplevel;
jmp_buf	peerdied;

extern	int errno;

int	sd(), quit(), suspend(), bye(), help();
int	setescape(), status(), toggle(), setoptions();
int	setcrmod(), setdebug(), thru(), top();

#define HELPINDENT (sizeof ("connect"))

struct cmd {
	char	*name;		/* command name */
	char	*help;		/* help string */
	int	(*handler)();	/* routine which executes command */
};

char	openhelp[] =	"connect to a site";
char	closehelp[] =	"close current connection";
char	quithelp[] =	"exit supdup";
char	zhelp[] =	"suspend supdup";
char	debughelp[] =	"toggle debugging";
char	escapehelp[] =	"set escape character";
char	statushelp[] =	"print status information";
char	helphelp[] =	"print help information";
char	optionshelp[] =	"toggle viewing of options processing";
char	crmodhelp[] =	"toggle mapping of received carriage returns";
char	tophelp[] =	"set 'TOP' bit on next character";
char	thruhelp[] =	"sends escape character through";

struct cmd cmdtab[] = {
	{ "\036",	thruhelp,	thru },
	{ "o",	openhelp,	sd },
	{ "c",	closehelp,	bye },
	{ "q",	quithelp,	quit },
	{ "p",	zhelp,		suspend },
	{ "e",	escapehelp,	setescape },
	{ "s",	statushelp,	status },
	{ "v",	optionshelp,	setoptions },
	{ "r",	crmodhelp,	setcrmod },
	{ "d",	debughelp,	setdebug },
	{ "t",	tophelp,	top },
	{ "?",	helphelp,	help },
	0
};

/*
 * Supdup display protocol commands
 */
#define TDMOV	0200
#define TDMV1	0201	/* not defined in supdup spec AIM 644 */
#define TDEOF	0202
#define TDEOL	0203
#define TDDLF	0204
#define TDCRL	0207
#define TDNOP	0210
#define TDBS	0211	/* not defined in supdup spec AIM 644 */
#define TDLF	0212	/* not defined in supdup spec AIM 644 */
#define TDCR	0213	/* not defined in supdup spec AIM 644 */
#define TDORS	0214
#define TDQOT	0215
#define TDFS	0216
#define TDMV0	0217
#define TDCLR	0220
#define TDBEL	0221
#define TDILP	0223
#define TDDLP	0224
#define TDICP	0225
#define TDDCP	0226
#define TDBOW	0227
#define TDRST	0230
#define TDGRF	0231
#define TDSCU	0232	/* Scroll region up */
#define TDSCD	0233	/* Scroll region down */

/* ITP and SUPDUP codes */
#define ITP_PREFIX 034
#define ITP_PIATY 003		/* Request for redisplay because
				 * screen was messed up */

int currcol, currline;	/* Current cursor position */

struct sockaddr_in sin;

int	intr(), deadpeer();
char	*control();
struct	cmd *getcmd();
struct	servent *sp;

struct	tchars otc;
struct	ltchars oltc;
struct	sgttyb ottyb;

#define	INIT_LEN	42	/* Number of bytes to send at initialization */
static char inits[] = {
	077, 077, -6, 0, 0, 0,
	0, 0, 0, 0, 0, 7,
	1, 2, 020, 0, 0, 050,
	0, 0, 0, 0, 0, 24,
	0, 0, 0, 0, 0, 79,
	0, 0, 0, 0, 0, 1,
	0, 0, 0, 0, 0, 0
	};

main(argc, argv)
	int argc;
	char *argv[];
{
	sp = getservbyname("supdup", "tcp");
	if (sp == 0) {
		fprintf(stderr, "supdup: tcp/supdup: unknown service\n");
		exit(1);
	}
	ioctl(0, TIOCGETP, (char *)&ottyb);
	ioctl(0, TIOCGETC, (char *)&otc);
	ioctl(0, TIOCGLTC, (char *)&oltc);
	setbuf(stdin, 0);
	setbuf(stdout, 0);
	if (argv[1][0] == '-')
		if (argv[1][1] == 's')
			argc--, argv++, inits[13] |= 01;

	if (argc > 1 && !strcmp(argv[1], "-d"))
		debug = SO_DEBUG, argv++, argc--;
#ifdef DEBUG
	if (argc > 1 && !strcmp(argv[1], "-t")) {
		argv++, argc--;
		debug_file = open("supdup_debug", 01001, 0777);	/* Open for write and creat */
		if (debug_file < 0) {
		    printf("Couldn't open debug file\n");
		    exit(-1);
		}
	}
#endif
	if (argc != 1) {
		if (setjmp(toplevel) != 0)
			exit(0);
		sd(argc, argv);
	}
	setjmp(toplevel);
	for (;;) {
		printf("Host: ");
		strcpy(line, "Host: ");
		if (gets(&line[strlen(line)]) == 0) {
		    if (feof(stdin)) {
			clearerr(stdin);
			putchar('\n');
		    }
		    break;
		}
		oldargv0 = argv[0];
		makeargv();
		argv = margv;
		argc = margc;
		argv[0] = oldargv0;
		sd(argc, argv);
	}
}

char	*hostname;
char	hnamebuf[32];

sd(argc, argv)
	int argc;
	char *argv[];
{
	register int c;
	register struct hostent *host;

	sup_term();

	if (connected) {
		printf("?Already connected to %s\n", hostname);
		return;
	}

	if ((argc != 2) & (argc != 3)) {
		printf("usage: %s host-name [port]\n", argv[0]);
		return;
	}
	if (*argv[1] >= '0' && *argv[1] <= '9') {
		sin.sin_family = AF_INET;
		sin.sin_addr.s_addr = inet_addr(argv[1]);
		if (sin.sin_addr.s_addr == -1) {
			printf("%s: unknown host\n", argv[1]);
			return;
		}
		strcpy(hnamebuf, argv[1]);
		hostname = hnamebuf;
	} else {
		if ((host = gethostbyname(argv[1])) == 0) {
			printf("%s: unknown host\n", argv[1]);
			return;
		}
		sin.sin_family = host->h_addrtype;
		bcopy(host->h_addr, (caddr_t)&sin.sin_addr, host->h_length);
		hostname = host->h_name;
	}
	sin.sin_port = sp->s_port;
	if (argc == 3) {
		sin.sin_port = atoi(argv[2]);
		if (sin.sin_port < 0) {
			printf("%s: bad port number\n", argv[2]);
			return;
		}
		sin.sin_port = htons(sin.sin_port);
	}
	net = socket(AF_INET, SOCK_STREAM, 0);
	if (net < 0) {
		perror("telnet: socket");
		return;
	}
	if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
		perror("setsockopt (SO_DEBUG)");
	signal(SIGINT, intr);
	signal(SIGPIPE, deadpeer);
	printf("Trying...\n");
	if (connect(net, (caddr_t)&sin, sizeof (sin)) < 0) {
		perror("supdup: connect");
		signal(SIGINT, SIG_DFL);
		return;
	}
	connected++;
	call(status, "status", 0);
	if (setjmp(peerdied) == 0)
		supdup(net);
	fprintf(stderr, "Connection closed by foreign host.\n");
	exit(1);
}

/*
 * Initialize the terminal description to be sent when the connection is
 * opened.
 */
sup_term()
{
#ifdef	TERMINFO
	int	errret;

	setupterm(0,1,&errret);
	if (errret == -1) {
		fprintf(stderr, "Bad terminfo database\n");
		exit(1);
	}
	else if (errret == 0) {
		fprintf(stderr, "Unknown terminal type\n");
		exit(1);
	}
#endif	TERMINFO
#ifdef	TERMCAP
    auto char bp[1024];

    switch (systgetent(bp))
    {
	case 1:	    zap();
		    break;

	case 0:	    fprintf(stderr, "Invalid terminal\n");
		    exit(3);

	case -1:    fprintf(stderr, "Can't open termcap file\n");
		    exit(4);
    }
#endif	TERMCAP

	inits[23] = lines;
	inits[29] = columns - 1;
	if (clr_eol)		inits[12] |= 04;
	if (over_strike)	inits[13] |= 010;
	if (cursor_address)	inits[13] |= 04;
	if (has_meta_key) {
				inits[14] |= 010;
				mask = 0377;
	}
	if ((delete_line || parm_delete_line) &&
	    (insert_line || parm_insert_line))
				inits[14] |= 02;
	if ((delete_character || parm_dch) &&
	    (insert_character || parm_ich))
				inits[14] |= 01;
}

#ifdef	TERMCAP
static zap()
{
    char *fp;
    char *namp, **sp;
    int *np;
    extern char *tgetstr();
    extern int character_mask;

    /*
     *	get boolean flags
     */

    namp = "ambshckmmincptul";
    for (fp = _bools; fp < _bools + sizeof _bools; namp += 2)
	*fp++ = tgetflag(namp);

    /*
     *	get string values
     */

    aoftspace = tspace;
    namp = "albcbtcdcechclcmcrcscvdcdldmdoedeihoicimisllndnlpcsfsrtatetiupvbvevssesoDCICDLAL";
    for (sp = _strs; sp < _strs + sizeof _strs / sizeof _strs[0]; namp += 2)
	*sp++ = tgetstr(namp, &aoftspace);
    if (!cursor_left)
	cursor_left = "\b";
    if (!carriage_return)
	carriage_return = "\r";

    /*
     *	get numeric values
     */

    namp = "codBdCdNdTli";
    for (np = _nums;  np < _nums + sizeof _nums / sizeof _nums[0]; namp += 2)
	*np++ = tgetnum(namp);
}

extern char *getenv();

systgetent(bp)
char bp[1024];
{
    register char *term;

    if (term = getenv("TERM"))
	return tgetent(bp, term);
    else
	return 0;
}
#endif	TERMCAP


/*
 * Print status about the connection.
 */
/*VARARGS*/
status()
{
	if (connected)
		printf("Connected to %s.\n", hostname);
	else
		printf("No connection.\n");
	printf("Escape character is '%s'.\n", control(escape));
	fflush(stdout);
}

makeargv()
{
	register char *cp;
	register char **argp = margv;

	margc = 0;
	for (cp = line; *cp;) {
		while (isspace(*cp))
			cp++;
		if (*cp == '\0')
			break;
		*argp++ = cp;
		margc += 1;
		while (*cp != '\0' && !isspace(*cp))
			cp++;
		if (*cp == '\0')
			break;
		*cp++ = '\0';
	}
	*argp++ = 0;
}

/*VARARGS*/
suspend(margc, margv)
int margc;
char *margv[];
{
	register int save;

	save = mode(0);
	kill(0, SIGTSTP);
	/* reget parameters in case they were changed */
	ioctl(0, TIOCGETP, (char *)&ottyb);
	ioctl(0, TIOCGETC, (char *)&otc);
	ioctl(0, TIOCGLTC, (char *)&oltc);
	(void) mode(save);
	*nfrontp++ = ITP_PREFIX; /* Tell other end that it sould refresh */
	*nfrontp++ = ITP_PIATY;	 /* the screen */
}

/*VARARGS*/
bye()
{
	register char *op;

	(void) mode(0);
	if (connected) {
		shutdown(net, 2);
		printf("Connection closed.\n");
		close(net);
		connected = 0;
		/* reset his options */
		for (op = hisopts; op < &hisopts[256]; op++)
			*op = 0;
	}
}

/*VARARGS*/
quit()
{
	call(bye, "bye", 0);
	exit(0);
}

/*
 * Help command.
 */
help()
{
	register struct cmd *c;

	printf("Type %s followed by the command character.  Commands are:\n",
		control(escape));
	for (c = cmdtab; c->name; c++)
		printf("%-*s\t%s\n", HELPINDENT, control(*(c->name)), c->help);
	fflush(stdout);
	return;
}

/*
 * Call routine with argc, argv set from args (terminated by 0).
 * VARARGS2
 */
call(routine, args)
	int (*routine)();
	int args;
{
	register int *argp;
	register int argc;

	for (argc = 0, argp = &args; *argp++ != 0; argc++)
		;
	(*routine)(argc, &args);
}

struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };

mode(f)
	register int f;
{
	static int prevmode = 0;
	struct tchars *tc;
	struct ltchars *ltc;
	struct sgttyb sb;
	int onoff, old;

	if (prevmode == f)
		return (f);
	old = prevmode;
	prevmode = f;
	sb = ottyb;
	switch (f) {

	case 0:
		onoff = 0;
		tc = &otc;
		ltc = &oltc;
		break;

	case 1:
	case 2:
		sb.sg_flags |= RAW;		/* was CBREAK */
		if (f == 1)
			sb.sg_flags &= ~(ECHO|CRMOD);
		else
			sb.sg_flags |= ECHO|CRMOD;
		sb.sg_erase = sb.sg_kill = -1;
		tc = &notc;
		ltc = &noltc;
		onoff = 1;
		break;

	default:
		return;
	}
	ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
	ioctl(fileno(stdin), TIOCSETC, (char *)tc);
	ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
	ioctl(fileno(stdin), FIONBIO, &onoff);
	ioctl(fileno(stdout), FIONBIO, &onoff);
	return (old);
}

char	sibuf[BUFSIZ], *sbp;
char	tibuf[BUFSIZ], *tbp;
int	scc, tcc;

/*
 * Select from tty and network...
 */
supdup(s)
	int s;
{
	register int c;
	int tin = fileno(stdin), tout = fileno(stdout);
	int on = 1;
	int escape_seen = 0;

	(void) mode(1);
	ioctl(s, FIONBIO, &on);
	send_init();
	for (;;) {
		int ibits = 0, obits = 0;

		if (nfrontp - nbackp)
			obits |= (1 << s);
		else
			ibits |= (1 << tin);
		if (tfrontp - tbackp)
			obits |= (1 << tout);
		else
			ibits |= (1 << s);
		if (scc < 0 && tcc < 0)
			break;
		select(16, &ibits, &obits, 0, 0);
		if (ibits == 0 && obits == 0) {
			sleep(5);
			continue;
		}

		/*
		 * Something to read from the network...
		 */
		if (ibits & (1 << s)) {
			scc = read(s, sibuf, sizeof (sibuf));
			if (scc < 0 && errno == EWOULDBLOCK)
				scc = 0;
			else {
				if (scc <= 0)
					break;
				sbp = sibuf;
#ifdef DEBUG
				if (debug_file)
				  write(debug_file, sibuf, scc);
#endif
			}
		}

		/*
		 * Something to read from the tty...
		 */
		if (ibits & (1 << tin)) {
			tcc = read(tin, tibuf, sizeof (tibuf));
			if (tcc < 0 && errno == EWOULDBLOCK)
				tcc = 0;
			else {
				if (tcc <= 0)
					break;
				tbp = tibuf;
			}
		}

		while (tcc > 0) {
			register int c;

			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
				break;
			c = *tbp++ & mask, tcc--;
			if (escape_seen) {
				if (c == escape)
				  *nfrontp++ = c;
				else
				  command(c);
				escape_seen = 0;
				continue;
			}
			if (c == escape) {
				escape_seen++;
				continue;
			}
			if (c & 0200) {
				high_bits = 2;
				c &= 0177;
			}
			if ((c & 0140) == 0) {
				switch(c) {
				case 010:
				case 011:
				case 012:
				case 013:
				case 014:
				case 015:
				case 032:
				case 033:
				case 037:
					break;
				default:
					high_bits |= 1;
					c = c + '@';
					break;
				}
			}
			if (high_bits) {
				*nfrontp++ = ITP_PREFIX;
				*nfrontp++ = high_bits + 0100;
				high_bits = 0;
			}
			*nfrontp++ = c;
		}
		if ((obits & (1 << s)) && (nfrontp - nbackp) > 0)
			netflush(s);
		if (scc > 0)
			suprcv();
		if ((obits & (1 << tout)) && (tfrontp - tbackp) > 0)
			ttyflush(tout);
	}
	(void) mode(0);
}

send_init()
{
	register	i;

	for (i = 0; i < INIT_LEN; i++)
		*nfrontp++ = inits[i];
}

command(chr)
	char chr;
{
	register struct cmd *c;
	int oldmode, wasopen;

	oldmode = mode(0);
	c = getcmd(&chr);
	if (c == 0) {
		printf("?Invalid supdup command\n");
		(void) mode(oldmode);
		return;
	}
	(*c->handler)(margc, margv);
	if (!connected)
		longjmp(toplevel, 1);
	(void) mode(oldmode);
}

putch(c)
int	c;
{
	*tfrontp++ = c;
}


/*
 * Supdup receiver states for fsm
 */
#define	SR_DATA		0
#define	SR_M0		1
#define	SR_M1		2
#define	SR_M2		3
#define	SR_M3		4
#define	SR_QUOTE	5
#define	SR_IL		6
#define	SR_DL		7
#define	SR_IC		8
#define	SR_DC		9

suprcv()
{
	register int c;
	static int state = SR_DATA;
	static int y;

	while (scc > 0) {
		c = *sbp++ & 0377, scc--;
		switch (state) {

		case SR_DATA:
		    if ((c & 0200) == 0) {
			*tfrontp++ = c;
			if (c == '\r')
			  currcol = 0;
			else
			  currcol++;
			continue;
		    }
		    else
			switch (c) {
			case TDMOV:
				state = SR_M0;
				continue;
			case TDMV1:
			case TDMV0:
				state = SR_M2;
				continue;
			case TDEOF:
				if (clr_eos)
					tputs(clr_eos, lines-currline, putch);
				continue;
			case TDEOL:
				if (clr_eol)
					tputs(clr_eol, columns-currcol, putch);
				continue;
			case TDDLF:
				putch(' ');
				putch('\b');
				continue;
			case TDBS:
				if (cursor_left)
					tputs(cursor_left, 0, putch);
				else
					putch('\b');
				if (currcol != 0)
				  currcol--;
				continue;
			case TDCR:
				putch('\r');
				currcol = 0;
				continue;
			case TDLF:
				if (cursor_down)
					tputs(cursor_down, 0, putch);
				else
					putch('\n');
				currline++;
				continue;
			case TDCRL:
				if (!(currcol >= 80 && auto_right_margin)) {
#ifdef	TERMINFO
				    if (newline)
				      tputs(newline, 1, putch);
				    else
#endif
				      {
					  putch('\r');
					  putch('\n');
				      }
				}
				currcol = 0;
				currline++;
				tputs(clr_eol, columns-currcol, putch);
				continue;
			case TDNOP:
				continue;
			case TDORS:		/* should do something here? */
				continue;
			case TDQOT:
				state = SR_QUOTE;
				continue;
			case TDFS:
				tputs(cursor_right, 1, putch);
				currcol++;
				continue;
			case TDCLR:
				if (clear_screen)
					tputs(clear_screen, lines, putch);
				else {
				    if (cursor_address)
#ifdef	TERMINFO
					tputs(tparm(cursor_address, 0, 0), lines, putch);
#endif
#ifdef	TERMCAP
					tputs(tgoto(cursor_address, 0, 0), lines, putch);
#endif
				    tputs(clr_eos, lines, putch);
				}
				currcol = 0;
				currline = 0;
				continue;
			case TDBEL:
				if (flash_screen)
					tputs(flash_screen, 0, putch);
				else if (bell)
					tputs(bell, 0, putch);
				else
					putch('\007');
				continue;
			case TDILP:
				state = SR_IL;
				continue;
			case TDDLP:
				state = SR_DL;
				continue;
			case TDICP:
				state = SR_IC;
				continue;
			case TDDCP:
				state = SR_DC;
				continue;
			case TDBOW:
				if (enter_standout_mode)
					tputs(enter_standout_mode, 0, putch);
				continue;
			case TDRST:
				if (exit_standout_mode)
					tputs(exit_standout_mode, 0, putch);
				continue;
			default:
				fprintf(stderr, "bad supdup opcode %o ignored\r\n", c);
			}
		case SR_M0:
			state = SR_M1;
			continue;
		case SR_M1:
			state = SR_M2;
			continue;
		case SR_M2:
			y = c;
			state = SR_M3;
			continue;
		case SR_M3:
#ifdef	TERMINFO
			tputs(tparm(cursor_address, y, c), lines, putch);
#endif
#ifdef	TERMCAP
			tputs(tgoto(cursor_address, c, y), lines, putch);
#endif
			state = SR_DATA;
			currline = y;
			currcol = c;
			continue;
		case SR_QUOTE:
			putch(c);
			state = SR_DATA;
			continue;
		case SR_IL:
		    if (parm_insert_line) {
#ifdef	TERMINFO
			tputs(tparm(parm_insert_line, c), c, putch);
#endif
#ifdef	TERMCAP
			tparam(parm_insert_line, outstring, c);
			tputs(outstring, c, putch);
#endif
		    }
		    else
		      if (insert_line)
			while (c--)
			  tputs(insert_line, 1, putch);
		    state = SR_DATA;
		    continue;
		case SR_DL:
		    if (parm_delete_line) {
#ifdef	TERMINFO
			tputs(tparm(parm_delete_line, c), c, putch);
#endif
#ifdef	TERMCAP
			tparam(parm_delete_line, outstring, c);
			tputs(outstring, c, putch);
#endif
		    }
		    else
		      if (delete_line)
			while (c--)
			  tputs(delete_line, 1, putch);
		    state = SR_DATA;
		    continue;
		case SR_IC:
		    if (parm_ich) {
#ifdef	TERMINFO
			tputs(tparm(parm_ich, c), c, putch);
#endif
#ifdef	TERMCAP
			tparam(parm_ich, outstring, c);
			tputs(outstring, c, putch);
#endif
		    }
		    else
		      if (insert_character)
			while (c--)
			  tputs(insert_character, 1, putch);
		    state = SR_DATA;
		    continue;
		case SR_DC:
		    if (parm_dch) {
#ifdef	TERMINFO
			tputs(tparm(parm_dch, c), c, putch);
#endif
#ifdef	TERMCAP
			tparam(parm_dch, outstring, c);
			tputs(outstring, c, putch);
#endif
		    }
		    else
		      if (delete_character)
			while (c--)
			  tputs(delete_character, 1, putch);
		    state = SR_DATA;
		    continue;
		}
	}
}


/*
 * Set the escape character.
 */
setescape()
{
	printf("type new escape character followed by <return>: ");
	escape = getchar();
	*(cmdtab->name) = escape;
	printf("Escape character is '%s'.\n", control(escape));
	fflush(stdout);
}

/*VARARGS*/
setoptions()
{

	showoptions = !showoptions;
	printf("%s show option processing.\n", showoptions ? "Will" : "Wont");
	fflush(stdout);
}

/*VARARGS*/
setcrmod()
{

	crmod = !crmod;
	printf("%s map carriage return on output.\n", crmod ? "Will" : "Wont");
	fflush(stdout);
}

/*VARARGS*/
setdebug()
{

	debug = !debug;
	printf("%s turn on socket level debugging.\n",
		debug ? "Will" : "Wont");
	fflush(stdout);
	if (debug && net > 0 && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
		perror("setsockopt (SO_DEBUG)");
}

/*
 * Construct a control character sequence
 * for a special character.
 */
char *
control(c)
	register int c;
{
	static char buf[3];

	if (c == 0177)
		return ("^?");
	if (c >= 040) {
		buf[0] = c;
		buf[1] = 0;
	} else {
		buf[0] = '^';
		buf[1] = '@'+c;
		buf[2] = 0;
	}
	return (buf);
}

struct cmd *
getcmd(name)
	register char *name;
{
	register char *p;
	register struct cmd *c;

	for (c = cmdtab; p = c->name; c++)
		if (*p == *name)
			return(c);
	return(0);
}

deadpeer()
{
	(void) mode(0);
	longjmp(peerdied, -1);
}

intr()
{
	(void) mode(0);
	longjmp(toplevel, -1);
}

ttyflush(fd)
{
	int n;

	if ((n = tfrontp - tbackp) > 0)
		n = write(fd, tbackp, n);
	if (n < 0)
		return;
	tbackp += n;
	if (tbackp == tfrontp)
		tbackp = tfrontp = ttyobuf;
}

netflush(fd)
{
	int n;

	if ((n = nfrontp - nbackp) > 0)
		n = write(fd, nbackp, n);
	if (n < 0) {
		if (errno != ENOBUFS && errno != EWOULDBLOCK) {
			(void) mode(0);
			perror(hostname);
			close(fd);
			longjmp(peerdied, -1);
			/*NOTREACHED*/
		}
		n = 0;
	}
	nbackp += n;
	if (nbackp == nfrontp)
		nbackp = nfrontp = netobuf;
}

thru()
{}

top()
{
	high_bits |= 020;
}

#ifdef	TERMCAP

/* Assuming `string' is the value of a termcap string entry
   containing `%' constructs to expand parameters,
   merge in parameter values and store result in block `outstring' points to.
   No check is made for overflowing `outstring';
   the caller is wise to allocate space for it based on the size of
   `string', knowing that the size can increase by at most a couple
   of characters per parameter.
   The third and following args to tparam serve as the parameter values.  */

tparam (string, outstring, arg)
     char *string;
     register char *outstring;
     int arg;
{
  register int c;
  register char *p = string;
  register int *argp = &arg;

  int tem;
  char *format;
  char buf[10];

  while (c = *p++)
    {
      if (c == '%')
	{
	  c = *p++;
	  format = 0;
	  switch (c)
	    {
	    case 'd':		/* %d means output in decimal */
	      format = "%d";
	      break;
	    case '2':		/* %2 means output in decimal, 2 digits. */
	      format = "%2d";
	      break;
	    case '3':		/* %3 means output in decimal, 3 digits. */
	      format = "%3d";
	      break;
	    case '.':		/* %. means output as character */
	      format = "%c";
	      break;
	    case 'C':
	      /* For c-100: print quotient of value by 96, if nonzero,
		 then do like %+ */
	      if (argp[0] >= 96)
		{
		  *outstring++ = argp[0] / 96;
		  argp[0] %= 96;
		}
	    case '+':		/* %+x means add character code of char x */
	      format = "%c";	/* Then output sum as a character. */
	      argp[0] += *p++;
	      break;
	    case '>':		/* %>xy means if arg is > char code of x, */
	      if (argp[0] > *p++) /* then add char code of y to the arg, */
		argp[0] += *p;	/* and in any case don't output. */
	      p++;		/* Leave the arg to be output later. */
	      break;
	    case 'r':		/* %r means interchange following two args */
	      tem = argp[0];
	      argp[0] = argp[1];
	      argp[1] = tem;
	      break;
	    case 'i':		/* %i means add one to arg, */
	      argp[0] ++;	/* and leave it to be output later. */
	      break;
	    case '%':		/* %% means output %; no arg. */
	      format = "%%";
	      argp--;
	      break;
	    case 'n':		/* %n means xor each of next two args with 140 */
	      argp[0] ^= 0140;
	      argp[1] ^= 0140;
	      break;
	    case 'B':		/* %B means express arg as BCD char code. */
	      argp[0] = 16 * (argp[0] / 10) + argp[0] % 10;
	    case 'D':		/* %D means weird Delta Data transformation */
	      argp[0] -= 2 * (argp[0] % 16);
	      break;
	    }
	  /* If an output format was specified, output one arg with it,
	     and swallow that arg.  */
	  if (format)
	    {
	      sprintf (outstring, format, argp[0]);
	      outstring += strlen (outstring);
	      argp++;
	    }
	}
      else
	/* Ordinary character in the argument string.
	   Just output it as itself, but flush high bit.  */
	*outstring++ = c;
    }
  *outstring = 0;
}
#endif
