/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: commands.c,v 1.7.2.1 92/06/24 13:42:35 marker Exp $"
/*
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to the University of California at Berkeley. The name of the University
 * may not be used to endorse or promote products derived from this
 * software without specific prior written permission. This software
 * is provided ``as is'' without express or implied warranty.
 */

#ifndef lint
static char sccsid[] = "@(#)commands.c	1.5 (Berkeley) 5/15/88";
#endif /* not lint */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <signal.h>
#include <netdb.h>
#include <ctype.h>

#include <arpa/telnet.h>

#include "general.h"

#include "ring.h"

#include "externs.h"
#include "defines.h"
#include "types.h"

#ifdef RISCOS
#include <varargs.h>
#endif /* RISCOS */

extern char *gettxt();

char	*hostname;

#define Ambiguous(s)	((char *)s == ambiguous)
static char *ambiguous;		/* special return value for command routines */

typedef struct {
	char	*name;		/* command name */
	char	*help;		/* help string */
	int	(*handler)();	/* routine which executes command */
	int	dohelp;		/* Should we give general help information? */
	int	needconnect;	/* Do we need to be connected to execute? */
} Command;

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

/*
 * Various utility routines.
 */

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

    margc = 0;
    cp = line;
    if (*cp == '!') {		/* Special case shell escape */
	*argp++ = "!";		/* No room in string to get this */
	margc++;
	cp++;
    }
    while (*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;
}


static char **
genget(name, table, next)
char	*name;		/* name to match */
char	**table;		/* name entry in table */
char	**(*next)();	/* routine to return next entry in table */
{
	register char *p, *q;
	register char **c, **found;
	register int nmatches, longest;

	if (name == 0) {
	    return 0;
	}
	longest = 0;
	nmatches = 0;
	found = 0;
	for (c = table; (p = *c) != 0; c = (*next)(c)) {
		for (q = name;
		    (*q == *p) || (isupper(*q) && tolower(*q) == *p); p++, q++)
			if (*q == 0)		/* exact match? */
				return (c);
		if (!*q) {			/* the name was a prefix */
			if (q - name > longest) {
				longest = q - name;
				nmatches = 1;
				found = c;
			} else if (q - name == longest)
				nmatches++;
		}
	}
	if (nmatches > 1)
		return (char **)ambiguous;
	return (found);
}

/*
 * Make a character string into a number.
 *
 * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
 */

static
special(s)
register char *s;
{
	register char c;
	char b;

	switch (*s) {
	case '^':
		b = *++s;
		if (b == '?') {
		    c = b | 0x40;		/* DEL */
		} else {
		    c = b & 0x1f;
		}
		break;
	default:
		c = *s;
		break;
	}
	return c;
}

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

	if (c == 0x7f)
		return ("^?");
	if (c == '\377') {
		return "off";
	}
	if (c >= 0x20) {
		buf[0] = c;
		buf[1] = 0;
	} else {
		buf[0] = '^';
		buf[1] = '@'+c;
		buf[2] = 0;
	}
	return (buf);
}



/*
 *	The following are data structures and routines for
 *	the "send" command.
 *
 */
 
struct sendlist {
    char	*name;		/* How user refers to it (case independent) */
    int		what;		/* Character to be sent (<0 ==> special) */
    char	*help;		/* Help information (0 ==> no help) */
#if	defined(NOT43)
    int		(*routine)();	/* Routine to perform (for special ops) */
#else	/* defined(NOT43) */
    void	(*routine)();	/* Routine to perform (for special ops) */
#endif	/* defined(NOT43) */
};

#define	SENDQUESTION	-1
#define	SENDESCAPE	-3

static struct sendlist Sendlist[] = {
    { "ao", AO, "Send Telnet Abort output" },
    { "ayt", AYT, "Send Telnet 'Are You There'" },
    { "brk", BREAK, "Send Telnet Break" },
    { "ec", EC, "Send Telnet Erase Character" },
    { "el", EL, "Send Telnet Erase Line" },
    { "escape", SENDESCAPE, "Send current escape character" },
    { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
    { "ip", IP, "Send Telnet Interrupt Process" },
    { "nop", NOP, "Send Telnet 'No operation'" },
    { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
    { "?", SENDQUESTION, "Display send options" },
    { 0 }
};

static struct sendlist Sendlist2[] = {		/* some synonyms */
	{ "break", BREAK, 0 },

	{ "intp", IP, 0 },
	{ "interrupt", IP, 0 },
	{ "intr", IP, 0 },

	{ "help", SENDQUESTION, 0 },

	{ 0 }
};

initializeSendlist()
{
	Sendlist[0].help = gettxt("telnet:1", "Send Telnet Abort output");
	Sendlist[1].help = gettxt("telnet:2", "Send Telnet 'Are You There'");
	Sendlist[2].help = gettxt("telnet:3", "Send Telnet Break");
	Sendlist[3].help = gettxt("telnet:4", "Send Telnet Erase Character");
	Sendlist[4].help = gettxt("telnet:5", "Send Telnet Erase Line");
	Sendlist[5].help = gettxt("telnet:6", "Send current escape character");
	Sendlist[6].help = gettxt("telnet:7", "Send Telnet 'Go Ahead' sequence");
	Sendlist[7].help = gettxt("telnet:8", "Send Telnet Interrupt Process");
	Sendlist[8].help = gettxt("telnet:9", "Send Telnet 'No operation'");
	Sendlist[9].help = gettxt("telnet:10", "Perform Telnet 'Synch operation'");
	Sendlist[10].help = gettxt("telnet:11", "Display send options");
}

static char **
getnextsend(name)
char *name;
{
    struct sendlist *c = (struct sendlist *) name;

    return (char **) (c+1);
}

static struct sendlist *
getsend(name)
char *name;
{
    struct sendlist *sl;

    if ((sl = (struct sendlist *)
			genget(name, (char **) Sendlist, getnextsend)) != 0) {
	return sl;
    } else {
	return (struct sendlist *)
				genget(name, (char **) Sendlist2, getnextsend);
    }
}

static
sendcmd(argc, argv)
int	argc;
char	**argv;
{
    int what;		/* what we are sending this time */
    int count;		/* how many bytes we are going to need to send */
    int i;
    int question = 0;	/* was at least one argument a question */
    struct sendlist *s;	/* pointer to current command */

    if (argc < 2) {
	printf(gettxt("telnet:12", "need at least one argument for 'send' command\n"));
	printf(gettxt("telnet:13", "'send ?' for help\n"));
	return 0;
    }
    /*
     * First, validate all the send arguments.
     * In addition, we see how much space we are going to need, and
     * whether or not we will be doing a "SYNCH" operation (which
     * flushes the network queue).
     */
    count = 0;
    for (i = 1; i < argc; i++) {
	s = getsend(argv[i]);
	if (s == 0) {
	    printf(gettxt("telnet:14", "Unknown send argument '%s'\n'send ?' for help.\n"),
			argv[i]);
	    return 0;
	} else if (Ambiguous(s)) {
	    printf(gettxt("telnet:15", "Ambiguous send argument '%s'\n'send ?' for help.\n"),
			argv[i]);
	    return 0;
	}
	switch (s->what) {
	case SENDQUESTION:
	    break;
	case SENDESCAPE:
	    count += 1;
	    break;
	case SYNCH:
	    count += 2;
	    break;
	default:
	    count += 2;
	    break;
	}
    }
    /* Now, do we have enough room? */
    if (NETROOM() < count) {
	printf("There is not enough room in the buffer TO the network\n");
	printf("to process your request.  Nothing will be done.\n");
	printf("('send synch' will throw away most data in the network\n");
	printf("buffer, if this might help.)\n");
	return 0;
    }
    /* OK, they are all OK, now go through again and actually send */
    for (i = 1; i < argc; i++) {
	if ((s = getsend(argv[i])) == 0) {
	    fprintf(stderr, gettxt("telnet:16", "Telnet 'send' error - argument disappeared!\n"));
	    quit();
	    /*NOTREACHED*/
	}
	if (s->routine) {
	    (*s->routine)(s);
	} else {
	    switch (what = s->what) {
	    case SYNCH:
		dosynch();
		break;
	    case SENDQUESTION:
		for (s = Sendlist; s->name; s++) {
		    if (s->help) {
			printf(s->name);
			if (s->help) {
			    printf("\t%s", s->help);
			}
			printf("\n");
		    }
		}
		question = 1;
		break;
	    case SENDESCAPE:
		NETADD(escape);
		break;
	    default:
		NET2ADD(IAC, what);
		break;
	    }
	}
    }
    return !question;
}

/*
 * The following are the routines and data structures referred
 * to by the arguments to the "toggle" command.
 */

static
lclchars()
{
    donelclchars = 1;
    return 1;
}

static
togdebug()
{
#ifndef	NOT43
    if (net > 0 &&
	(SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
	    perror("setsockopt (SO_DEBUG)");
    }
#else	/* NOT43 */
    if (debug) {
	if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
	    perror("setsockopt (SO_DEBUG)");
    } else
	printf(gettxt("telnet:17", "Cannot turn off socket debugging\n"));
#endif	/* NOT43 */
    return 1;
}


static int
togcrlf()
{
    if (crlf) {
	printf(gettxt("telnet:18", "Will send carriage returns as telnet <CR><LF>.\n"));
    } else {
	printf(gettxt("telnet:19", "Will send carriage returns as telnet <CR><NUL>.\n"));
    }
    return 1;
}


static int
togbinary()
{
    donebinarytoggle = 1;

    if (myopts[TELOPT_BINARY] == 0) {	/* Go into binary mode */
	NET2ADD(IAC, DO);
	NETADD(TELOPT_BINARY);
	printoption("<SENT", doopt, TELOPT_BINARY, 0);
	NET2ADD(IAC, WILL);
	NETADD(TELOPT_BINARY);
	printoption("<SENT", doopt, TELOPT_BINARY, 0);
	hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 1;
	printf(gettxt("telnet:20", "Negotiating binary mode with remote host.\n"));
    } else {				/* Turn off binary mode */
	NET2ADD(IAC, DONT);
	NETADD(TELOPT_BINARY);
	printoption("<SENT", dont, TELOPT_BINARY, 0);
	NET2ADD(IAC, DONT);
	NETADD(TELOPT_BINARY);
	printoption("<SENT", dont, TELOPT_BINARY, 0);
	hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 0;
	printf(gettxt("telnet:21", "Negotiating network ascii mode with remote host.\n"));
    }
    return 1;
}


extern int togglehelp();

struct togglelist {
    char	*name;		/* name of toggle */
    char	*help;		/* help message */
    int		(*handler)();	/* routine to do actual setting */
    int		dohelp;		/* should we display help information */
    int		*variable;
    char	*actionexplanation;
};

static struct togglelist Togglelist[] = {
    { "autoflush",
	"toggle flushing of output when sending interrupt characters",
	    0,
		1,
		    &autoflush,
			"flush output when sending interrupt characters" },
    { "autosynch",
	"toggle automatic sending of interrupt characters in urgent mode",
	    0,
		1,
		    &autosynch,
			"send interrupt characters in urgent mode" },
    { "binary",
	"toggle sending and receiving of binary data",
	    togbinary,
		1,
		    0,
			0 },
    { "crlf",
	"toggle sending carriage returns as telnet <CR><LF>",
	    togcrlf,
		1,
		    &crlf,
			0 },
    { "crmod",
	"toggle mapping of received carriage returns",
	    0,
		1,
		    &crmod,
			"map carriage return on output" },
    { "localchars",
	"toggle local recognition of certain control characters",
	    lclchars,
		1,
		    &localchars,
			"recognize certain control characters" },
    { " ", "", 0, 1 },		/* empty line */
    { "debug",
	"(debugging) toggle debugging",
	    togdebug,
		1,
		    &debug,
			"turn on socket level debugging" },
    { "netdata",
	"(debugging) toggle printing of hexadecimal network data",
	    0,
		1,
		    &netdata,
			"print hexadecimal representation of network traffic" },
    { "options",
	"(debugging) toggle viewing of options processing",
	    0,
		1,
		    &showoptions,
			"show option processing" },
    { " ", "", 0, 1 },		/* empty line */
    { "?",
	"display help information",
	    togglehelp,
		1 },
    { "help",
	"display help information",
	    togglehelp,
		0 },
    { 0 }
};

initializeTogglelist()
{
	int i = 0;

	Togglelist[0].help = gettxt("telnet:22", "toggle flushing of output when sending interrupt characters");
	Togglelist[0].actionexplanation = gettxt("telnet:32", "flush output when sending interrupt characters");

	Togglelist[1].help = gettxt("telnet:23", "toggle automatic sending of interrupt characters in urgent mode");
	Togglelist[1].actionexplanation = gettxt("telnet:33", "send interrupt characters in urgent mode");

	Togglelist[2].help = gettxt("telnet:24", "toggle sending and receiving of binary data");
	Togglelist[2].actionexplanation = 0;

	Togglelist[3].help =	gettxt("telnet:25", "toggle sending carriage returns as telnet <CR><LF>");
	Togglelist[3].actionexplanation = 0;

	Togglelist[4].help = gettxt("telnet:26", "toggle mapping of received carriage returns");
	Togglelist[4].actionexplanation = gettxt("telnet:34", "map carriage return on output");

	Togglelist[5].help = gettxt("telnet:27", "toggle local recognition of certain control characters");
	Togglelist[5].actionexplanation = gettxt("telnet:35", "recognize certain control characters");

	Togglelist[6].help = "";
	Togglelist[6].actionexplanation = 0;

	Togglelist[7].help = gettxt("telnet:28", "(debugging) toggle debugging");
	Togglelist[7].actionexplanation = gettxt("telnet:36", "turn on socket level debugging");	

	Togglelist[8].help = gettxt("telnet:29", "(debugging) toggle printing of hexadecimal network data");
	Togglelist[8].actionexplanation = gettxt("telnet:37", "print hexadecimal representation of network traffic");

	Togglelist[9].help = gettxt("telnet:30", "(debugging) toggle viewing of options processing");
	Togglelist[9].actionexplanation = gettxt("telnet:38", "show option processing");

	Togglelist[10].help = "";
	Togglelist[11].help = gettxt("telnet:31", "display help information");
	Togglelist[12].help = gettxt("telnet:31", "display help information");
}

/* take away static to get rid of cfe warning ..*/
/* static*/
togglehelp()
{
    struct togglelist *c;

    for (c = Togglelist; c->name; c++) {
	if (c->dohelp) {
	    printf("%s\t%s\n", c->name, c->help);
	}
    }
    return 0;
}

static char **
getnexttoggle(name)
char *name;
{
    struct togglelist *c = (struct togglelist *) name;

    return (char **) (c+1);
}

static struct togglelist *
gettoggle(name)
char *name;
{
    return (struct togglelist *)
			genget(name, (char **) Togglelist, getnexttoggle);
}

static
toggle(argc, argv)
int	argc;
char	*argv[];
{
    int retval = 1;
    char *name;
    struct togglelist *c;

    if (argc < 2) {
	fprintf(stderr,
	    gettxt("telnet:39", "Need an argument to 'toggle' command.  'toggle ?' for help.\n"));
	return 0;
    }
    argc--;
    argv++;
    while (argc--) {
	name = *argv++;
	c = gettoggle(name);
	if (Ambiguous(c)) {
	    fprintf(stderr, gettxt("telnet:40", "'%s': ambiguous argument ('toggle ?' for help).\n"),
					name);
	    return 0;
	} else if (c == 0) {
	    fprintf(stderr, gettxt("telnet:41", "'%s': unknown argument ('toggle ?' for help).\n"),
					name);
	    return 0;
	} else {
	    if (c->variable) {
		*c->variable = !*c->variable;		/* invert it */
		if (c->actionexplanation) {
		    printf("%s %s.\n", *c->variable? gettxt("telnet:42", "Will") : gettxt("telnet:43", "Won't"),
							c->actionexplanation);
		}
		printf("%s %s.\n", *c->variable? gettxt("telnet:42", "Will") : gettxt("telnet:43", "Won't"),
							c->actionexplanation);
	    }
	    if (c->handler) {
		retval &= (*c->handler)(c);
	    }
	}
    }
    return retval;
}

/*
 * The following perform the "set" command.
 */

struct setlist {
    char *name;				/* name */
    char *help;				/* help information */
    char *charp;			/* where it is located at */
};

static struct setlist Setlist[] = {
    { "echo", 	"character to toggle local echoing on/off", &echoc },
    { "escape",	"character to escape back to telnet command mode", &escape },
    { " ", "" },
    { " ", "The following need 'localchars' to be toggled true", 0 },
    { "erase",	"character to cause an Erase Character", &termEraseChar },
    { "flushoutput", "character to cause an Abort Oubput", &termFlushChar },
    { "interrupt", "character to cause an Interrupt Process", &termIntChar },
    { "kill",	"character to cause an Erase Line", &termKillChar },
    { "quit",	"character to cause a Break", &termQuitChar },
    { "eof",	"character to cause an EOF ", &termEofChar },
    { 0 }
};

initializeSetlist()
{
	Setlist[0].help = gettxt("telnet:45", "character to toggle local echoing on/off");
	Setlist[1].help = gettxt("telnet:46", "character to escape back to telnet command mode");
	Setlist[2].help = "";
	Setlist[3].help = gettxt("telnet:47", "The following need 'localchars' to be toggled true");
	Setlist[4].help = gettxt("telnet:48", "character to cause an Erase Character");
	Setlist[5].help = gettxt("telnet:49", "character to cause an Abort Oubput");
	Setlist[6].help = gettxt("telnet:50", "character to cause an Interrupt Process");
	Setlist[7].help = gettxt("telnet:51", "character to cause an Erase Line");
	Setlist[8].help = gettxt("telnet:52", "character to cause a Break");
	Setlist[9].help = gettxt("telnet:44", "character to cause an EOF ");
}

static char **
getnextset(name)
char *name;
{
    struct setlist *c = (struct setlist *)name;

    return (char **) (c+1);
}

static struct setlist *
getset(name)
char *name;
{
    return (struct setlist *) genget(name, (char **) Setlist, getnextset);
}

static
setcmd(argc, argv)
int	argc;
char	*argv[];
{
    int value;
    struct setlist *ct;

    /* XXX back we go... sigh */
    if (argc != 3) {
	if ((argc == 2) &&
		    ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
	    for (ct = Setlist; ct->name; ct++) {
		printf("%s\t%s\n", ct->name, ct->help);
	    }
	    printf(gettxt("telnet:53", "?\tdisplay help information\n"));
	} else {
	    printf(gettxt("telnet:54", "Format is 'set Name Value'\n'set ?' for help.\n"));
	}
	return 0;
    }

    ct = getset(argv[1]);
    if (ct == 0) {
	fprintf(stderr, gettxt("telnet:55", "'%s': unknown argument ('set ?' for help).\n"),
			argv[1]);
	return 0;
    } else if (Ambiguous(ct)) {
	fprintf(stderr, gettxt("telnet:56", "'%s': ambiguous argument ('set ?' for help).\n"),
			argv[1]);
	return 0;
    } else {
	if (strcmp("off", argv[2])) {
	    value = special(argv[2]);
	} else {
	    value = -1;
	}
	*(ct->charp) = value;
	printf(gettxt("telnet:57", "%s character is '%s'.\n"), ct->name, control(*(ct->charp)));
    }
    return 1;
}

/*
 * The following are the data structures and routines for the
 * 'mode' command.
 */

static
dolinemode()
{
    if (hisopts[TELOPT_SGA]) {
	wontoption(TELOPT_SGA, 0);
    }
    if (hisopts[TELOPT_ECHO]) {
	wontoption(TELOPT_ECHO, 0);
    }
    return 1;
}

static
docharmode()
{
    if (!hisopts[TELOPT_SGA]) {
	willoption(TELOPT_SGA, 0);
    }
    if (!hisopts[TELOPT_ECHO]) {
	willoption(TELOPT_ECHO, 0);
    }
    return 1;
}

static Command Mode_commands[] = {
    { "character",	"character-at-a-time mode",	docharmode, 1, 1 },
    { "line",		"line-by-line mode",		dolinemode, 1, 1 },
    { 0 },
};

initializeModecommands()
{
	Mode_commands[0].help = gettxt("telnet:58", "character-at-a-time mode");
	Mode_commands[1].help = gettxt("telnet:59", "line-by-line mode");
}

static char **
getnextmode(name)
char *name;
{
    Command *c = (Command *) name;

    return (char **) (c+1);
}

static Command *
getmodecmd(name)
char *name;
{
    return (Command *) genget(name, (char **) Mode_commands, getnextmode);
}

static
modecmd(argc, argv)
int	argc;
char	*argv[];
{
    Command *mt;

    if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
	printf(gettxt("telnet:60", "format is:  'mode Mode', where 'Mode' is one of:\n\n"));
	for (mt = Mode_commands; mt->name; mt++) {
	    printf("%s\t%s\n", mt->name, mt->help);
	}
	return 0;
    }
    mt = getmodecmd(argv[1]);
    if (mt == 0) {
	fprintf(stderr, gettxt("telnet:61", "Unknown mode '%s' ('mode ?' for help).\n"), argv[1]);
	return 0;
    } else if (Ambiguous(mt)) {
	fprintf(stderr, gettxt("telnet:62", "Ambiguous mode '%s' ('mode ?' for help).\n"), argv[1]);
	return 0;
    } else {
	(*mt->handler)();
    }
    return 1;
}

/*
 * The following data structures and routines implement the
 * "display" command.
 */

static
display(argc, argv)
int	argc;
char	*argv[];
{
#define	dotog(tl)	if (tl->variable && tl->actionexplanation) { \
			    if (*tl->variable) { \
				printf("will"); \
			    } else { \
				printf("won't"); \
			    } \
			    printf(" %s.\n", tl->actionexplanation); \
			}

#define	doset(sl)   if (sl->name && *sl->name != ' ') { \
			printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
		    }

    struct togglelist *tl;
    struct setlist *sl;

    if (argc == 1) {
	for (tl = Togglelist; tl->name; tl++) {
	    dotog(tl);
	}
	printf("\n");
	for (sl = Setlist; sl->name; sl++) {
	    doset(sl);
	}
    } else {
	int i;

	for (i = 1; i < argc; i++) {
	    sl = getset(argv[i]);
	    tl = gettoggle(argv[i]);
	    if (Ambiguous(sl) || Ambiguous(tl)) {
		printf(gettxt("telnet:63", "?Ambiguous argument '%s'.\n"), argv[i]);
		return 0;
	    } else if (!sl && !tl) {
		printf(gettxt("telnet:64", "?Unknown argument '%s'.\n"), argv[i]);
		return 0;
	    } else {
		if (tl) {
		    dotog(tl);
		}
		if (sl) {
		    doset(sl);
		}
	    }
	}
    }
    return 1;
#undef	doset
#undef	dotog
}

/*
 * The following are the data structures, and many of the routines,
 * relating to command processing.
 */

/*
 * Set the escape character.
 */
static
setescape(argc, argv)
	int argc;
	char *argv[];
{
	register char *arg;
	char buf[50];

	printf(
	    gettxt("telnet:65", "Deprecated usage - please use 'set escape%s%s' in the future.\n"),
				(argc > 2)? gettxt("telnet:66", " "):gettxt("telnet:67", ""), (argc > 2)? argv[1]: gettxt("telnet:67", ""));
	if (argc > 2)
		arg = argv[1];
	else {
		printf(gettxt("telnet:68", "new escape character: "));
		gets(buf);
		arg = buf;
	}
	if (arg[0] != '\0')
		escape = arg[0];
	if (!In3270) {
		printf(gettxt("telnet:69", "Escape character is '%s'.\n"), control(escape));
	}
	fflush(stdout);
	return 1;
}

/*VARARGS*/
static
togcrmod()
{
    crmod = !crmod;
    printf(gettxt("telnet:70", "Deprecated usage - please use 'toggle crmod' in the future.\n"));
    printf(gettxt("telnet:71", "%s map carriage return on output.\n"), crmod ? gettxt("telnet:42", "Will") : gettxt("telnet:43", "Won't"));
    fflush(stdout);
    return 1;
}

/*VARARGS*/
suspend()
{
	setcommandmode();
#if	defined(unix)
	kill(0, SIGTSTP);
#endif	/* defined(unix) */
	/* reget parameters in case they were changed */
	TerminalSaveState();
	setconnmode();
	return 1;
}

/*VARARGS*/
static
bye(argc, argv)
int	argc;		/* Number of arguments */
char	*argv[];	/* arguments */
{
    if (connected) {
	shutdown(net, 2);
	printf(gettxt("telnet:72", "Connection closed.\n"));
	NetClose(net);
	connected = 0;
	/* reset options */
	tninit();
#if	defined(TN3270)
	SetIn3270();		/* Get out of 3270 mode */
#endif	/* defined(TN3270) */
    }
    if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
	longjmp(toplevel, 1);
	/* NOTREACHED */
    }
    return 1;			/* Keep lint, etc., happy */
}

/*VARARGS*/
quit()
{
	(void) call(bye, "bye", "fromquit", 0);
	Exit(0);
	/*NOTREACHED*/
	return 1;			/* just to keep lint happy */
}

/*
 * Print status about the connection.
 */
static
status(argc, argv)
int	argc;
char	*argv[];
{
    if (connected) {
	printf(gettxt("telnet:73", "Connected to %s.\n"), hostname);
	if (argc < 2) {
	    printf(gettxt("telnet:74", "Operating in %s.\n"),
				modelist[getconnmode()].modedescriptions);
	    if (localchars) {
		printf(gettxt("telnet:75", "Catching signals locally.\n"));
	    }
	}
    } else {
	printf(gettxt("telnet:76", "No connection.\n"));
    }
#   if !defined(TN3270)
    printf(gettxt("telnet:69", "Escape character is '%s'.\n"), control(escape));
    fflush(stdout);
#   else /* !defined(TN3270) */
    if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
	printf(gettxt("telnet:69", "Escape character is '%s'.\n"), control(escape));
    }
#   if defined(unix)
    if (In3270 && transcom) {
       printf(gettxt("telnet:77", "Transparent mode command is '%s'.\n"), transcom);
    }
#   endif /* defined(unix) */
    fflush(stdout);
    if (In3270) {
	return 0;
    }
#   endif /* defined(TN3270) */
    return 1;
}



int
tn(argc, argv)
	int argc;
	char *argv[];
{
    register struct hostent *host = 0;
    struct sockaddr_in sin;
    struct servent *sp = 0;
    static char	hnamebuf[32];


#if defined(MSDOS)
    char *cp;
#endif	/* defined(MSDOS) */

    if (connected) {
	printf(gettxt("telnet:78", "?Already connected to %s\n"), hostname);
	return 0;
    }
    if (argc < 2) {
	(void) strcpy(line, "Connect ");
	printf(gettxt("telnet:79", "(to) "));
	gets(&line[strlen(line)]);
	makeargv();
	argc = margc;
	argv = margv;
    }
    if ((argc < 2) || (argc > 3)) {
	printf(gettxt("telnet:80", "usage: %s host-name [port]\n"), argv[0]);
	return 0;
    }
#if	defined(MSDOS)
    for (cp = argv[1]; *cp; cp++) {
	if (isupper(*cp)) {
	    *cp = tolower(*cp);
	}
    }
#endif	/* defined(MSDOS) */
    sin.sin_addr.s_addr = inet_addr(argv[1]);
    if (sin.sin_addr.s_addr != -1) {
	sin.sin_family = AF_INET;
	(void) strcpy(hnamebuf, argv[1]);
	hostname = hnamebuf;
    } else {
	host = gethostbyname(argv[1]);
	if (host) {
	    sin.sin_family = host->h_addrtype;
#if	defined(h_addr)		/* In 4.3, this is a #define */
	    memcpy((caddr_t)&sin.sin_addr,
				host->h_addr_list[0], host->h_length);
#else	/* defined(h_addr) */
	    memcpy((caddr_t)&sin.sin_addr, host->h_addr, host->h_length);
#endif	/* defined(h_addr) */
	    hostname = host->h_name;
	} else {
	    printf(gettxt("telnet:81", "%s: unknown host\n"), argv[1]);
	    return 0;
	}
    }
    if (argc == 3) {
	sin.sin_port = atoi(argv[2]);
	if (sin.sin_port == 0) {
	    sp = getservbyname(argv[2], "tcp");
	    if (sp)
		sin.sin_port = sp->s_port;
	    else {
		printf(gettxt("telnet:82", "%s: bad port number\n"), argv[2]);
		return 0;
	    }
	} else {
	    sin.sin_port = atoi(argv[2]);
	    sin.sin_port = htons(sin.sin_port);
	}
	telnetport = 0;
    } else {
	if (sp == 0) {
	    sp = getservbyname("telnet", "tcp");
	    if (sp == 0) {
		fprintf(stderr, gettxt("telnet:83", "telnet: tcp/telnet: unknown service\n"),1);
		return 0;
	    }
	    sin.sin_port = sp->s_port;
	}
	telnetport = 1;
    }
    printf(gettxt("telnet:84", "Trying...\n"));
    do {
	net = socket(AF_INET, SOCK_STREAM, 0);
	if (net < 0) {
	    perror("telnet: socket");
	    return 0;
	}
	if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
		perror("setsockopt (SO_DEBUG)");
	}

	if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
#if	defined(h_addr)		/* In 4.3, this is a #define */
	    if (host && host->h_addr_list[1]) {
		int oerrno = errno;

		fprintf(stderr, gettxt("telnet:85", "telnet: connect to address %s: "),
						inet_ntoa(sin.sin_addr));
		errno = oerrno;
#ifdef RISCOS
		perror("");

#else
		perror((char *)0);
#endif RISCOS
		host->h_addr_list++;
		memcpy((caddr_t)&sin.sin_addr, 
			host->h_addr_list[0], host->h_length);
		fprintf(stderr, gettxt("telnet:86", "Trying %s...\n"),
			inet_ntoa(sin.sin_addr));
		(void) NetClose(net);
		continue;
	    }
#endif	/* defined(h_addr) */
	    perror(gettxt("telnet:87", "telnet: Unable to connect to remote host"));
	    return 0;
	    }
	connected++;
    } while (connected == 0);
    call(status, "status", "notmuch", 0);
    if (setjmp(peerdied) == 0)
	telnet();
    NetClose(net);
    ExitString(gettxt("telnet:88", "Connection closed by foreign host.\n"),1);
    /*NOTREACHED*/
}


#define HELPINDENT (sizeof ("connect"))

static char
	*openhelp =	"connect to a site",
	*closehelp =	"close current connection",
	*quithelp =	"exit telnet",
	*statushelp =	"print status information",
	*helphelp =	"print help information",
	*sendhelp =	"transmit special characters ('send ?' for more)",
	*sethelp = 	"set operating parameters ('set ?' for more)",
	*togglestring ="toggle operating parameters ('toggle ?' for more)",
	*displayhelp =	"display operating parameters",
#if	defined(TN3270) && defined(unix)
	*transcomhelp = "specify Unix command for transparent mode pipe",
#endif	/* defined(TN3270) && defined(unix) */
#if	defined(unix)
	*zhelp =	"suspend telnet",
#endif	/* defined(unix */
#if	defined(TN3270)
	*shellhelp =	"invoke a subshell",
#endif	/* defined(TN3270) */
	*modehelp = "try to enter line-by-line or character-at-a-time mode";

extern int	help(), shell(); 

static Command cmdtab[] = {
	{ "close",	0,	bye,		1, 1 },
	{ "display",	0,	display,	1, 0 },
	{ "mode",	0,	modecmd,	1, 1 },
	{ "open",	0,	tn,		1, 0 },
	{ "quit",	0,	quit,		1, 0 },
	{ "send",	0,	sendcmd,	1, 1 },
	{ "set",	0,	setcmd,		1, 0 },
	{ "status",	0,	status,		1, 0 },
	{ "toggle",	0,	toggle,		1, 0 },
#if	defined(TN3270) && defined(unix)
	{ "transcom",	0,	settranscom,	1, 0 },
#endif	/* defined(TN3270) && defined(unix) */
#if	defined(unix)
	{ "z",		0,		suspend,	1, 0 },
#endif	/* defined(unix) */
#if	defined(TN3270)
	{ "!",		0,	shell,		1, 1 },
#endif	/* defined(TN3270) */
	{ "?",		0,	help,		1, 0 },
	0
};

static char	*crmodhelp =	"deprecated command -- use 'toggle crmod' instead";
static char	*escapehelp =	"deprecated command -- use 'set escape' instead";

static Command cmdtab2[] = {
	{ "help",	0,	help,		0, 0 },
	{ "escape",	0,	setescape,	1, 0 },
	{ "crmod",	0,	togcrmod,	1, 0 },
	0
};

initializeCmdtab()
{
	int i = 0;

	openhelp =	gettxt("telnet:89", "connect to a site");
	closehelp =	gettxt("telnet:90", "close current connection");
	quithelp =	gettxt("telnet:91", "exit telnet");
	statushelp =	gettxt("telnet:92", "print status information");
	helphelp =	gettxt("telnet:93", "print help information");
	sendhelp =	gettxt("telnet:94", "transmit special characters ('send ?' for more)");
	sethelp = 	gettxt("telnet:95", "set operating parameters ('set ?' for more)");
	togglestring =gettxt("telnet:96", "toggle operating parameters ('toggle ?' for more)");
	displayhelp =	gettxt("telnet:97", "display operating parameters");
#if	defined(TN3270) && defined(unix)
	transcomhelp = gettxt("telnet:98", "specify Unix command for transparent mode pipe");
#endif	/* defined(TN3270) && defined(unix) */
#if	defined(unix)
	zhelp =	gettxt("telnet:99", "suspend telnet");
#endif	/* defined(unix */
#if	defined(TN3270)
	shellhelp =	gettxt("telnet:100", "invoke a subshell");
#endif	/* defined(TN3270) */
	modehelp = gettxt("telnet:101", "try to enter line-by-line or character-at-a-time mode");

	cmdtab[i++].help = closehelp;
	cmdtab[i++].help = displayhelp;
	cmdtab[i++].help = modehelp;
	cmdtab[i++].help = openhelp;
	cmdtab[i++].help = quithelp;
	cmdtab[i++].help = sendhelp;
	cmdtab[i++].help = sethelp;
	cmdtab[i++].help = statushelp;
	cmdtab[i++].help = togglestring;
#if	defined(TN3270) && defined(unix)
	cmdtab[i++].help = transcomhelp;
#endif	/* defined(TN3270) && defined(unix) */
#if	defined(unix)
	cmdtab[i++].help = zhelp;
#endif	/* defined(unix) */
#if	defined(TN3270)
	cmdtab[i++].help = shellhelp;
#endif	/* defined(TN3270) */
	cmdtab[i++].help = helphelp;

	crmodhelp =	gettxt("telnet:102", "deprecated command -- use 'toggle crmod' instead");
	escapehelp =	gettxt("telnet:103", "deprecated command -- use 'set escape' instead");

	cmdtab2[0].help = helphelp;
	cmdtab2[1].help = escapehelp;
	cmdtab2[2].help = crmodhelp;
}

/*
 * Call routine with argc, argv set from args (terminated by 0).
 * VARARGS2
 */
static
#ifdef RISCOS
call(routine, va_alist)
	int (*routine)();
	va_dcl
{
	register int 	argc = 0;
	va_list		ap, sp;

	va_start(ap);
	sp = ap;
	while (va_arg(sp, char *))
		argc++;
	(*routine)(argc, ap);
}
#else
call(routine, args)
	int (*routine)();
	char *args;
{
	register char **argp;
	register int argc;

	for (argc = 0, argp = &args; *argp++ != 0; argc++)
		;
	return (*routine)(argc, &args);
}
#endif /* RISCOS */

static char **
getnextcmd(name)
char *name;
{
    Command *c = (Command *) name;

    return (char **) (c+1);
}

static Command *
getcmd(name)
char *name;
{
    Command *cm;

    if ((cm = (Command *) genget(name, (char **) cmdtab, getnextcmd)) != 0) {
	return cm;
    } else {
	return (Command *) genget(name, (char **) cmdtab2, getnextcmd);
    }
}

void
command(top)
	int top;
{
    register Command *c;

    setcommandmode();
    if (!top) {
	putchar('\n');
    } else {
#if	defined(unix)
	signal(SIGINT, SIG_DFL);
	signal(SIGQUIT, SIG_DFL);
#endif	/* defined(unix) */
    }
    for (;;) {
	printf("%s> ", prompt);
	if (gets(line) == NULL) {
	    if (feof(stdin) || ferror(stdin))
		quit();
	    break;
	}
	if (line[0] == 0)
	    break;
	makeargv();
	c = getcmd(margv[0]);
	if (Ambiguous(c)) {
	    printf(gettxt("telnet:104", "?Ambiguous command\n"));
	    continue;
	}
	if (c == 0) {
	    printf(gettxt("telnet:105", "?Invalid command\n"));
	    continue;
	}
	if (c->needconnect && !connected) {
	    printf(gettxt("telnet:106", "?Need to be connected first.\n"));
	    continue;
	}
	if ((*c->handler)(margc, margv)) {
	    break;
	}
    }
    if (!top) {
	if (!connected) {
	    longjmp(toplevel, 1);
	    /*NOTREACHED*/
	}
#if	defined(TN3270)
	if (shell_active == 0) {
	    setconnmode();
	}
#else	/* defined(TN3270) */
	setconnmode();
#endif	/* defined(TN3270) */
    }
}

/*
 * Help command.
 */
/* take away static to get rid of cfe warning ..*/
/* static */
help(argc, argv)
	int argc;
	char *argv[];
{
	register Command *c;

	if (argc == 1) {
		printf(gettxt("telnet:107", "Commands may be abbreviated.  Commands are:\n\n"));
		for (c = cmdtab; c->name; c++)
			if (c->dohelp) {
				printf("%-*s\t%s\n", HELPINDENT, c->name,
								    c->help);
			}
		return 0;
	}
	while (--argc > 0) {
		register char *arg;
		arg = *++argv;
		c = getcmd(arg);
		if (Ambiguous(c))
			printf(gettxt("telnet:108", "?Ambiguous help command %s\n"), arg);
		else if (c == (Command *)0)
			printf(gettxt("telnet:109", "?Invalid help command %s\n"), arg);
		else
			printf("%s\n", c->help);
	}
	return 0;
}
