xref: /openbsd/usr.bin/telnet/commands.c (revision cecf84d4)
1 /*	$OpenBSD: commands.c,v 1.71 2015/01/16 06:40:13 deraadt Exp $	*/
2 /*	$NetBSD: commands.c,v 1.14 1996/03/24 22:03:48 jtk Exp $	*/
3 
4 /*
5  * Copyright (c) 1988, 1990, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include "telnet_locl.h"
34 
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <netinet/ip.h>
38 #include <arpa/inet.h>
39 #include <arpa/telnet.h>
40 
41 #include <ctype.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <netdb.h>
45 #include <pwd.h>
46 #include <stdarg.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <limits.h>
51 
52 #ifdef SKEY
53 #include <sys/wait.h>
54 #define PATH_SKEY	"/usr/bin/skey"
55 #endif
56 
57 static unsigned long sourceroute(char *arg, char **cpp, int *lenp);
58 
59 int tos = -1;
60 
61 char	*hostname;
62 
63 typedef struct {
64 	char	*name;		/* command name */
65 	char	*help;		/* help string (NULL for no help) */
66 	int	(*handler)(int, char **);/* routine which executes command */
67 	int	needconnect;	/* Do we need to be connected to execute? */
68 } Command;
69 
70 static char line[256];
71 static char saveline[256];
72 static int margc;
73 static char *margv[20];
74 
75 #ifdef SKEY
76 int
77 skey_calc(int argc, char **argv)
78 {
79 	int status;
80 
81 	if(argc != 3) {
82 		printf("usage: %s sequence challenge\n", argv[0]);
83 		return 0;
84 	}
85 
86 	switch(fork()) {
87 	case 0:
88 		execv(PATH_SKEY, argv);
89 		exit (1);
90 	case -1:
91 		err(1, "fork");
92 		break;
93 	default:
94 		(void) wait(&status);
95 		if (WIFEXITED(status))
96 			return (WEXITSTATUS(status));
97 		return (0);
98 	}
99 }
100 #endif
101 
102 static void
103 makeargv(void)
104 {
105     char *cp, *cp2, c;
106     char **argp = margv;
107 
108     margc = 0;
109     cp = line;
110     if (*cp == '!') {		/* Special case shell escape */
111 	strlcpy(saveline, line, sizeof(saveline)); /* save for shell command */
112 	*argp++ = "!";		/* No room in string to get this */
113 	margc++;
114 	cp++;
115     }
116     while ((c = *cp)) {
117 	int inquote = 0;
118 	while (isspace((unsigned char)c))
119 	    c = *++cp;
120 	if (c == '\0')
121 	    break;
122 	*argp++ = cp;
123 	margc += 1;
124 	for (cp2 = cp; c != '\0'; c = *++cp) {
125 	    if (inquote) {
126 		if (c == inquote) {
127 		    inquote = 0;
128 		    continue;
129 		}
130 	    } else {
131 		if (c == '\\') {
132 		    if ((c = *++cp) == '\0')
133 			break;
134 		} else if (c == '"') {
135 		    inquote = '"';
136 		    continue;
137 		} else if (c == '\'') {
138 		    inquote = '\'';
139 		    continue;
140 		} else if (isspace((unsigned char)c))
141 		    break;
142 	    }
143 	    *cp2++ = c;
144 	}
145 	*cp2 = '\0';
146 	if (c == '\0')
147 	    break;
148 	cp++;
149     }
150     *argp++ = 0;
151 }
152 
153 /*
154  * Make a character string into a number.
155  *
156  * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
157  */
158 
159 static char
160 special(char *s)
161 {
162 	char c;
163 	char b;
164 
165 	switch (*s) {
166 	case '^':
167 		b = *++s;
168 		if (b == '?') {
169 		    c = b | 0x40;		/* DEL */
170 		} else {
171 		    c = b & 0x1f;
172 		}
173 		break;
174 	default:
175 		c = *s;
176 		break;
177 	}
178 	return c;
179 }
180 
181 /*
182  * Construct a control character sequence
183  * for a special character.
184  */
185 static char *
186 control(cc_t c)
187 {
188 	static char buf[5];
189 	/*
190 	 * The only way I could get the Sun 3.5 compiler
191 	 * to shut up about
192 	 *	if ((unsigned int)c >= 0x80)
193 	 * was to assign "c" to an unsigned int variable...
194 	 * Arggg....
195 	 */
196 	unsigned int uic = (unsigned int)c;
197 
198 	if (uic == 0x7f)
199 		return ("^?");
200 	if (c == (cc_t)_POSIX_VDISABLE) {
201 		return "off";
202 	}
203 	if (uic >= 0x80) {
204 		buf[0] = '\\';
205 		buf[1] = ((c>>6)&07) + '0';
206 		buf[2] = ((c>>3)&07) + '0';
207 		buf[3] = (c&07) + '0';
208 		buf[4] = 0;
209 	} else if (uic >= 0x20) {
210 		buf[0] = c;
211 		buf[1] = 0;
212 	} else {
213 		buf[0] = '^';
214 		buf[1] = '@'+c;
215 		buf[2] = 0;
216 	}
217 	return (buf);
218 }
219 
220 /*
221  *	The following are data structures and routines for
222  *	the "send" command.
223  *
224  */
225 
226 struct sendlist {
227     char	*name;		/* How user refers to it (case independent) */
228     char	*help;		/* Help information (0 ==> no help) */
229     int		needconnect;	/* Need to be connected */
230     int		narg;		/* Number of arguments */
231     int		(*handler)();	/* Routine to perform (for special ops) */
232     int		nbyte;		/* Number of bytes to send this command */
233     int		what;		/* Character to be sent (<0 ==> special) */
234 };
235 
236 
237 static int
238 	send_esc(void),
239 	send_help(void),
240 	send_docmd(char *),
241 	send_dontcmd(char *),
242 	send_willcmd(char *),
243 	send_wontcmd(char *);
244 
245 static struct sendlist Sendlist[] = {
246     { "ao",	"Send Telnet Abort output",		1, 0, 0, 2, AO },
247     { "ayt",	"Send Telnet 'Are You There'",		1, 0, 0, 2, AYT },
248     { "brk",	"Send Telnet Break",			1, 0, 0, 2, BREAK },
249     { "break",	0,					1, 0, 0, 2, BREAK },
250     { "ec",	"Send Telnet Erase Character",		1, 0, 0, 2, EC },
251     { "el",	"Send Telnet Erase Line",		1, 0, 0, 2, EL },
252     { "escape",	"Send current escape character",	1, 0, send_esc, 1, 0 },
253     { "ga",	"Send Telnet 'Go Ahead' sequence",	1, 0, 0, 2, GA },
254     { "ip",	"Send Telnet Interrupt Process",	1, 0, 0, 2, IP },
255     { "intp",	0,					1, 0, 0, 2, IP },
256     { "interrupt", 0,					1, 0, 0, 2, IP },
257     { "intr",	0,					1, 0, 0, 2, IP },
258     { "nop",	"Send Telnet 'No operation'",		1, 0, 0, 2, NOP },
259     { "eor",	"Send Telnet 'End of Record'",		1, 0, 0, 2, EOR },
260     { "abort",	"Send Telnet 'Abort Process'",		1, 0, 0, 2, ABORT },
261     { "susp",	"Send Telnet 'Suspend Process'",	1, 0, 0, 2, SUSP },
262     { "eof",	"Send Telnet End of File Character",	1, 0, 0, 2, xEOF },
263     { "synch",	"Perform Telnet 'Synch operation'",	1, 0, dosynch, 2, 0 },
264     { "getstatus", "Send request for STATUS",		1, 0, get_status, 6, 0 },
265     { "?",	"Display send options",			0, 0, send_help, 0, 0 },
266     { "help",	0,					0, 0, send_help, 0, 0 },
267     { "do",	0,					0, 1, send_docmd, 3, 0 },
268     { "dont",	0,					0, 1, send_dontcmd, 3, 0 },
269     { "will",	0,					0, 1, send_willcmd, 3, 0 },
270     { "wont",	0,					0, 1, send_wontcmd, 3, 0 },
271     { 0 }
272 };
273 
274 #define	GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \
275 				sizeof(struct sendlist)))
276 
277 static int
278 sendcmd(int argc, char **argv)
279 {
280     int count;		/* how many bytes we are going to need to send */
281     int i;
282     struct sendlist *s;	/* pointer to current command */
283     int success = 0;
284     int needconnect = 0;
285 
286     if (argc < 2) {
287 	printf("need at least one argument for 'send' command\r\n");
288 	printf("'send ?' for help\r\n");
289 	return 0;
290     }
291     /*
292      * First, validate all the send arguments.
293      * In addition, we see how much space we are going to need, and
294      * whether or not we will be doing a "SYNCH" operation (which
295      * flushes the network queue).
296      */
297     count = 0;
298     for (i = 1; i < argc; i++) {
299 	s = GETSEND(argv[i]);
300 	if (s == 0) {
301 	    printf("Unknown send argument '%s'\r\n'send ?' for help.\r\n",
302 			argv[i]);
303 	    return 0;
304 	} else if (Ambiguous(s)) {
305 	    printf("Ambiguous send argument '%s'\r\n'send ?' for help.\r\n",
306 			argv[i]);
307 	    return 0;
308 	}
309 	if (i + s->narg >= argc) {
310 	    fprintf(stderr,
311 	    "Need %d argument%s to 'send %s' command.  'send %s ?' for help.\r\n",
312 		s->narg, s->narg == 1 ? "" : "s", s->name, s->name);
313 	    return 0;
314 	}
315 	count += s->nbyte;
316 	if (s->handler == send_help) {
317 	    send_help();
318 	    return 0;
319 	}
320 
321 	i += s->narg;
322 	needconnect += s->needconnect;
323     }
324     if (!connected && needconnect) {
325 	printf("?Need to be connected first.\r\n");
326 	printf("'send ?' for help\r\n");
327 	return 0;
328     }
329     /* Now, do we have enough room? */
330     if (NETROOM() < count) {
331 	printf("There is not enough room in the buffer TO the network\r\n");
332 	printf("to process your request.  Nothing will be done.\r\n");
333 	printf("('send synch' will throw away most data in the network\r\n");
334 	printf("buffer, if this might help.)\r\n");
335 	return 0;
336     }
337     /* OK, they are all OK, now go through again and actually send */
338     count = 0;
339     for (i = 1; i < argc; i++) {
340 	if ((s = GETSEND(argv[i])) == 0) {
341 	    fprintf(stderr, "Telnet 'send' error - argument disappeared!\r\n");
342 	    quit();
343 	}
344 	if (s->handler) {
345 	    count++;
346 	    success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0,
347 				  (s->narg > 1) ? argv[i+2] : 0);
348 	    i += s->narg;
349 	} else {
350 	    NET2ADD(IAC, s->what);
351 	    printoption("SENT", IAC, s->what);
352 	}
353     }
354     return (count == success);
355 }
356 
357 static int send_tncmd(void (*func)(int, int), char *cmd, char *name);
358 
359 static int
360 send_esc(void)
361 {
362     NETADD(escape);
363     return 1;
364 }
365 
366 static int
367 send_docmd(char *name)
368 {
369     return(send_tncmd(send_do, "do", name));
370 }
371 
372 static int
373 send_dontcmd(char *name)
374 {
375     return(send_tncmd(send_dont, "dont", name));
376 }
377 
378 static int
379 send_willcmd(char *name)
380 {
381     return(send_tncmd(send_will, "will", name));
382 }
383 
384 static int
385 send_wontcmd(char *name)
386 {
387     return(send_tncmd(send_wont, "wont", name));
388 }
389 
390 int
391 send_tncmd(void (*func)(int, int), char *cmd, char *name)
392 {
393     char **cpp;
394     extern char *telopts[];
395     int val = 0;
396 
397     if (isprefix(name, "help") || isprefix(name, "?")) {
398 	int col, len;
399 
400 	printf("Usage: send %s <value|option>\r\n", cmd);
401 	printf("\"value\" must be from 0 to 255\r\n");
402 	printf("Valid options are:\r\n\t");
403 
404 	col = 8;
405 	for (cpp = telopts; *cpp; cpp++) {
406 	    len = strlen(*cpp) + 3;
407 	    if (col + len > 65) {
408 		printf("\r\n\t");
409 		col = 8;
410 	    }
411 	    printf(" \"%s\"", *cpp);
412 	    col += len;
413 	}
414 	printf("\r\n");
415 	return 0;
416     }
417     cpp = (char **)genget(name, telopts, sizeof(char *));
418     if (Ambiguous(cpp)) {
419 	fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\r\n",
420 					name, cmd);
421 	return 0;
422     }
423     if (cpp) {
424 	val = cpp - telopts;
425     } else {
426 	char *cp = name;
427 
428 	while (*cp >= '0' && *cp <= '9') {
429 	    val *= 10;
430 	    val += *cp - '0';
431 	    cp++;
432 	}
433 	if (*cp != 0) {
434 	    fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\r\n",
435 					name, cmd);
436 	    return 0;
437 	} else if (val < 0 || val > 255) {
438 	    fprintf(stderr, "'%s': bad value ('send %s ?' for help).\r\n",
439 					name, cmd);
440 	    return 0;
441 	}
442     }
443     if (!connected) {
444 	printf("?Need to be connected first.\r\n");
445 	return 0;
446     }
447     (*func)(val, 1);
448     return 1;
449 }
450 
451 static int
452 send_help(void)
453 {
454     struct sendlist *s;	/* pointer to current command */
455     for (s = Sendlist; s->name; s++) {
456 	if (s->help)
457 	    printf("%-15s %s\r\n", s->name, s->help);
458     }
459     return(0);
460 }
461 
462 /*
463  * The following are the routines and data structures referred
464  * to by the arguments to the "toggle" command.
465  */
466 
467 static int
468 lclchars(int unused)
469 {
470     donelclchars = 1;
471     return 1;
472 }
473 
474 static int
475 togdebug(int unused)
476 {
477     if (net > 0 &&
478 	(setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug, sizeof(debug))) == -1) {
479 	    perror("setsockopt (SO_DEBUG)");
480     }
481     return 1;
482 }
483 
484 static int
485 togcrlf(int unused)
486 {
487     if (crlf) {
488 	printf("Will send carriage returns as telnet <CR><LF>.\r\n");
489     } else {
490 	printf("Will send carriage returns as telnet <CR><NUL>.\r\n");
491     }
492     return 1;
493 }
494 
495 int binmode;
496 
497 static int
498 togbinary(int val)
499 {
500     donebinarytoggle = 1;
501 
502     if (val >= 0) {
503 	binmode = val;
504     } else {
505 	if (my_want_state_is_will(TELOPT_BINARY) &&
506 				my_want_state_is_do(TELOPT_BINARY)) {
507 	    binmode = 1;
508 	} else if (my_want_state_is_wont(TELOPT_BINARY) &&
509 				my_want_state_is_dont(TELOPT_BINARY)) {
510 	    binmode = 0;
511 	}
512 	val = binmode ? 0 : 1;
513     }
514 
515     if (val == 1) {
516 	if (my_want_state_is_will(TELOPT_BINARY) &&
517 					my_want_state_is_do(TELOPT_BINARY)) {
518 	    printf("Already operating in binary mode with remote host.\r\n");
519 	} else {
520 	    printf("Negotiating binary mode with remote host.\r\n");
521 	    tel_enter_binary(3);
522 	}
523     } else {
524 	if (my_want_state_is_wont(TELOPT_BINARY) &&
525 					my_want_state_is_dont(TELOPT_BINARY)) {
526 	    printf("Already in network ascii mode with remote host.\r\n");
527 	} else {
528 	    printf("Negotiating network ascii mode with remote host.\r\n");
529 	    tel_leave_binary(3);
530 	}
531     }
532     return 1;
533 }
534 
535 static int
536 togrbinary(int val)
537 {
538     donebinarytoggle = 1;
539 
540     if (val == -1)
541 	val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1;
542 
543     if (val == 1) {
544 	if (my_want_state_is_do(TELOPT_BINARY)) {
545 	    printf("Already receiving in binary mode.\r\n");
546 	} else {
547 	    printf("Negotiating binary mode on input.\r\n");
548 	    tel_enter_binary(1);
549 	}
550     } else {
551 	if (my_want_state_is_dont(TELOPT_BINARY)) {
552 	    printf("Already receiving in network ascii mode.\r\n");
553 	} else {
554 	    printf("Negotiating network ascii mode on input.\r\n");
555 	    tel_leave_binary(1);
556 	}
557     }
558     return 1;
559 }
560 
561 static int
562 togxbinary(int val)
563 {
564     donebinarytoggle = 1;
565 
566     if (val == -1)
567 	val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1;
568 
569     if (val == 1) {
570 	if (my_want_state_is_will(TELOPT_BINARY)) {
571 	    printf("Already transmitting in binary mode.\r\n");
572 	} else {
573 	    printf("Negotiating binary mode on output.\r\n");
574 	    tel_enter_binary(2);
575 	}
576     } else {
577 	if (my_want_state_is_wont(TELOPT_BINARY)) {
578 	    printf("Already transmitting in network ascii mode.\r\n");
579 	} else {
580 	    printf("Negotiating network ascii mode on output.\r\n");
581 	    tel_leave_binary(2);
582 	}
583     }
584     return 1;
585 }
586 
587 
588 static int togglehelp(int);
589 
590 struct togglelist {
591     char	*name;			/* name of toggle */
592     char	*help;			/* help message */
593     int		(*handler)(int);	/* routine to do actual setting */
594     int		*variable;
595     char	*actionexplanation;
596     int		needconnect;	/* Need to be connected */
597 };
598 
599 static struct togglelist Togglelist[] = {
600     { "autoflush",
601 	"flushing of output when sending interrupt characters",
602 	    0,
603 		&autoflush,
604 		    "flush output when sending interrupt characters" },
605     { "autosynch",
606 	"automatic sending of interrupt characters in urgent mode",
607 	    0,
608 		&autosynch,
609 		    "send interrupt characters in urgent mode" },
610     { "autologin",
611 	"automatic sending of login name",
612 	    0,
613 		&autologin,
614 		    "send login name" },
615     { "skiprc",
616 	"don't read ~/.telnetrc file",
617 	    0,
618 		&skiprc,
619 		    "skip reading of ~/.telnetrc file" },
620     { "binary",
621 	"sending and receiving of binary data",
622 	    togbinary,
623 		0,
624 		    0 },
625     { "inbinary",
626 	"receiving of binary data",
627 	    togrbinary,
628 		0,
629 		    0 },
630     { "outbinary",
631 	"sending of binary data",
632 	    togxbinary,
633 		0,
634 		    0 },
635     { "crlf",
636 	"sending carriage returns as telnet <CR><LF>",
637 	    togcrlf,
638 		&crlf,
639 		    0 },
640     { "crmod",
641 	"mapping of received carriage returns",
642 	    0,
643 		&crmod,
644 		    "map carriage return on output" },
645     { "localchars",
646 	"local recognition of certain control characters",
647 	    lclchars,
648 		&localchars,
649 		    "recognize certain control characters" },
650     { " ", "", 0, 0 },		/* empty line */
651     { "debug",
652 	"debugging",
653 	    togdebug,
654 		&debug,
655 		    "turn on socket level debugging" },
656     { "netdata",
657 	"printing of hexadecimal network data (debugging)",
658 	    0,
659 		&netdata,
660 		    "print hexadecimal representation of network traffic" },
661     { "prettydump",
662 	"output of \"netdata\" to user readable format (debugging)",
663 	    0,
664 		&prettydump,
665 		    "print user readable output for \"netdata\"" },
666     { "options",
667 	"viewing of options processing (debugging)",
668 	    0,
669 		&showoptions,
670 		    "show option processing" },
671     { "termdata",
672 	"(debugging) toggle printing of hexadecimal terminal data",
673 	    0,
674 		&termdata,
675 		    "print hexadecimal representation of terminal traffic" },
676     { "?",
677 	0,
678 	    togglehelp },
679     { "help",
680 	0,
681 	    togglehelp },
682     { 0 }
683 };
684 
685 static int
686 togglehelp(int unused)
687 {
688     struct togglelist *c;
689 
690     for (c = Togglelist; c->name; c++) {
691 	if (c->help) {
692 	    if (*c->help)
693 		printf("%-15s toggle %s\r\n", c->name, c->help);
694 	    else
695 		printf("\r\n");
696 	}
697     }
698     printf("\r\n");
699     printf("%-15s %s\r\n", "?", "display help information");
700     return 0;
701 }
702 
703 static void
704 settogglehelp(int set)
705 {
706     struct togglelist *c;
707 
708     for (c = Togglelist; c->name; c++) {
709 	if (c->help) {
710 	    if (*c->help)
711 		printf("%-15s %s %s\r\n", c->name, set ? "enable" : "disable",
712 						c->help);
713 	    else
714 		printf("\r\n");
715 	}
716     }
717 }
718 
719 #define	GETTOGGLE(name) (struct togglelist *) \
720 		genget(name, (char **) Togglelist, sizeof(struct togglelist))
721 
722 static int
723 toggle(int argc, char *argv[])
724 {
725     int retval = 1;
726     char *name;
727     struct togglelist *c;
728 
729     if (argc < 2) {
730 	fprintf(stderr,
731 	    "Need an argument to 'toggle' command.  'toggle ?' for help.\r\n");
732 	return 0;
733     }
734     argc--;
735     argv++;
736     while (argc--) {
737 	name = *argv++;
738 	c = GETTOGGLE(name);
739 	if (Ambiguous(c)) {
740 	    fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\r\n",
741 					name);
742 	    return 0;
743 	} else if (c == 0) {
744 	    fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\r\n",
745 					name);
746 	    return 0;
747 	} else if (!connected && c->needconnect) {
748 	    printf("?Need to be connected first.\r\n");
749 	    printf("'send ?' for help\r\n");
750 	    return 0;
751 	} else {
752 	    if (c->variable) {
753 		*c->variable = !*c->variable;		/* invert it */
754 		if (c->actionexplanation) {
755 		    printf("%s %s.\r\n", *c->variable? "Will" : "Won't",
756 							c->actionexplanation);
757 		}
758 	    }
759 	    if (c->handler) {
760 		retval &= (*c->handler)(-1);
761 	    }
762 	}
763     }
764     return retval;
765 }
766 
767 /*
768  * The following perform the "set" command.
769  */
770 
771 struct termios new_tc = { 0 };
772 
773 struct setlist {
774     char *name;				/* name */
775     char *help;				/* help information */
776     void (*handler)(const char *);
777     cc_t *charp;			/* where it is located at */
778 };
779 
780 static struct setlist Setlist[] = {
781 #ifdef	KLUDGELINEMODE
782     { "echo", 	"character to toggle local echoing on/off", 0, &echoc },
783 #endif
784     { "escape",	"character to escape back to telnet command mode", 0, &escape },
785     { "rlogin", "rlogin escape character", 0, &rlogin },
786     { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile},
787     { " ", "" },
788     { " ", "The following need 'localchars' to be toggled true", 0, 0 },
789     { "flushoutput", "character to cause an Abort Output", 0, &termFlushChar },
790     { "interrupt", "character to cause an Interrupt Process", 0, &termIntChar },
791     { "quit",	"character to cause an Abort process", 0, &termQuitChar },
792     { "eof",	"character to cause an EOF ", 0, &termEofChar },
793     { " ", "" },
794     { " ", "The following are for local editing in linemode", 0, 0 },
795     { "erase",	"character to use to erase a character", 0, &termEraseChar },
796     { "kill",	"character to use to erase a line", 0, &termKillChar },
797     { "lnext",	"character to use for literal next", 0, &termLiteralNextChar },
798     { "susp",	"character to cause a Suspend Process", 0, &termSuspChar },
799     { "reprint", "character to use for line reprint", 0, &termRprntChar },
800     { "worderase", "character to use to erase a word", 0, &termWerasChar },
801     { "start",	"character to use for XON", 0, &termStartChar },
802     { "stop",	"character to use for XOFF", 0, &termStopChar },
803     { "forw1",	"alternate end of line character", 0, &termForw1Char },
804     { "forw2",	"alternate end of line character", 0, &termForw2Char },
805     { "ayt",	"alternate AYT character", 0, &termAytChar },
806     { 0 }
807 };
808 
809 static struct setlist *
810 getset(char *name)
811 {
812     return (struct setlist *)
813 		genget(name, (char **) Setlist, sizeof(struct setlist));
814 }
815 
816 void
817 set_escape_char(char *s)
818 {
819 	if (rlogin != _POSIX_VDISABLE) {
820 		rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE;
821 		printf("Telnet rlogin escape character is '%s'.\r\n",
822 					control(rlogin));
823 	} else {
824 		escape = (s && *s) ? special(s) : _POSIX_VDISABLE;
825 		printf("Telnet escape character is '%s'.\r\n", control(escape));
826 	}
827 }
828 
829 static int
830 setcmd(int argc, char *argv[])
831 {
832     int value;
833     struct setlist *ct;
834     struct togglelist *c;
835 
836     if (argc < 2 || argc > 3) {
837 	printf("Format is 'set Name Value'\r\n'set ?' for help.\r\n");
838 	return 0;
839     }
840     if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) {
841 	for (ct = Setlist; ct->name; ct++)
842 	    printf("%-15s %s\r\n", ct->name, ct->help);
843 	printf("\r\n");
844 	settogglehelp(1);
845 	printf("%-15s %s\r\n", "?", "display help information");
846 	return 0;
847     }
848 
849     ct = getset(argv[1]);
850     if (ct == 0) {
851 	c = GETTOGGLE(argv[1]);
852 	if (c == 0) {
853 	    fprintf(stderr, "'%s': unknown argument ('set ?' for help).\r\n",
854 			argv[1]);
855 	    return 0;
856 	} else if (Ambiguous(c)) {
857 	    fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\r\n",
858 			argv[1]);
859 	    return 0;
860 	} else if (!connected && c->needconnect) {
861 	    printf("?Need to be connected first.\r\n");
862 	    printf("'send ?' for help\r\n");
863 	    return 0;
864 	}
865 
866 	if (c->variable) {
867 	    if ((argc == 2) || (strcmp("on", argv[2]) == 0))
868 		*c->variable = 1;
869 	    else if (strcmp("off", argv[2]) == 0)
870 		*c->variable = 0;
871 	    else {
872 		printf("Format is 'set togglename [on|off]'\r\n'set ?' for help.\r\n");
873 		return 0;
874 	    }
875 	    if (c->actionexplanation) {
876 		printf("%s %s.\r\n", *c->variable? "Will" : "Won't",
877 							c->actionexplanation);
878 	    }
879 	}
880 	if (c->handler)
881 	    (*c->handler)(1);
882     } else if (argc != 3) {
883 	printf("Format is 'set Name Value'\r\n'set ?' for help.\r\n");
884 	return 0;
885     } else if (Ambiguous(ct)) {
886 	fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\r\n",
887 			argv[1]);
888 	return 0;
889     } else if (ct->handler) {
890 	(*ct->handler)(argv[2]);
891 	printf("%s set to \"%s\".\r\n", ct->name, (char *)ct->charp);
892     } else {
893 	if (strcmp("off", argv[2])) {
894 	    value = special(argv[2]);
895 	} else {
896 	    value = _POSIX_VDISABLE;
897 	}
898 	*(ct->charp) = (cc_t)value;
899 	printf("%s character is '%s'.\r\n", ct->name, control(*(ct->charp)));
900     }
901     slc_check();
902     return 1;
903 }
904 
905 static int
906 unsetcmd(int argc, char *argv[])
907 {
908     struct setlist *ct;
909     struct togglelist *c;
910     char *name;
911 
912     if (argc < 2) {
913 	fprintf(stderr,
914 	    "Need an argument to 'unset' command.  'unset ?' for help.\r\n");
915 	return 0;
916     }
917     if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) {
918 	for (ct = Setlist; ct->name; ct++)
919 	    printf("%-15s %s\r\n", ct->name, ct->help);
920 	printf("\r\n");
921 	settogglehelp(0);
922 	printf("%-15s %s\r\n", "?", "display help information");
923 	return 0;
924     }
925 
926     argc--;
927     argv++;
928     while (argc--) {
929 	name = *argv++;
930 	ct = getset(name);
931 	if (ct == 0) {
932 	    c = GETTOGGLE(name);
933 	    if (c == 0) {
934 		fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\r\n",
935 			name);
936 		return 0;
937 	    } else if (Ambiguous(c)) {
938 		fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\r\n",
939 			name);
940 		return 0;
941 	    }
942 	    if (c->variable) {
943 		*c->variable = 0;
944 		if (c->actionexplanation) {
945 		    printf("%s %s.\r\n", *c->variable? "Will" : "Won't",
946 							c->actionexplanation);
947 		}
948 	    }
949 	    if (c->handler)
950 		(*c->handler)(0);
951 	} else if (Ambiguous(ct)) {
952 	    fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\r\n",
953 			name);
954 	    return 0;
955 	} else if (ct->handler) {
956 	    (*ct->handler)(NULL);
957 	    printf("%s reset to \"%s\".\r\n", ct->name, (char *)ct->charp);
958 	} else {
959 	    *(ct->charp) = _POSIX_VDISABLE;
960 	    printf("%s character is '%s'.\r\n", ct->name, control(*(ct->charp)));
961 	}
962     }
963     return 1;
964 }
965 
966 /*
967  * The following are the data structures and routines for the
968  * 'mode' command.
969  */
970 #ifdef	KLUDGELINEMODE
971 static int
972 dokludgemode(int unused)
973 {
974     kludgelinemode = 1;
975     send_wont(TELOPT_LINEMODE, 1);
976     send_dont(TELOPT_SGA, 1);
977     send_dont(TELOPT_ECHO, 1);
978     return 1;
979 }
980 #endif
981 
982 static int
983 dolinemode(int unused)
984 {
985 #ifdef	KLUDGELINEMODE
986     if (kludgelinemode)
987 	send_dont(TELOPT_SGA, 1);
988 #endif
989     send_will(TELOPT_LINEMODE, 1);
990     send_dont(TELOPT_ECHO, 1);
991     return 1;
992 }
993 
994 static int
995 docharmode(int unused)
996 {
997 #ifdef	KLUDGELINEMODE
998     if (kludgelinemode)
999 	send_do(TELOPT_SGA, 1);
1000     else
1001 #endif
1002     send_wont(TELOPT_LINEMODE, 1);
1003     send_do(TELOPT_ECHO, 1);
1004     return 1;
1005 }
1006 
1007 static int
1008 dolmmode(int bit, int on)
1009 {
1010     unsigned char c;
1011 
1012     if (my_want_state_is_wont(TELOPT_LINEMODE)) {
1013 	printf("?Need to have LINEMODE option enabled first.\r\n");
1014 	printf("'mode ?' for help.\r\n");
1015 	return 0;
1016     }
1017 
1018     if (on)
1019 	c = (linemode | bit);
1020     else
1021 	c = (linemode & ~bit);
1022     lm_mode(&c, 1, 1);
1023     return 1;
1024 }
1025 
1026 int
1027 tn_setmode(int bit)
1028 {
1029     return dolmmode(bit, 1);
1030 }
1031 
1032 int
1033 tn_clearmode(int bit)
1034 {
1035     return dolmmode(bit, 0);
1036 }
1037 
1038 struct modelist {
1039 	char	*name;		/* command name */
1040 	char	*help;		/* help string */
1041 	int	(*handler)(int);/* routine which executes command */
1042 	int	needconnect;	/* Do we need to be connected to execute? */
1043 	int	arg1;
1044 };
1045 
1046 static int modehelp(int);
1047 
1048 static struct modelist ModeList[] = {
1049     { "character", "Disable LINEMODE option",	docharmode, 1 },
1050 #ifdef	KLUDGELINEMODE
1051     { "",	"(or disable obsolete line-by-line mode)", 0 },
1052 #endif
1053     { "line",	"Enable LINEMODE option",	dolinemode, 1 },
1054 #ifdef	KLUDGELINEMODE
1055     { "",	"(or enable obsolete line-by-line mode)", 0 },
1056 #endif
1057     { "", "", 0 },
1058     { "",	"These require the LINEMODE option to be enabled", 0 },
1059     { "isig",	"Enable signal trapping",	tn_setmode, 1, MODE_TRAPSIG },
1060     { "+isig",	0,				tn_setmode, 1, MODE_TRAPSIG },
1061     { "-isig",	"Disable signal trapping",	tn_clearmode, 1, MODE_TRAPSIG },
1062     { "edit",	"Enable character editing",	tn_setmode, 1, MODE_EDIT },
1063     { "+edit",	0,				tn_setmode, 1, MODE_EDIT },
1064     { "-edit",	"Disable character editing",	tn_clearmode, 1, MODE_EDIT },
1065     { "softtabs", "Enable tab expansion",	tn_setmode, 1, MODE_SOFT_TAB },
1066     { "+softtabs", 0,				tn_setmode, 1, MODE_SOFT_TAB },
1067     { "-softtabs", "Disable character editing",	tn_clearmode, 1, MODE_SOFT_TAB },
1068     { "litecho", "Enable literal character echo", tn_setmode, 1, MODE_LIT_ECHO },
1069     { "+litecho", 0,				tn_setmode, 1, MODE_LIT_ECHO },
1070     { "-litecho", "Disable literal character echo", tn_clearmode, 1, MODE_LIT_ECHO },
1071     { "help",	0,				modehelp, 0 },
1072 #ifdef	KLUDGELINEMODE
1073     { "kludgeline", 0,				dokludgemode, 1 },
1074 #endif
1075     { "", "", 0 },
1076     { "?",	"Print help information",	modehelp, 0 },
1077     { 0 },
1078 };
1079 
1080 static int
1081 modehelp(int unused)
1082 {
1083     struct modelist *mt;
1084 
1085     printf("format is:  'mode Mode', where 'Mode' is one of:\r\n\r\n");
1086     for (mt = ModeList; mt->name; mt++) {
1087 	if (mt->help) {
1088 	    if (*mt->help)
1089 		printf("%-15s %s\r\n", mt->name, mt->help);
1090 	    else
1091 		printf("\r\n");
1092 	}
1093     }
1094     return 0;
1095 }
1096 
1097 #define	GETMODECMD(name) (struct modelist *) \
1098 		genget(name, (char **) ModeList, sizeof(struct modelist))
1099 
1100 static int
1101 modecmd(int argc, char *argv[])
1102 {
1103     struct modelist *mt;
1104 
1105     if (argc != 2) {
1106 	printf("'mode' command requires an argument\r\n");
1107 	printf("'mode ?' for help.\r\n");
1108     } else if ((mt = GETMODECMD(argv[1])) == 0) {
1109 	fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\r\n", argv[1]);
1110     } else if (Ambiguous(mt)) {
1111 	fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\r\n", argv[1]);
1112     } else if (mt->needconnect && !connected) {
1113 	printf("?Need to be connected first.\r\n");
1114 	printf("'mode ?' for help.\r\n");
1115     } else if (mt->handler) {
1116 	return (*mt->handler)(mt->arg1);
1117     }
1118     return 0;
1119 }
1120 
1121 /*
1122  * The following data structures and routines implement the
1123  * "display" command.
1124  */
1125 
1126 static int
1127 display(int argc, char *argv[])
1128 {
1129     struct togglelist *tl;
1130     struct setlist *sl;
1131 
1132 #define	dotog(tl)	if (tl->variable && tl->actionexplanation) { \
1133 			    if (*tl->variable) { \
1134 				printf("will"); \
1135 			    } else { \
1136 				printf("won't"); \
1137 			    } \
1138 			    printf(" %s.\r\n", tl->actionexplanation); \
1139 			}
1140 
1141 #define	doset(sl)   if (sl->name && *sl->name != ' ') { \
1142 			if (sl->handler == 0) \
1143 			    printf("%-15s [%s]\r\n", sl->name, control(*sl->charp)); \
1144 			else \
1145 			    printf("%-15s \"%s\"\r\n", sl->name, (char *)sl->charp); \
1146 		    }
1147 
1148     if (argc == 1) {
1149 	for (tl = Togglelist; tl->name; tl++) {
1150 	    dotog(tl);
1151 	}
1152 	printf("\r\n");
1153 	for (sl = Setlist; sl->name; sl++) {
1154 	    doset(sl);
1155 	}
1156     } else {
1157 	int i;
1158 
1159 	for (i = 1; i < argc; i++) {
1160 	    sl = getset(argv[i]);
1161 	    tl = GETTOGGLE(argv[i]);
1162 	    if (Ambiguous(sl) || Ambiguous(tl)) {
1163 		printf("?Ambiguous argument '%s'.\r\n", argv[i]);
1164 		return 0;
1165 	    } else if (!sl && !tl) {
1166 		printf("?Unknown argument '%s'.\r\n", argv[i]);
1167 		return 0;
1168 	    } else {
1169 		if (tl) {
1170 		    dotog(tl);
1171 		}
1172 		if (sl) {
1173 		    doset(sl);
1174 		}
1175 	    }
1176 	}
1177     }
1178 /*@*/optionstatus();
1179     return 1;
1180 #undef	doset
1181 #undef	dotog
1182 }
1183 
1184 /*
1185  * The following are the data structures, and many of the routines,
1186  * relating to command processing.
1187  */
1188 
1189 /*
1190  * Set the escape character.
1191  */
1192 static int
1193 setescape(int argc, char *argv[])
1194 {
1195 	char *arg;
1196 	char buf[50];
1197 
1198 	printf(
1199 	    "Deprecated usage - please use 'set escape%s%s' in the future.\r\n",
1200 				(argc > 2)? " ":"", (argc > 2)? argv[1]: "");
1201 	if (argc > 2)
1202 		arg = argv[1];
1203 	else {
1204 		printf("new escape character: ");
1205 		(void) fgets(buf, sizeof(buf), stdin);
1206 		arg = buf;
1207 	}
1208 	if (arg[0] != '\0')
1209 		escape = arg[0];
1210 	printf("Escape character is '%s'.\r\n", control(escape));
1211 	(void) fflush(stdout);
1212 	return 1;
1213 }
1214 
1215 static int
1216 togcrmod(int unused1, char *unused2[])
1217 {
1218     crmod = !crmod;
1219     printf("Deprecated usage - please use 'toggle crmod' in the future.\r\n");
1220     printf("%s map carriage return on output.\r\n", crmod ? "Will" : "Won't");
1221     (void) fflush(stdout);
1222     return 1;
1223 }
1224 
1225 int
1226 telnetsuspend(int unused1, char *unused2[])
1227 {
1228     setcommandmode();
1229     {
1230 	long oldrows, oldcols, newrows, newcols, err;
1231 
1232 	err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1233 	(void) kill(0, SIGTSTP);
1234 	/*
1235 	 * If we didn't get the window size before the SUSPEND, but we
1236 	 * can get them now (?), then send the NAWS to make sure that
1237 	 * we are set up for the right window size.
1238 	 */
1239 	if (TerminalWindowSize(&newrows, &newcols) && connected &&
1240 	    (err || ((oldrows != newrows) || (oldcols != newcols)))) {
1241 		sendnaws();
1242 	}
1243     }
1244     /* reget parameters in case they were changed */
1245     TerminalSaveState();
1246     setconnmode(0);
1247     return 1;
1248 }
1249 
1250 int
1251 shell(int argc, char *argv[])
1252 {
1253     long oldrows, oldcols, newrows, newcols, err;
1254 
1255     setcommandmode();
1256 
1257     err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1258     switch(vfork()) {
1259     case -1:
1260 	perror("Fork failed\r\n");
1261 	break;
1262 
1263     case 0:
1264 	{
1265 	    /*
1266 	     * Fire up the shell in the child.
1267 	     */
1268 	    char *shellp, *shellname;
1269 
1270 	    shellp = getenv("SHELL");
1271 	    if (shellp == NULL)
1272 		shellp = "/bin/sh";
1273 	    if ((shellname = strrchr(shellp, '/')) == 0)
1274 		shellname = shellp;
1275 	    else
1276 		shellname++;
1277 	    if (argc > 1)
1278 		execl(shellp, shellname, "-c", &saveline[1], (char *)NULL);
1279 	    else
1280 		execl(shellp, shellname, (char *)NULL);
1281 	    perror("Execl");
1282 	    _exit(1);
1283 	}
1284     default:
1285 	    (void)wait((int *)0);	/* Wait for the shell to complete */
1286 
1287 	    if (TerminalWindowSize(&newrows, &newcols) && connected &&
1288 		(err || ((oldrows != newrows) || (oldcols != newcols)))) {
1289 		    sendnaws();
1290 	    }
1291 	    break;
1292     }
1293     return 1;
1294 }
1295 
1296 static void
1297 close_connection(void)
1298 {
1299 	if (connected) {
1300 		(void) shutdown(net, 2);
1301 		printf("Connection closed.\r\n");
1302 		(void)close(net);
1303 		connected = 0;
1304 		resettermname = 1;
1305 		/* reset options */
1306 		tninit();
1307 	}
1308 }
1309 
1310 static int
1311 bye(int argc, char *argv[])
1312 {
1313 	close_connection();
1314 	longjmp(toplevel, 1);
1315 }
1316 
1317 void
1318 quit(void)
1319 {
1320 	close_connection();
1321 	Exit(0);
1322 }
1323 
1324 static int
1325 quitcmd(int unused1, char *unused2[])
1326 {
1327 	quit();
1328 }
1329 
1330 static int
1331 logout(int unused1, char *unused2[])
1332 {
1333 	send_do(TELOPT_LOGOUT, 1);
1334 	(void) netflush();
1335 	return 1;
1336 }
1337 
1338 
1339 /*
1340  * The SLC command.
1341  */
1342 
1343 struct slclist {
1344 	char	*name;
1345 	char	*help;
1346 	void	(*handler)(int);
1347 	int	arg;
1348 };
1349 
1350 static void slc_help(int);
1351 
1352 struct slclist SlcList[] = {
1353     { "export",	"Use local special character definitions",
1354 						slc_mode_export,	0 },
1355     { "import",	"Use remote special character definitions",
1356 						slc_mode_import,	1 },
1357     { "check",	"Verify remote special character definitions",
1358 						slc_mode_import,	0 },
1359     { "help",	0,				slc_help,		0 },
1360     { "?",	"Print help information",	slc_help,		0 },
1361     { 0 },
1362 };
1363 
1364 static void
1365 slc_help(int unused)
1366 {
1367     struct slclist *c;
1368 
1369     for (c = SlcList; c->name; c++) {
1370 	if (c->help) {
1371 	    if (*c->help)
1372 		printf("%-15s %s\r\n", c->name, c->help);
1373 	    else
1374 		printf("\r\n");
1375 	}
1376     }
1377 }
1378 
1379 static struct slclist *
1380 getslc(char *name)
1381 {
1382     return (struct slclist *)
1383 		genget(name, (char **) SlcList, sizeof(struct slclist));
1384 }
1385 
1386 static int
1387 slccmd(int argc, char *argv[])
1388 {
1389     struct slclist *c;
1390 
1391     if (argc != 2) {
1392 	fprintf(stderr,
1393 	    "Need an argument to 'slc' command.  'slc ?' for help.\r\n");
1394 	return 0;
1395     }
1396     c = getslc(argv[1]);
1397     if (c == 0) {
1398 	fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\r\n",
1399     				argv[1]);
1400 	return 0;
1401     }
1402     if (Ambiguous(c)) {
1403 	fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\r\n",
1404     				argv[1]);
1405 	return 0;
1406     }
1407     (*c->handler)(c->arg);
1408     slcstate();
1409     return 1;
1410 }
1411 
1412 /*
1413  * The ENVIRON command.
1414  */
1415 
1416 struct envlist {
1417 	char	*name;
1418 	char	*help;
1419 	void	(*handler)();
1420 	int	narg;
1421 };
1422 
1423 static void	env_help(void);
1424 static void	env_undefine(const char *);
1425 static void	env_export(const char *);
1426 static void	env_unexport(const char *);
1427 static void	env_send(const char *);
1428 static void	env_list(void);
1429 static struct env_lst *env_find(const char *var);
1430 
1431 struct envlist EnvList[] = {
1432     { "define",	"Define an environment variable",
1433 						(void (*)())env_define,	2 },
1434     { "undefine", "Undefine an environment variable",
1435 						env_undefine,	1 },
1436     { "export",	"Mark an environment variable for automatic export",
1437 						env_export,	1 },
1438     { "unexport", "Don't mark an environment variable for automatic export",
1439 						env_unexport,	1 },
1440     { "send",	"Send an environment variable", env_send,	1 },
1441     { "list",	"List the current environment variables",
1442 						env_list,	0 },
1443     { "help",	0,				env_help,		0 },
1444     { "?",	"Print help information",	env_help,		0 },
1445     { 0 },
1446 };
1447 
1448 static void
1449 env_help(void)
1450 {
1451     struct envlist *c;
1452 
1453     for (c = EnvList; c->name; c++) {
1454 	if (c->help) {
1455 	    if (*c->help)
1456 		printf("%-15s %s\r\n", c->name, c->help);
1457 	    else
1458 		printf("\r\n");
1459 	}
1460     }
1461 }
1462 
1463 static struct envlist *
1464 getenvcmd(char *name)
1465 {
1466     return (struct envlist *)
1467 		genget(name, (char **) EnvList, sizeof(struct envlist));
1468 }
1469 
1470 static int
1471 env_cmd(int argc, char *argv[])
1472 {
1473     struct envlist *c;
1474 
1475     if (argc < 2) {
1476 	fprintf(stderr,
1477 	    "Need an argument to 'environ' command.  'environ ?' for help.\r\n");
1478 	return 0;
1479     }
1480     c = getenvcmd(argv[1]);
1481     if (c == 0) {
1482 	fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\r\n",
1483     				argv[1]);
1484 	return 0;
1485     }
1486     if (Ambiguous(c)) {
1487 	fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\r\n",
1488     				argv[1]);
1489 	return 0;
1490     }
1491     if (c->narg + 2 != argc) {
1492 	fprintf(stderr,
1493 	    "Need %s%d argument%s to 'environ %s' command.  'environ ?' for help.\r\n",
1494 		c->narg < argc + 2 ? "only " : "",
1495 		c->narg, c->narg == 1 ? "" : "s", c->name);
1496 	return 0;
1497     }
1498     (*c->handler)(argv[2], argv[3]);
1499     return 1;
1500 }
1501 
1502 struct env_lst {
1503 	struct env_lst *next;	/* pointer to next structure */
1504 	struct env_lst *prev;	/* pointer to previous structure */
1505 	char *var;		/* pointer to variable name */
1506 	char *value;		/* pointer to variable value */
1507 	int export;		/* 1 -> export with default list of variables */
1508 	int welldefined;	/* A well defined variable */
1509 };
1510 
1511 struct env_lst envlisthead;
1512 
1513 static struct env_lst *
1514 env_find(const char *var)
1515 {
1516 	struct env_lst *ep;
1517 
1518 	for (ep = envlisthead.next; ep; ep = ep->next) {
1519 		if (strcmp(ep->var, var) == 0)
1520 			return(ep);
1521 	}
1522 	return(NULL);
1523 }
1524 
1525 void
1526 env_init(void)
1527 {
1528 	extern char **environ;
1529 	char **epp, *cp;
1530 	struct env_lst *ep;
1531 
1532 	for (epp = environ; *epp; epp++) {
1533 		if ((cp = strchr(*epp, '='))) {
1534 			*cp = '\0';
1535 			ep = env_define(*epp, cp+1);
1536 			ep->export = 0;
1537 			*cp = '=';
1538 		}
1539 	}
1540 	/*
1541 	 * Special case for DISPLAY variable.  If it is ":0.0" or
1542 	 * "unix:0.0", we have to get rid of "unix" and insert our
1543 	 * hostname.
1544 	 */
1545 	if ((ep = env_find("DISPLAY"))
1546 	    && ((*ep->value == ':')
1547 		|| (strncmp(ep->value, "unix:", 5) == 0))) {
1548 		char hbuf[HOST_NAME_MAX+1];
1549 		char *cp2 = strchr(ep->value, ':');
1550 
1551 		gethostname(hbuf, sizeof hbuf);
1552 
1553 		/* If this is not the full name, try to get it via DNS */
1554 		if (strchr(hbuf, '.') == 0) {
1555 			struct hostent *he = gethostbyname(hbuf);
1556 			if (he != 0)
1557 				strncpy(hbuf, he->h_name, sizeof hbuf-1);
1558 			hbuf[sizeof hbuf-1] = '\0';
1559 		}
1560 
1561 		if (asprintf (&cp, "%s%s", hbuf, cp2) == -1)
1562 			err(1, "asprintf");
1563 
1564 		free(ep->value);
1565 		ep->value = cp;
1566 	}
1567 	/*
1568 	 * If USER is not defined, but LOGNAME is, then add
1569 	 * USER with the value from LOGNAME.  By default, we
1570 	 * don't export the USER variable.
1571 	 */
1572 	if ((env_find("USER") == NULL) && (ep = env_find("LOGNAME"))) {
1573 		env_define("USER", ep->value);
1574 		env_unexport("USER");
1575 	}
1576 	env_export("DISPLAY");
1577 	env_export("PRINTER");
1578 	env_export("XAUTHORITY");
1579 }
1580 
1581 struct env_lst *
1582 env_define(const char *var, const char *value)
1583 {
1584 	struct env_lst *ep;
1585 
1586 	if ((ep = env_find(var))) {
1587 		if (ep->var)
1588 			free(ep->var);
1589 		if (ep->value)
1590 			free(ep->value);
1591 	} else {
1592 		if ((ep = malloc(sizeof(struct env_lst))) == NULL)
1593 			err(1, "malloc");
1594 		ep->next = envlisthead.next;
1595 		envlisthead.next = ep;
1596 		ep->prev = &envlisthead;
1597 		if (ep->next)
1598 			ep->next->prev = ep;
1599 	}
1600 	ep->welldefined = opt_welldefined(var);
1601 	ep->export = 1;
1602 	if ((ep->var = strdup(var)) == NULL)
1603 		err(1, "strdup");
1604 	if ((ep->value = strdup(value)) == NULL)
1605 		err(1, "strdup");
1606 	return(ep);
1607 }
1608 
1609 static void
1610 env_undefine(const char *var)
1611 {
1612 	struct env_lst *ep;
1613 
1614 	if ((ep = env_find(var))) {
1615 		ep->prev->next = ep->next;
1616 		if (ep->next)
1617 			ep->next->prev = ep->prev;
1618 		if (ep->var)
1619 			free(ep->var);
1620 		if (ep->value)
1621 			free(ep->value);
1622 		free(ep);
1623 	}
1624 }
1625 
1626 static void
1627 env_export(const char *var)
1628 {
1629 	struct env_lst *ep;
1630 
1631 	if ((ep = env_find(var)))
1632 		ep->export = 1;
1633 }
1634 
1635 static void
1636 env_unexport(const char *var)
1637 {
1638 	struct env_lst *ep;
1639 
1640 	if ((ep = env_find(var)) != NULL)
1641 		ep->export = 0;
1642 }
1643 
1644 static void
1645 env_send(const char *var)
1646 {
1647 	struct env_lst *ep;
1648 
1649 	if (my_state_is_wont(TELOPT_NEW_ENVIRON)
1650 		) {
1651 		fprintf(stderr,
1652 		    "Cannot send '%s': Telnet ENVIRON option not enabled\r\n",
1653 									var);
1654 		return;
1655 	}
1656 	ep = env_find(var);
1657 	if (ep == 0) {
1658 		fprintf(stderr, "Cannot send '%s': variable not defined\r\n",
1659 									var);
1660 		return;
1661 	}
1662 	env_opt_start_info();
1663 	env_opt_add(ep->var);
1664 	env_opt_end(0);
1665 }
1666 
1667 static void
1668 env_list(void)
1669 {
1670 	struct env_lst *ep;
1671 
1672 	for (ep = envlisthead.next; ep; ep = ep->next) {
1673 		printf("%c %-20s %s\r\n", ep->export ? '*' : ' ',
1674 					ep->var, ep->value);
1675 	}
1676 }
1677 
1678 char *
1679 env_default(int init, int welldefined)
1680 {
1681 	static struct env_lst *nep = NULL;
1682 
1683 	if (init) {
1684 		nep = &envlisthead;
1685 		return NULL;
1686 	}
1687 	if (nep) {
1688 		while ((nep = nep->next)) {
1689 			if (nep->export && (nep->welldefined == welldefined))
1690 				return(nep->var);
1691 		}
1692 	}
1693 	return(NULL);
1694 }
1695 
1696 char *
1697 env_getvalue(const char *var, int exported_only)
1698 {
1699 	struct env_lst *ep;
1700 
1701 	if ((ep = env_find(var)) && (!exported_only || ep->export))
1702 		return(ep->value);
1703 	return(NULL);
1704 }
1705 
1706 static void
1707 connection_status(int local_only)
1708 {
1709 	if (!connected)
1710 		printf("No connection.\r\n");
1711 	else {
1712 		printf("Connected to %s.\r\n", hostname);
1713 		if (!local_only) {
1714 			int mode = getconnmode();
1715 
1716 			printf("Operating ");
1717 			if (my_want_state_is_will(TELOPT_LINEMODE)) {
1718 				printf("with LINEMODE option\r\n"
1719 				    "%s line editing\r\n"
1720 				    "%s catching of signals\r\n",
1721 				    (mode & MODE_EDIT) ? "Local" : "No",
1722 				    (mode & MODE_TRAPSIG) ? "Local" : "No");
1723 				slcstate();
1724 #ifdef	KLUDGELINEMODE
1725 			} else if (kludgelinemode &&
1726 			    my_want_state_is_dont(TELOPT_SGA)) {
1727 				printf("in obsolete linemode\r\n");
1728 #endif
1729 			} else {
1730 				printf("in single character mode\r\n");
1731 				if (localchars)
1732 					printf("Catching signals locally\r\n");
1733 			}
1734 
1735 			printf("%s character echo\r\n",
1736 			    (mode & MODE_ECHO) ? "Local" : "Remote");
1737 			if (my_want_state_is_will(TELOPT_LFLOW))
1738 				printf("%s flow control\r\n",
1739 				    (mode & MODE_FLOW) ? "Local" : "No");
1740 		}
1741 	}
1742 	printf("Escape character is '%s'.\r\n", control(escape));
1743 	(void) fflush(stdout);
1744 }
1745 
1746 /*
1747  * Print status about the connection.
1748  */
1749 static int
1750 status(int argc, char *argv[])
1751 {
1752 	connection_status(0);
1753 	return 1;
1754 }
1755 
1756 #ifdef	SIGINFO
1757 /*
1758  * Function that gets called when SIGINFO is received.
1759  */
1760 void
1761 ayt_status(int sig)
1762 {
1763 	connection_status(1);
1764 }
1765 #endif
1766 
1767 static Command *getcmd(char *name);
1768 
1769 static void
1770 cmdrc(char *m1, char *m2)
1771 {
1772     static char rcname[128];
1773     Command *c;
1774     FILE *rcfile;
1775     int gotmachine = 0;
1776     int l1 = strlen(m1);
1777     int l2 = strlen(m2);
1778     char m1save[HOST_NAME_MAX+1];
1779 
1780     if (skiprc)
1781 	return;
1782 
1783     strlcpy(m1save, m1, sizeof(m1save));
1784     m1 = m1save;
1785 
1786     if (rcname[0] == 0) {
1787 	char *home = getenv("HOME");
1788 
1789 	if (home == NULL || *home == '\0')
1790 	    return;
1791 	snprintf (rcname, sizeof(rcname), "%s/.telnetrc",
1792 		  home ? home : "");
1793     }
1794 
1795     if ((rcfile = fopen(rcname, "r")) == 0) {
1796 	return;
1797     }
1798 
1799     for (;;) {
1800 	if (fgets(line, sizeof(line), rcfile) == NULL)
1801 	    break;
1802 	if (line[0] == 0)
1803 	    break;
1804 	if (line[0] == '#')
1805 	    continue;
1806 	if (gotmachine) {
1807 	    if (!isspace((unsigned char)line[0]))
1808 		gotmachine = 0;
1809 	}
1810 	if (gotmachine == 0) {
1811 	    if (isspace((unsigned char)line[0]))
1812 		continue;
1813 	    if (strncasecmp(line, m1, l1) == 0)
1814 		strncpy(line, &line[l1], sizeof(line) - l1);
1815 	    else if (strncasecmp(line, m2, l2) == 0)
1816 		strncpy(line, &line[l2], sizeof(line) - l2);
1817 	    else if (strncasecmp(line, "DEFAULT", 7) == 0)
1818 		strncpy(line, &line[7], sizeof(line) - 7);
1819 	    else
1820 		continue;
1821 	    if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n')
1822 		continue;
1823 	    gotmachine = 1;
1824 	}
1825 	makeargv();
1826 	if (margv[0] == 0)
1827 	    continue;
1828 	c = getcmd(margv[0]);
1829 	if (Ambiguous(c)) {
1830 	    printf("?Ambiguous command: %s\r\n", margv[0]);
1831 	    continue;
1832 	}
1833 	if (c == 0) {
1834 	    printf("?Invalid command: %s\r\n", margv[0]);
1835 	    continue;
1836 	}
1837 	/*
1838 	 * This should never happen...
1839 	 */
1840 	if (c->needconnect && !connected) {
1841 	    printf("?Need to be connected first for %s.\r\n", margv[0]);
1842 	    continue;
1843 	}
1844 	(*c->handler)(margc, margv);
1845     }
1846     fclose(rcfile);
1847 }
1848 
1849 int
1850 tn(int argc, char *argv[])
1851 {
1852     struct addrinfo hints, *res, *res0;
1853     int error;
1854     struct sockaddr_in sin;
1855     unsigned long temp;
1856     char *srp = 0;
1857     int srlen;
1858     char *cmd, *hostp = 0, *portp = 0, *user = 0, *aliasp = 0;
1859     int retry;
1860     const int niflags = NI_NUMERICHOST;
1861 
1862     /* clear the socket address prior to use */
1863     memset(&sin, 0, sizeof(sin));
1864 
1865     if (connected) {
1866 	printf("?Already connected to %s\r\n", hostname);
1867 	return 0;
1868     }
1869     if (argc < 2) {
1870 	strlcpy(line, "open ", sizeof(line));
1871 	printf("(to) ");
1872 	(void) fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin);
1873 	makeargv();
1874 	argc = margc;
1875 	argv = margv;
1876     }
1877     cmd = *argv;
1878     --argc; ++argv;
1879     while (argc) {
1880 	if (strcmp(*argv, "help") == 0 || isprefix(*argv, "?"))
1881 	    goto usage;
1882 	if (strcmp(*argv, "-l") == 0) {
1883 	    --argc; ++argv;
1884 	    if (argc == 0)
1885 		goto usage;
1886 	    if ((user = strdup(*argv++)) == NULL)
1887 		err(1, "strdup");
1888 	    --argc;
1889 	    continue;
1890 	}
1891 	if (strcmp(*argv, "-b") == 0) {
1892 	    --argc; ++argv;
1893 	    if (argc == 0)
1894 		goto usage;
1895 	    aliasp = *argv++;
1896 	    --argc;
1897 	    continue;
1898 	}
1899 	if (strcmp(*argv, "-a") == 0) {
1900 	    --argc; ++argv;
1901 	    autologin = 1;
1902 	    continue;
1903 	}
1904 	if (hostp == 0) {
1905 	    hostp = *argv++;
1906 	    --argc;
1907 	    continue;
1908 	}
1909 	if (portp == 0) {
1910 	    portp = *argv++;
1911 	    --argc;
1912 	    continue;
1913 	}
1914     usage:
1915 	printf("usage: %s [-a] [-b hostalias] [-l user] host-name [port]\r\n", cmd);
1916 	return 0;
1917     }
1918     if (hostp == 0)
1919 	goto usage;
1920 
1921     if (hostp[0] == '@' || hostp[0] == '!') {
1922 	if ((hostname = strrchr(hostp, ':')) == NULL)
1923 	    hostname = strrchr(hostp, '@');
1924 	hostname++;
1925 	srp = 0;
1926 	temp = sourceroute(hostp, &srp, &srlen);
1927 	if (temp == 0) {
1928 	    herror(srp);
1929 	    return 0;
1930 	} else if (temp == -1) {
1931 	    printf("Bad source route option: %s\r\n", hostp);
1932 	    return 0;
1933 	} else {
1934 	    abort();
1935 	}
1936     } else
1937     {
1938 	hostname = hostp;
1939 	memset(&hints, 0, sizeof(hints));
1940 	hints.ai_family = family;
1941 	hints.ai_socktype = SOCK_STREAM;
1942 	hints.ai_flags = AI_CANONNAME;
1943 	if (portp == NULL) {
1944 	    portp = "telnet";
1945 	    telnetport = 1;
1946 	} else if (*portp == '-') {
1947 	    portp++;
1948 	    telnetport = 1;
1949 	} else
1950 	    telnetport = 0;
1951 	h_errno = 0;
1952 	error = getaddrinfo(hostp, portp, &hints, &res0);
1953 	if (error) {
1954 	    if (error == EAI_SERVICE)
1955 		warnx("%s: bad port", portp);
1956 	    else
1957 		warnx("%s: %s", hostp, gai_strerror(error));
1958 	    if (h_errno)
1959 		herror(hostp);
1960 	    return 0;
1961 	}
1962     }
1963 
1964     net = -1;
1965     retry = 0;
1966     for (res = res0; res; res = res->ai_next) {
1967 	if (1 /* retry */) {
1968 	    char hbuf[NI_MAXHOST];
1969 
1970 	    if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
1971 		    NULL, 0, niflags) != 0) {
1972 		strlcpy(hbuf, "(invalid)", sizeof(hbuf));
1973 	    }
1974 	    printf("Trying %s...\r\n", hbuf);
1975 	}
1976 	net = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1977 	if (net < 0)
1978 	    continue;
1979 
1980 	if (rtableid >= 0 && (setsockopt(net, SOL_SOCKET, SO_RTABLE, &rtableid,
1981 	    sizeof(rtableid)) == -1))
1982 		perror("setsockopt (SO_RTABLE)");
1983 
1984 	if (aliasp) {
1985 	    struct addrinfo ahints, *ares;
1986 
1987 	    memset(&ahints, 0, sizeof(ahints));
1988 	    ahints.ai_family = family;
1989 	    ahints.ai_socktype = SOCK_STREAM;
1990 	    ahints.ai_flags = AI_PASSIVE;
1991 	    error = getaddrinfo(aliasp, "0", &ahints, &ares);
1992 	    if (error) {
1993 		warn("%s: %s", aliasp, gai_strerror(error));
1994 		close(net);
1995 		net = -1;
1996 		continue;
1997 	    }
1998 	    if (bind(net, ares->ai_addr, ares->ai_addrlen) < 0) {
1999 		perror(aliasp);
2000 		(void) close(net);   /* dump descriptor */
2001 		net = -1;
2002 		freeaddrinfo(ares);
2003 		continue;
2004             }
2005 	    freeaddrinfo(ares);
2006 	}
2007 	if (srp && res->ai_family == AF_INET
2008 	 && setsockopt(net, IPPROTO_IP, IP_OPTIONS, srp, srlen) < 0)
2009 		perror("setsockopt (IP_OPTIONS)");
2010 	if (res->ai_family == AF_INET) {
2011 	    if (tos < 0)
2012 		tos = IPTOS_LOWDELAY;	/* Low Delay bit */
2013 	    if (tos
2014 		&& (setsockopt(net, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0)
2015 		&& (errno != ENOPROTOOPT))
2016 		    perror("telnet: setsockopt (IP_TOS) (ignored)");
2017 	}
2018 
2019 	if (debug) {
2020 		int one = 1;
2021 
2022 		if (setsockopt(net, SOL_SOCKET, SO_DEBUG, &one,
2023 		    sizeof(one)) < 0)
2024 			perror("setsockopt (SO_DEBUG)");
2025 	}
2026 
2027 	if (connect(net, res->ai_addr, res->ai_addrlen) < 0) {
2028 	    char hbuf[NI_MAXHOST];
2029 
2030 	    if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
2031 		    NULL, 0, niflags) != 0) {
2032 		strlcpy(hbuf, "(invalid)", sizeof(hbuf));
2033 	    }
2034 	    fprintf(stderr, "telnet: connect to address %s: %s\n", hbuf,
2035 		strerror(errno));
2036 
2037 	    close(net);
2038 	    net = -1;
2039 	    retry++;
2040 	    continue;
2041 	}
2042 
2043 	connected++;
2044 	break;
2045     }
2046     freeaddrinfo(res0);
2047     if (net < 0) {
2048 	return 0;
2049     }
2050     cmdrc(hostp, hostname);
2051     if (autologin && user == NULL) {
2052 	struct passwd *pw;
2053 
2054 	user = getlogin();
2055 	if (user == NULL ||
2056 	    (pw = getpwnam(user)) == NULL || pw->pw_uid != getuid()) {
2057 		if ((pw = getpwuid(getuid())) != NULL)
2058 			user = pw->pw_name;
2059 		else
2060 			user = NULL;
2061 	}
2062     }
2063     if (user) {
2064 	env_define("USER", user);
2065 	env_export("USER");
2066     }
2067     connection_status(1);
2068     if (setjmp(peerdied) == 0)
2069 	telnet(user);
2070     (void)close(net);
2071     ExitString("Connection closed by foreign host.\r\n",1);
2072 }
2073 
2074 #define HELPINDENT (sizeof ("connect"))
2075 
2076 static char
2077 	openhelp[] =	"connect to a site",
2078 	closehelp[] =	"close current connection",
2079 	logouthelp[] =	"forcibly logout remote user and close the connection",
2080 	quithelp[] =	"exit telnet",
2081 	statushelp[] =	"print status information",
2082 	helphelp[] =	"print help information",
2083 	sendhelp[] =	"transmit special characters ('send ?' for more)",
2084 	sethelp[] = 	"set operating parameters ('set ?' for more)",
2085 	unsethelp[] = 	"unset operating parameters ('unset ?' for more)",
2086 	togglestring[] ="toggle operating parameters ('toggle ?' for more)",
2087 	slchelp[] =	"change state of special charaters ('slc ?' for more)",
2088 	displayhelp[] =	"display operating parameters",
2089 	zhelp[] =	"suspend telnet",
2090 #ifdef SKEY
2091 	skeyhelp[] =	"compute response to s/key challenge",
2092 #endif
2093 	shellhelp[] =	"invoke a subshell",
2094 	envhelp[] =	"change environment variables ('environ ?' for more)",
2095 	modestring[] = "try to enter line or character mode ('mode ?' for more)";
2096 
2097 static int	help(int, char**);
2098 
2099 static Command cmdtab[] = {
2100 	{ "close",	closehelp,	bye,		1 },
2101 	{ "logout",	logouthelp,	logout,		1 },
2102 	{ "display",	displayhelp,	display,	0 },
2103 	{ "mode",	modestring,	modecmd,	0 },
2104 	{ "open",	openhelp,	tn,		0 },
2105 	{ "quit",	quithelp,	quitcmd,	0 },
2106 	{ "send",	sendhelp,	sendcmd,	0 },
2107 	{ "set",	sethelp,	setcmd,		0 },
2108 	{ "unset",	unsethelp,	unsetcmd,	0 },
2109 	{ "status",	statushelp,	status,		0 },
2110 	{ "toggle",	togglestring,	toggle,		0 },
2111 	{ "slc",	slchelp,	slccmd,		0 },
2112 
2113 	{ "z",		zhelp,		telnetsuspend,	0 },
2114 	{ "!",		shellhelp,	shell,		0 },
2115 	{ "environ",	envhelp,	env_cmd,	0 },
2116 	{ "?",		helphelp,	help,		0 },
2117 #ifdef SKEY
2118 	{ "skey",	skeyhelp,	skey_calc,	0 },
2119 #endif
2120 	{ 0,		0,		0,		0 }
2121 };
2122 
2123 static char	crmodhelp[] =	"deprecated command -- use 'toggle crmod' instead";
2124 static char	escapehelp[] =	"deprecated command -- use 'set escape' instead";
2125 
2126 static Command cmdtab2[] = {
2127 	{ "help",	0,		help,		0 },
2128 	{ "escape",	escapehelp,	setescape,	0 },
2129 	{ "crmod",	crmodhelp,	togcrmod,	0 },
2130 	{ 0,		0,		0,		0 }
2131 };
2132 
2133 
2134 static Command *
2135 getcmd(char *name)
2136 {
2137     Command *cm;
2138 
2139     if ((cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command))))
2140 	return cm;
2141     return (Command *) genget(name, (char **) cmdtab2, sizeof(Command));
2142 }
2143 
2144 void
2145 command(int top, char *tbuf, int cnt)
2146 {
2147     Command *c;
2148 
2149     setcommandmode();
2150     if (!top) {
2151 	putchar('\n');
2152     } else {
2153 	(void) signal(SIGINT, SIG_DFL);
2154 	(void) signal(SIGQUIT, SIG_DFL);
2155     }
2156     for (;;) {
2157 	if (rlogin == _POSIX_VDISABLE)
2158 		printf("%s> ", prompt);
2159 	if (tbuf) {
2160 	    char *cp;
2161 	    cp = line;
2162 	    while (cnt > 0 && (*cp++ = *tbuf++) != '\n')
2163 		cnt--;
2164 	    tbuf = 0;
2165 	    if (cp == line || *--cp != '\n' || cp == line)
2166 		goto getline;
2167 	    *cp = '\0';
2168 	    if (rlogin == _POSIX_VDISABLE)
2169 		printf("%s\r\n", line);
2170 	} else {
2171 	getline:
2172 	    if (rlogin != _POSIX_VDISABLE)
2173 		printf("%s> ", prompt);
2174 	    if (fgets(line, sizeof(line), stdin) == NULL) {
2175 		if (feof(stdin) || ferror(stdin))
2176 		    quit();
2177 		break;
2178 	    }
2179 	}
2180 	if (line[0] == 0)
2181 	    break;
2182 	makeargv();
2183 	if (margv[0] == 0) {
2184 	    break;
2185 	}
2186 	c = getcmd(margv[0]);
2187 	if (Ambiguous(c)) {
2188 	    printf("?Ambiguous command\r\n");
2189 	    continue;
2190 	}
2191 	if (c == 0) {
2192 	    printf("?Invalid command\r\n");
2193 	    continue;
2194 	}
2195 	if (c->needconnect && !connected) {
2196 	    printf("?Need to be connected first.\r\n");
2197 	    continue;
2198 	}
2199 	if ((*c->handler)(margc, margv)) {
2200 	    break;
2201 	}
2202     }
2203     if (!top) {
2204 	if (!connected)
2205 	    longjmp(toplevel, 1);
2206 	setconnmode(0);
2207     }
2208 }
2209 
2210 /*
2211  * Help command.
2212  */
2213 static int
2214 help(int argc, char *argv[])
2215 {
2216 	Command *c;
2217 
2218 	if (argc == 1) {
2219 		printf("Commands may be abbreviated.  Commands are:\r\n\r\n");
2220 		for (c = cmdtab; c->name; c++)
2221 			if (c->help) {
2222 				printf("%-*s\t%s\r\n", (int)HELPINDENT, c->name,
2223 								    c->help);
2224 			}
2225 		return 0;
2226 	}
2227 	while (--argc > 0) {
2228 		char *arg;
2229 		arg = *++argv;
2230 		c = getcmd(arg);
2231 		if (Ambiguous(c))
2232 			printf("?Ambiguous help command %s\r\n", arg);
2233 		else if (c == (Command *)0)
2234 			printf("?Invalid help command %s\r\n", arg);
2235 		else
2236 			printf("%s\r\n", c->help);
2237 	}
2238 	return 0;
2239 }
2240 
2241 /*
2242  * Source route is handed in as
2243  *	[!]@hop1@hop2...[@|:]dst
2244  * If the leading ! is present, it is a
2245  * strict source route, otherwise it is
2246  * assmed to be a loose source route.
2247  *
2248  * We fill in the source route option as
2249  *	hop1,hop2,hop3...dest
2250  * and return a pointer to hop1, which will
2251  * be the address to connect() to.
2252  *
2253  * Arguments:
2254  *	arg:	pointer to route list to decipher
2255  *
2256  *	cpp: 	If *cpp is not equal to NULL, this is a
2257  *		pointer to a pointer to a character array
2258  *		that should be filled in with the option.
2259  *
2260  *	lenp:	pointer to an integer that contains the
2261  *		length of *cpp if *cpp != NULL.
2262  *
2263  * Return values:
2264  *
2265  *	Returns the address of the host to connect to.  If the
2266  *	return value is -1, there was a syntax error in the
2267  *	option, either unknown characters, or too many hosts.
2268  *	If the return value is 0, one of the hostnames in the
2269  *	path is unknown, and *cpp is set to point to the bad
2270  *	hostname.
2271  *
2272  *	*cpp:	If *cpp was equal to NULL, it will be filled
2273  *		in with a pointer to our static area that has
2274  *		the option filled in.  This will be 32bit aligned.
2275  *
2276  *	*lenp:	This will be filled in with how long the option
2277  *		pointed to by *cpp is.
2278  *
2279  */
2280 
2281 static unsigned long
2282 sourceroute(char *arg, char **cpp, int *lenp)
2283 {
2284 	static char lsr[44];
2285 	char *cp, *cp2, *lsrp, *lsrep;
2286 	struct in_addr addr;
2287 	struct hostent *host = 0;
2288 	char c;
2289 
2290 	/*
2291 	 * Verify the arguments, and make sure we have
2292 	 * at least 7 bytes for the option.
2293 	 */
2294 	if (cpp == NULL || lenp == NULL)
2295 		return((unsigned long)-1);
2296 	if (*cpp != NULL && *lenp < 7)
2297 		return((unsigned long)-1);
2298 	/*
2299 	 * Decide whether we have a buffer passed to us,
2300 	 * or if we need to use our own static buffer.
2301 	 */
2302 	if (*cpp) {
2303 		lsrp = *cpp;
2304 		lsrep = lsrp + *lenp;
2305 	} else {
2306 		*cpp = lsrp = lsr;
2307 		lsrep = lsrp + 44;
2308 	}
2309 
2310 	cp = arg;
2311 
2312 	/*
2313 	 * Next, decide whether we have a loose source
2314 	 * route or a strict source route, and fill in
2315 	 * the begining of the option.
2316 	 */
2317 	if (*cp == '!') {
2318 		cp++;
2319 		*lsrp++ = IPOPT_SSRR;
2320 	} else
2321 		*lsrp++ = IPOPT_LSRR;
2322 
2323 	if (*cp != '@')
2324 		return((unsigned long)-1);
2325 
2326 	lsrp++;		/* skip over length, we'll fill it in later */
2327 	*lsrp++ = 4;
2328 
2329 	cp++;
2330 
2331 	addr.s_addr = 0;
2332 
2333 	for (c = 0;;) {
2334 		if (c == ':')
2335 			cp2 = 0;
2336 		else for (cp2 = cp; (c = *cp2); cp2++) {
2337 			if (c == ',') {
2338 				*cp2++ = '\0';
2339 				if (*cp2 == '@')
2340 					cp2++;
2341 			} else if (c == '@') {
2342 				*cp2++ = '\0';
2343 			} else if (c == ':') {
2344 				*cp2++ = '\0';
2345 			} else
2346 				continue;
2347 			break;
2348 		}
2349 		if (!c)
2350 			cp2 = 0;
2351 
2352 		if ((addr.s_addr = inet_addr(cp)) == INADDR_NONE) {
2353 			if ((host = gethostbyname(cp)) == NULL) {
2354 				*cpp = cp;
2355 				return(0);
2356 			}
2357 			memcpy(&addr, host->h_addr_list[0], sizeof addr);
2358 		}
2359 		memcpy(lsrp, &addr, 4);
2360 		lsrp += 4;
2361 		if (cp2)
2362 			cp = cp2;
2363 		else
2364 			break;
2365 		/*
2366 		 * Check to make sure there is space for next address
2367 		 */
2368 		if (lsrp + 4 > lsrep)
2369 			return((unsigned long)-1);
2370 	}
2371 	if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) {
2372 		*cpp = 0;
2373 		*lenp = 0;
2374 		return((unsigned long)-1);
2375 	}
2376 	*lsrp++ = IPOPT_NOP; /* 32 bit word align it */
2377 	*lenp = lsrp - *cpp;
2378 	return(addr.s_addr);
2379 }
2380