xref: /netbsd-src/usr.bin/telnet/commands.c (revision 1ffa7b76c40339c17a0fb2a09fac93f287cfc046)
1 /*	$NetBSD: commands.c,v 1.50 2003/04/27 11:09:57 jdolecek Exp $	*/
2 
3 /*
4  * Copyright (C) 1997 and 1998 WIDE Project.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (c) 1988, 1990, 1993
34  *	The Regents of the University of California.  All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. All advertising materials mentioning features or use of this software
45  *    must display the following acknowledgement:
46  *	This product includes software developed by the University of
47  *	California, Berkeley and its contributors.
48  * 4. Neither the name of the University nor the names of its contributors
49  *    may be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  */
64 
65 #include <sys/cdefs.h>
66 #ifndef lint
67 #if 0
68 static char sccsid[] = "@(#)commands.c	8.4 (Berkeley) 5/30/95";
69 #else
70 __RCSID("$NetBSD: commands.c,v 1.50 2003/04/27 11:09:57 jdolecek Exp $");
71 #endif
72 #endif /* not lint */
73 
74 #if	defined(unix)
75 #include <sys/param.h>
76 #if	defined(CRAY) || defined(sysV88)
77 #include <sys/types.h>
78 #endif
79 #include <sys/file.h>
80 #else
81 #include <sys/types.h>
82 #endif	/* defined(unix) */
83 #include <sys/wait.h>
84 #include <sys/socket.h>
85 #include <netinet/in.h>
86 #include <arpa/inet.h>
87 #ifdef	CRAY
88 #include <fcntl.h>
89 #endif	/* CRAY */
90 
91 #include <ctype.h>
92 #include <errno.h>
93 #include <netdb.h>
94 #include <pwd.h>
95 #include <signal.h>
96 #include <stdarg.h>
97 #include <unistd.h>
98 
99 #include <arpa/telnet.h>
100 
101 #include "general.h"
102 
103 #include "ring.h"
104 
105 #include "externs.h"
106 #include "defines.h"
107 #include "types.h"
108 #include <libtelnet/misc.h>
109 #ifdef AUTHENTICATION
110 #include <libtelnet/auth.h>
111 #endif
112 #ifdef ENCRYPTION
113 #include <libtelnet/encrypt.h>
114 #endif
115 
116 #if !defined(CRAY) && !defined(sysV88)
117 #include <netinet/in_systm.h>
118 # if (defined(vax) || defined(tahoe) || defined(hp300)) && !defined(ultrix)
119 # include <machine/endian.h>
120 # endif /* vax */
121 #endif /* !defined(CRAY) && !defined(sysV88) */
122 #include <netinet/ip.h>
123 
124 
125 #ifndef	MAXHOSTNAMELEN
126 #define	MAXHOSTNAMELEN 64
127 #endif	/* MAXHOSTNAMELEN */
128 
129 #if	defined(IPPROTO_IP) && defined(IP_TOS)
130 int tos = -1;
131 #endif	/* defined(IPPROTO_IP) && defined(IP_TOS) */
132 
133 char	*hostname;
134 static char _hostname[MAXHOSTNAMELEN];
135 
136 typedef struct {
137 	char	*name;		/* command name */
138 	char	*help;		/* help string (NULL for no help) */
139 	int	(*handler)	/* routine which executes command */
140 			(int, char *[]);
141 	int	needconnect;	/* Do we need to be connected to execute? */
142 } Command;
143 
144 static char line[256];
145 static char saveline[256];
146 static int margc;
147 static char *margv[20];
148 
149 static void makeargv(void);
150 static int special(char *);
151 static char *control(cc_t);
152 static int sendcmd(int, char **);
153 static int send_esc(char *);
154 static int send_docmd(char *);
155 static int send_dontcmd(char *);
156 static int send_willcmd(char *);
157 static int send_wontcmd(char *);
158 static int send_help(char *);
159 static int lclchars(int);
160 static int togdebug(int);
161 static int togcrlf(int);
162 static int togbinary(int);
163 static int togrbinary(int);
164 static int togxbinary(int);
165 static int togglehelp(int);
166 static void settogglehelp(int);
167 static int toggle(int, char *[]);
168 static struct setlist *getset(char *);
169 static int setcmd(int, char *[]);
170 static int unsetcmd(int, char *[]);
171 static int dokludgemode(int);
172 static int dolinemode(int);
173 static int docharmode(int);
174 static int dolmmode(int, int );
175 static int modecmd(int, char *[]);
176 static int display(int, char *[]);
177 static int setescape(int, char *[]);
178 static int togcrmod(int, char *[]);
179 static int bye(int, char *[]);
180 static void slc_help(int);
181 static struct slclist *getslc(char *);
182 static int slccmd(int, char *[]);
183 static struct env_lst *env_help(unsigned char *, unsigned char *);
184 static struct envlist *getenvcmd(char *);
185 #ifdef AUTHENTICATION
186 static int auth_help(char *);
187 #endif
188 #if	defined(unix) && defined(TN3270)
189 static void filestuff(int);
190 #endif
191 static int status(int, char *[]);
192 static const char *sockaddr_ntop (struct sockaddr *);
193 typedef int (*intrtn_t)(int, char **);
194 static int call(intrtn_t, ...);
195 static Command *getcmd(char *);
196 static int help(int, char *[]);
197 
198     static void
199 makeargv()
200 {
201     char *cp, *cp2, c;
202     char **argp = margv;
203 
204     margc = 0;
205     cp = line;
206     if (*cp == '!') {		/* Special case shell escape */
207 	strcpy(saveline, line);	/* save for shell command */
208 	*argp++ = "!";		/* No room in string to get this */
209 	margc++;
210 	cp++;
211     }
212     while ((c = *cp) != '\0') {
213 	int inquote = 0;
214 	while (isspace((unsigned char)c))
215 	    c = *++cp;
216 	if (c == '\0')
217 	    break;
218 	*argp++ = cp;
219 	margc += 1;
220 	for (cp2 = cp; c != '\0'; c = *++cp) {
221 	    if (inquote) {
222 		if (c == inquote) {
223 		    inquote = 0;
224 		    continue;
225 		}
226 	    } else {
227 		if (c == '\\') {
228 		    if ((c = *++cp) == '\0')
229 			break;
230 		} else if (c == '"') {
231 		    inquote = '"';
232 		    continue;
233 		} else if (c == '\'') {
234 		    inquote = '\'';
235 		    continue;
236 		} else if (isspace((unsigned char)c))
237 		    break;
238 	    }
239 	    *cp2++ = c;
240 	}
241 	*cp2 = '\0';
242 	if (c == '\0')
243 	    break;
244 	cp++;
245     }
246     *argp++ = 0;
247 }
248 
249 /*
250  * Make a character string into a number.
251  *
252  * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
253  */
254 
255 	static int
256 special(s)
257 	char *s;
258 {
259 	char c;
260 	char b;
261 
262 	switch (*s) {
263 	case '^':
264 		b = *++s;
265 		if (b == '?') {
266 		    c = b | 0x40;		/* DEL */
267 		} else {
268 		    c = b & 0x1f;
269 		}
270 		break;
271 	default:
272 		c = *s;
273 		break;
274 	}
275 	return c;
276 }
277 
278 /*
279  * Construct a control character sequence
280  * for a special character.
281  */
282 	static char *
283 control(c)
284 	cc_t c;
285 {
286 	static char buf[5];
287 	/*
288 	 * The only way I could get the Sun 3.5 compiler
289 	 * to shut up about
290 	 *	if ((unsigned int)c >= 0x80)
291 	 * was to assign "c" to an unsigned int variable...
292 	 * Arggg....
293 	 */
294 	unsigned int uic = (unsigned int)c;
295 
296 	if (uic == 0x7f)
297 		return ("^?");
298 	if (c == (cc_t)_POSIX_VDISABLE) {
299 		return "off";
300 	}
301 	if (uic >= 0x80) {
302 		buf[0] = '\\';
303 		buf[1] = ((c>>6)&07) + '0';
304 		buf[2] = ((c>>3)&07) + '0';
305 		buf[3] = (c&07) + '0';
306 		buf[4] = 0;
307 	} else if (uic >= 0x20) {
308 		buf[0] = c;
309 		buf[1] = 0;
310 	} else {
311 		buf[0] = '^';
312 		buf[1] = '@'+c;
313 		buf[2] = 0;
314 	}
315 	return (buf);
316 }
317 
318 
319 
320 /*
321  *	The following are data structures and routines for
322  *	the "send" command.
323  *
324  */
325 
326 struct sendlist {
327     char	*name;		/* How user refers to it (case independent) */
328     char	*help;		/* Help information (0 ==> no help) */
329     int		needconnect;	/* Need to be connected */
330     int		narg;		/* Number of arguments */
331     int		(*handler)	/* Routine to perform (for special ops) */
332 			(char *);
333     int		nbyte;		/* Number of bytes to send this command */
334     int		what;		/* Character to be sent (<0 ==> special) */
335 };
336 
337 
338 static struct sendlist Sendlist[] = {
339     { "ao",	"Send Telnet Abort output",		1, 0, 0, 2, AO },
340     { "ayt",	"Send Telnet 'Are You There'",		1, 0, 0, 2, AYT },
341     { "brk",	"Send Telnet Break",			1, 0, 0, 2, BREAK },
342     { "break",	0,					1, 0, 0, 2, BREAK },
343     { "ec",	"Send Telnet Erase Character",		1, 0, 0, 2, EC },
344     { "el",	"Send Telnet Erase Line",		1, 0, 0, 2, EL },
345     { "escape",	"Send current escape character",	1, 0, send_esc, 1, 0 },
346     { "ga",	"Send Telnet 'Go Ahead' sequence",	1, 0, 0, 2, GA },
347     { "ip",	"Send Telnet Interrupt Process",	1, 0, 0, 2, IP },
348     { "intp",	0,					1, 0, 0, 2, IP },
349     { "interrupt", 0,					1, 0, 0, 2, IP },
350     { "intr",	0,					1, 0, 0, 2, IP },
351     { "nop",	"Send Telnet 'No operation'",		1, 0, 0, 2, NOP },
352     { "eor",	"Send Telnet 'End of Record'",		1, 0, 0, 2, EOR },
353     { "abort",	"Send Telnet 'Abort Process'",		1, 0, 0, 2, ABORT },
354     { "susp",	"Send Telnet 'Suspend Process'",	1, 0, 0, 2, SUSP },
355     { "eof",	"Send Telnet End of File Character",	1, 0, 0, 2, xEOF },
356     { "synch",	"Perform Telnet 'Synch operation'",	1, 0, dosynch, 2, 0 },
357     { "getstatus", "Send request for STATUS",		1, 0, get_status, 6, 0 },
358     { "?",	"Display send options",			0, 0, send_help, 0, 0 },
359     { "help",	0,					0, 0, send_help, 0, 0 },
360     { "do",	0,					0, 1, send_docmd, 3, 0 },
361     { "dont",	0,					0, 1, send_dontcmd, 3, 0 },
362     { "will",	0,					0, 1, send_willcmd, 3, 0 },
363     { "wont",	0,					0, 1, send_wontcmd, 3, 0 },
364     { 0 }
365 };
366 
367 #define	GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \
368 				sizeof(struct sendlist)))
369 
370     static int
371 sendcmd(argc, argv)
372     int  argc;
373     char **argv;
374 {
375     int count;		/* how many bytes we are going to need to send */
376     int i;
377     struct sendlist *s;	/* pointer to current command */
378     int success = 0;
379     int needconnect = 0;
380 
381     if (argc < 2) {
382 	printf("need at least one argument for 'send' command\n");
383 	printf("'send ?' for help\n");
384 	return 0;
385     }
386     /*
387      * First, validate all the send arguments.
388      * In addition, we see how much space we are going to need, and
389      * whether or not we will be doing a "SYNCH" operation (which
390      * flushes the network queue).
391      */
392     count = 0;
393     for (i = 1; i < argc; i++) {
394 	s = GETSEND(argv[i]);
395 	if (s == 0) {
396 	    printf("Unknown send argument '%s'\n'send ?' for help.\n",
397 			argv[i]);
398 	    return 0;
399 	} else if (Ambiguous(s)) {
400 	    printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
401 			argv[i]);
402 	    return 0;
403 	}
404 	if (i + s->narg >= argc) {
405 	    fprintf(stderr,
406 	    "Need %d argument%s to 'send %s' command.  'send %s ?' for help.\n",
407 		s->narg, s->narg == 1 ? "" : "s", s->name, s->name);
408 	    return 0;
409 	}
410 	count += s->nbyte;
411 	if (s->handler == send_help) {
412 	    send_help(NULL);
413 	    return 0;
414 	}
415 
416 	i += s->narg;
417 	needconnect += s->needconnect;
418     }
419     if (!connected && needconnect) {
420 	printf("?Need to be connected first.\n");
421 	printf("'send ?' for help\n");
422 	return 0;
423     }
424     /* Now, do we have enough room? */
425     if (NETROOM() < count) {
426 	printf("There is not enough room in the buffer TO the network\n");
427 	printf("to process your request.  Nothing will be done.\n");
428 	printf("('send synch' will throw away most data in the network\n");
429 	printf("buffer, if this might help.)\n");
430 	return 0;
431     }
432     /* OK, they are all OK, now go through again and actually send */
433     count = 0;
434     for (i = 1; i < argc; i++) {
435 	if ((s = GETSEND(argv[i])) == 0) {
436 	    fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
437 	    (void) quit(0, NULL);
438 	    /*NOTREACHED*/
439 	}
440 	if (s->handler) {
441 	    count++;
442 	    success += (*s->handler)(argv[i+1]);
443 	    i += s->narg;
444 	} else {
445 	    NET2ADD(IAC, s->what);
446 	    printoption("SENT", IAC, s->what);
447 	}
448     }
449     return (count == success);
450 }
451 
452     static int
453 send_esc(s)
454     char *s;
455 {
456     NETADD(escape);
457     return 1;
458 }
459 
460     static int
461 send_docmd(name)
462     char *name;
463 {
464     return(send_tncmd(send_do, "do", name));
465 }
466 
467     static int
468 send_dontcmd(name)
469     char *name;
470 {
471     return(send_tncmd(send_dont, "dont", name));
472 }
473     static int
474 send_willcmd(name)
475     char *name;
476 {
477     return(send_tncmd(send_will, "will", name));
478 }
479     static int
480 send_wontcmd(name)
481     char *name;
482 {
483     return(send_tncmd(send_wont, "wont", name));
484 }
485 
486     int
487 send_tncmd(func, cmd, name)
488     void	(*func)(int, int);
489     char	*cmd, *name;
490 {
491     char **cpp;
492     extern char *telopts[];
493     int val = 0;
494 
495     if (isprefix(name, "?")) {
496 	int col, len;
497 
498 	printf("Usage: send %s <value|option>\n", cmd);
499 	printf("\"value\" must be from 0 to 255\n");
500 	printf("Valid options are:\n\t");
501 
502 	col = 8;
503 	for (cpp = telopts; *cpp; cpp++) {
504 	    len = strlen(*cpp) + 3;
505 	    if (col + len > 65) {
506 		printf("\n\t");
507 		col = 8;
508 	    }
509 	    printf(" \"%s\"", *cpp);
510 	    col += len;
511 	}
512 	printf("\n");
513 	return 0;
514     }
515     cpp = (char **)genget(name, telopts, sizeof(char *));
516     if (Ambiguous(cpp)) {
517 	fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\n",
518 					name, cmd);
519 	return 0;
520     }
521     if (cpp) {
522 	val = cpp - telopts;
523     } else {
524 	char *cp = name;
525 
526 	while (*cp >= '0' && *cp <= '9') {
527 	    val *= 10;
528 	    val += *cp - '0';
529 	    cp++;
530 	}
531 	if (*cp != 0) {
532 	    fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\n",
533 					name, cmd);
534 	    return 0;
535 	} else if (val < 0 || val > 255) {
536 	    fprintf(stderr, "'%s': bad value ('send %s ?' for help).\n",
537 					name, cmd);
538 	    return 0;
539 	}
540     }
541     if (!connected) {
542 	printf("?Need to be connected first.\n");
543 	return 0;
544     }
545     (*func)(val, 1);
546     return 1;
547 }
548 
549     static int
550 send_help(n)
551     char *n;
552 {
553     struct sendlist *s;	/* pointer to current command */
554     for (s = Sendlist; s->name; s++) {
555 	if (s->help)
556 	    printf("%-15s %s\n", s->name, s->help);
557     }
558     return(0);
559 }
560 
561 /*
562  * The following are the routines and data structures referred
563  * to by the arguments to the "toggle" command.
564  */
565 
566     static int
567 lclchars(n)
568     int n;
569 {
570     donelclchars = 1;
571     return 1;
572 }
573 
574     static int
575 togdebug(n)
576     int n;
577 {
578 #ifndef	NOT43
579     if (net > 0 &&
580 	(SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
581 	    perror("setsockopt (SO_DEBUG)");
582     }
583 #else	/* NOT43 */
584     if (debug) {
585 	if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0)
586 	    perror("setsockopt (SO_DEBUG)");
587     } else
588 	printf("Cannot turn off socket debugging\n");
589 #endif	/* NOT43 */
590     return 1;
591 }
592 
593 
594     static int
595 togcrlf(n)
596     int n;
597 {
598     if (crlf) {
599 	printf("Will send carriage returns as telnet <CR><LF>.\n");
600     } else {
601 	printf("Will send carriage returns as telnet <CR><NUL>.\n");
602     }
603     return 1;
604 }
605 
606 int binmode;
607 
608     static int
609 togbinary(val)
610     int val;
611 {
612     donebinarytoggle = 1;
613 
614     if (val >= 0) {
615 	binmode = val;
616     } else {
617 	if (my_want_state_is_will(TELOPT_BINARY) &&
618 				my_want_state_is_do(TELOPT_BINARY)) {
619 	    binmode = 1;
620 	} else if (my_want_state_is_wont(TELOPT_BINARY) &&
621 				my_want_state_is_dont(TELOPT_BINARY)) {
622 	    binmode = 0;
623 	}
624 	val = binmode ? 0 : 1;
625     }
626 
627     if (val == 1) {
628 	if (my_want_state_is_will(TELOPT_BINARY) &&
629 					my_want_state_is_do(TELOPT_BINARY)) {
630 	    printf("Already operating in binary mode with remote host.\n");
631 	} else {
632 	    printf("Negotiating binary mode with remote host.\n");
633 	    tel_enter_binary(3);
634 	}
635     } else {
636 	if (my_want_state_is_wont(TELOPT_BINARY) &&
637 					my_want_state_is_dont(TELOPT_BINARY)) {
638 	    printf("Already in network ascii mode with remote host.\n");
639 	} else {
640 	    printf("Negotiating network ascii mode with remote host.\n");
641 	    tel_leave_binary(3);
642 	}
643     }
644     return 1;
645 }
646 
647     static int
648 togrbinary(val)
649     int val;
650 {
651     donebinarytoggle = 1;
652 
653     if (val == -1)
654 	val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1;
655 
656     if (val == 1) {
657 	if (my_want_state_is_do(TELOPT_BINARY)) {
658 	    printf("Already receiving in binary mode.\n");
659 	} else {
660 	    printf("Negotiating binary mode on input.\n");
661 	    tel_enter_binary(1);
662 	}
663     } else {
664 	if (my_want_state_is_dont(TELOPT_BINARY)) {
665 	    printf("Already receiving in network ascii mode.\n");
666 	} else {
667 	    printf("Negotiating network ascii mode on input.\n");
668 	    tel_leave_binary(1);
669 	}
670     }
671     return 1;
672 }
673 
674     static int
675 togxbinary(val)
676     int val;
677 {
678     donebinarytoggle = 1;
679 
680     if (val == -1)
681 	val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1;
682 
683     if (val == 1) {
684 	if (my_want_state_is_will(TELOPT_BINARY)) {
685 	    printf("Already transmitting in binary mode.\n");
686 	} else {
687 	    printf("Negotiating binary mode on output.\n");
688 	    tel_enter_binary(2);
689 	}
690     } else {
691 	if (my_want_state_is_wont(TELOPT_BINARY)) {
692 	    printf("Already transmitting in network ascii mode.\n");
693 	} else {
694 	    printf("Negotiating network ascii mode on output.\n");
695 	    tel_leave_binary(2);
696 	}
697     }
698     return 1;
699 }
700 
701 #ifdef	ENCRYPTION
702 extern int EncryptAutoEnc(int);
703 extern int EncryptAutoDec(int);
704 extern int EncryptDebug(int);
705 extern int EncryptVerbose(int);
706 #endif	/* ENCRYPTION */
707 
708 struct togglelist {
709     char	*name;		/* name of toggle */
710     char	*help;		/* help message */
711     int		(*handler)	/* routine to do actual setting */
712 			(int);
713     int		*variable;
714     char	*actionexplanation;
715 };
716 
717 static struct togglelist Togglelist[] = {
718     { "autoflush",
719 	"flushing of output when sending interrupt characters",
720 	    0,
721 		&autoflush,
722 		    "flush output when sending interrupt characters" },
723     { "autosynch",
724 	"automatic sending of interrupt characters in urgent mode",
725 	    0,
726 		&autosynch,
727 		    "send interrupt characters in urgent mode" },
728 #if	defined(AUTHENTICATION)
729     { "autologin",
730 	"automatic sending of login and/or authentication info",
731 	    0,
732 		&autologin,
733 		    "send login name and/or authentication information" },
734     { "authdebug",
735 	"Toggle authentication debugging",
736 	    auth_togdebug,
737 		0,
738 		     "print authentication debugging information" },
739 #endif
740 #ifdef	ENCRYPTION
741     { "autoencrypt",
742       "automatic encryption of data stream",
743 	    EncryptAutoEnc,
744 		0,
745 		     "automatically encrypt output" },
746     { "autodecrypt",
747       "automatic decryption of data stream",
748 	    EncryptAutoDec,
749 		0,
750 		     "automatically decrypt input" },
751     { "verbose_encrypt",
752       "Toggle verbose encryption output",
753 	    EncryptVerbose,
754 		0,
755 		     "print verbose encryption output" },
756     { "encdebug",
757       "Toggle encryption debugging",
758 	    EncryptDebug,
759 		0,
760 		     "print encryption debugging information" },
761 #endif	/* ENCRYPTION */
762     { "skiprc",
763 	"don't read ~/.telnetrc file",
764 	    0,
765 		&skiprc,
766 		    "skip reading of ~/.telnetrc file" },
767     { "binary",
768 	"sending and receiving of binary data",
769 	    togbinary,
770 		0,
771 		    0 },
772     { "inbinary",
773 	"receiving of binary data",
774 	    togrbinary,
775 		0,
776 		    0 },
777     { "outbinary",
778 	"sending of binary data",
779 	    togxbinary,
780 		0,
781 		    0 },
782     { "crlf",
783 	"sending carriage returns as telnet <CR><LF>",
784 	   togcrlf,
785 		&crlf,
786 		    0 },
787     { "crmod",
788 	"mapping of received carriage returns",
789 	    0,
790 		&crmod,
791 		    "map carriage return on output" },
792     { "localchars",
793 	"local recognition of certain control characters",
794 	    lclchars,
795 		&localchars,
796 		    "recognize certain control characters" },
797     { " ", "", 0 },		/* empty line */
798 #if	defined(unix) && defined(TN3270)
799     { "apitrace",
800 	"(debugging) toggle tracing of API transactions",
801 	    0,
802 		&apitrace,
803 		    "trace API transactions" },
804     { "cursesdata",
805 	"(debugging) toggle printing of hexadecimal curses data",
806 	    0,
807 		&cursesdata,
808 		    "print hexadecimal representation of curses data" },
809 #endif	/* defined(unix) && defined(TN3270) */
810     { "debug",
811 	"debugging",
812 	    togdebug,
813 		&debug,
814 		    "turn on socket level debugging" },
815     { "netdata",
816 	"printing of hexadecimal network data (debugging)",
817 	    0,
818 		&netdata,
819 		    "print hexadecimal representation of network traffic" },
820     { "prettydump",
821 	"output of \"netdata\" to user readable format (debugging)",
822 	    0,
823 		&prettydump,
824 		    "print user readable output for \"netdata\"" },
825     { "options",
826 	"viewing of options processing (debugging)",
827 	    0,
828 		&showoptions,
829 		    "show option processing" },
830 #if	defined(unix)
831     { "termdata",
832 	"(debugging) toggle printing of hexadecimal terminal data",
833 	    0,
834 		&termdata,
835 		    "print hexadecimal representation of terminal traffic" },
836 #endif	/* defined(unix) */
837     { "?",
838 	0,
839 	    togglehelp },
840     { "help",
841 	0,
842 	    togglehelp },
843     { 0 }
844 };
845 
846     static int
847 togglehelp(n)
848     int n;
849 {
850     struct togglelist *c;
851 
852     for (c = Togglelist; c->name; c++) {
853 	if (c->help) {
854 	    if (*c->help)
855 		printf("%-15s toggle %s\n", c->name, c->help);
856 	    else
857 		printf("\n");
858 	}
859     }
860     printf("\n");
861     printf("%-15s %s\n", "?", "display help information");
862     return 0;
863 }
864 
865     static void
866 settogglehelp(set)
867     int set;
868 {
869     struct togglelist *c;
870 
871     for (c = Togglelist; c->name; c++) {
872 	if (c->help) {
873 	    if (*c->help)
874 		printf("%-15s %s %s\n", c->name, set ? "enable" : "disable",
875 						c->help);
876 	    else
877 		printf("\n");
878 	}
879     }
880 }
881 
882 #define	GETTOGGLE(name) (struct togglelist *) \
883 		genget(name, (char **) Togglelist, sizeof(struct togglelist))
884 
885     static int
886 toggle(argc, argv)
887     int  argc;
888     char *argv[];
889 {
890     int retval = 1;
891     char *name;
892     struct togglelist *c;
893 
894     if (argc < 2) {
895 	fprintf(stderr,
896 	    "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
897 	return 0;
898     }
899     argc--;
900     argv++;
901     while (argc--) {
902 	name = *argv++;
903 	c = GETTOGGLE(name);
904 	if (Ambiguous(c)) {
905 	    fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
906 					name);
907 	    return 0;
908 	} else if (c == 0) {
909 	    fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
910 					name);
911 	    return 0;
912 	} else {
913 	    if (c->variable) {
914 		*c->variable = !*c->variable;		/* invert it */
915 		if (c->actionexplanation) {
916 		    printf("%s %s.\n", *c->variable? "Will" : "Won't",
917 							c->actionexplanation);
918 		}
919 	    }
920 	    if (c->handler) {
921 		retval &= (*c->handler)(-1);
922 	    }
923 	}
924     }
925     return retval;
926 }
927 
928 /*
929  * The following perform the "set" command.
930  */
931 
932 #ifdef	USE_TERMIO
933 struct termio new_tc = { 0 };
934 #endif
935 
936 struct setlist {
937     char *name;				/* name */
938     char *help;				/* help information */
939     void (*handler)(char *);
940     cc_t *charp;			/* where it is located at */
941 };
942 
943 static struct setlist Setlist[] = {
944 #ifdef	KLUDGELINEMODE
945     { "echo", 	"character to toggle local echoing on/off", 0, &echoc },
946 #endif
947     { "escape",	"character to escape back to telnet command mode", 0, &escape },
948     { "rlogin", "rlogin escape character", 0, &rlogin },
949     { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile},
950     { " ", "" },
951     { " ", "The following need 'localchars' to be toggled true", 0, 0 },
952     { "flushoutput", "character to cause an Abort Output", 0, termFlushCharp },
953     { "interrupt", "character to cause an Interrupt Process", 0, termIntCharp },
954     { "quit",	"character to cause an Abort process", 0, termQuitCharp },
955     { "eof",	"character to cause an EOF ", 0, termEofCharp },
956     { " ", "" },
957     { " ", "The following are for local editing in linemode", 0, 0 },
958     { "erase",	"character to use to erase a character", 0, termEraseCharp },
959     { "kill",	"character to use to erase a line", 0, termKillCharp },
960     { "lnext",	"character to use for literal next", 0, termLiteralNextCharp },
961     { "susp",	"character to cause a Suspend Process", 0, termSuspCharp },
962     { "reprint", "character to use for line reprint", 0, termRprntCharp },
963     { "worderase", "character to use to erase a word", 0, termWerasCharp },
964     { "start",	"character to use for XON", 0, termStartCharp },
965     { "stop",	"character to use for XOFF", 0, termStopCharp },
966     { "forw1",	"alternate end of line character", 0, termForw1Charp },
967     { "forw2",	"alternate end of line character", 0, termForw2Charp },
968     { "ayt",	"alternate AYT character", 0, termAytCharp },
969     { 0 }
970 };
971 
972     static struct setlist *
973 getset(name)
974     char *name;
975 {
976     return (struct setlist *)
977 		genget(name, (char **) Setlist, sizeof(struct setlist));
978 }
979 
980     void
981 set_escape_char(s)
982     char *s;
983 {
984 	if (rlogin != _POSIX_VDISABLE) {
985 		rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE;
986 		printf("Telnet rlogin escape character is '%s'.\n",
987 					control(rlogin));
988 	} else {
989 		escape = (s && *s) ? special(s) : _POSIX_VDISABLE;
990 		printf("Telnet escape character is '%s'.\n", control(escape));
991 	}
992 }
993 
994     static int
995 setcmd(argc, argv)
996     int  argc;
997     char *argv[];
998 {
999     int value;
1000     struct setlist *ct;
1001     struct togglelist *c;
1002 
1003     if (argc < 2 || argc > 3) {
1004 	printf("Format is 'set Name Value'\n'set ?' for help.\n");
1005 	return 0;
1006     }
1007     if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) {
1008 	for (ct = Setlist; ct->name; ct++)
1009 	    printf("%-15s %s\n", ct->name, ct->help);
1010 	printf("\n");
1011 	settogglehelp(1);
1012 	printf("%-15s %s\n", "?", "display help information");
1013 	return 0;
1014     }
1015 
1016     ct = getset(argv[1]);
1017     if (ct == 0) {
1018 	c = GETTOGGLE(argv[1]);
1019 	if (c == 0) {
1020 	    fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
1021 			argv[1]);
1022 	    return 0;
1023 	} else if (Ambiguous(c)) {
1024 	    fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
1025 			argv[1]);
1026 	    return 0;
1027 	}
1028 	if (c->variable) {
1029 	    if ((argc == 2) || (strcmp("on", argv[2]) == 0))
1030 		*c->variable = 1;
1031 	    else if (strcmp("off", argv[2]) == 0)
1032 		*c->variable = 0;
1033 	    else {
1034 		printf("Format is 'set togglename [on|off]'\n'set ?' for help.\n");
1035 		return 0;
1036 	    }
1037 	    if (c->actionexplanation) {
1038 		printf("%s %s.\n", *c->variable? "Will" : "Won't",
1039 							c->actionexplanation);
1040 	    }
1041 	}
1042 	if (c->handler)
1043 	    (*c->handler)(1);
1044     } else if (argc != 3) {
1045 	printf("Format is 'set Name Value'\n'set ?' for help.\n");
1046 	return 0;
1047     } else if (Ambiguous(ct)) {
1048 	fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
1049 			argv[1]);
1050 	return 0;
1051     } else if (ct->handler) {
1052 	(*ct->handler)(argv[2]);
1053 	printf("%s set to \"%s\".\n", ct->name, (char *)ct->charp);
1054     } else {
1055 	if (strcmp("off", argv[2])) {
1056 	    value = special(argv[2]);
1057 	} else {
1058 	    value = _POSIX_VDISABLE;
1059 	}
1060 	*(ct->charp) = (cc_t)value;
1061 	printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
1062     }
1063     slc_check();
1064     return 1;
1065 }
1066 
1067     static int
1068 unsetcmd(argc, argv)
1069     int  argc;
1070     char *argv[];
1071 {
1072     struct setlist *ct;
1073     struct togglelist *c;
1074     char *name;
1075 
1076     if (argc < 2) {
1077 	fprintf(stderr,
1078 	    "Need an argument to 'unset' command.  'unset ?' for help.\n");
1079 	return 0;
1080     }
1081     if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) {
1082 	for (ct = Setlist; ct->name; ct++)
1083 	    printf("%-15s %s\n", ct->name, ct->help);
1084 	printf("\n");
1085 	settogglehelp(0);
1086 	printf("%-15s %s\n", "?", "display help information");
1087 	return 0;
1088     }
1089 
1090     argc--;
1091     argv++;
1092     while (argc--) {
1093 	name = *argv++;
1094 	ct = getset(name);
1095 	if (ct == 0) {
1096 	    c = GETTOGGLE(name);
1097 	    if (c == 0) {
1098 		fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\n",
1099 			name);
1100 		return 0;
1101 	    } else if (Ambiguous(c)) {
1102 		fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n",
1103 			name);
1104 		return 0;
1105 	    }
1106 	    if (c->variable) {
1107 		*c->variable = 0;
1108 		if (c->actionexplanation) {
1109 		    printf("%s %s.\n", *c->variable? "Will" : "Won't",
1110 							c->actionexplanation);
1111 		}
1112 	    }
1113 	    if (c->handler)
1114 		(*c->handler)(0);
1115 	} else if (Ambiguous(ct)) {
1116 	    fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n",
1117 			name);
1118 	    return 0;
1119 	} else if (ct->handler) {
1120 	    (*ct->handler)(0);
1121 	    printf("%s reset to \"%s\".\n", ct->name, (char *)ct->charp);
1122 	} else {
1123 	    *(ct->charp) = _POSIX_VDISABLE;
1124 	    printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
1125 	}
1126     }
1127     return 1;
1128 }
1129 
1130 /*
1131  * The following are the data structures and routines for the
1132  * 'mode' command.
1133  */
1134 #ifdef	KLUDGELINEMODE
1135 extern int kludgelinemode;
1136 
1137     static int
1138 dokludgemode(n)
1139     int n;
1140 {
1141     kludgelinemode = 1;
1142     send_wont(TELOPT_LINEMODE, 1);
1143     send_dont(TELOPT_SGA, 1);
1144     send_dont(TELOPT_ECHO, 1);
1145     return 1;
1146 }
1147 #endif
1148 
1149     static int
1150 dolinemode(n)
1151     int n;
1152 {
1153 #ifdef	KLUDGELINEMODE
1154     if (kludgelinemode)
1155 	send_dont(TELOPT_SGA, 1);
1156 #endif
1157     send_will(TELOPT_LINEMODE, 1);
1158     send_dont(TELOPT_ECHO, 1);
1159     return 1;
1160 }
1161 
1162     static int
1163 docharmode(n)
1164     int n;
1165 {
1166 #ifdef	KLUDGELINEMODE
1167     if (kludgelinemode)
1168 	send_do(TELOPT_SGA, 1);
1169     else
1170 #endif
1171     send_wont(TELOPT_LINEMODE, 1);
1172     send_do(TELOPT_ECHO, 1);
1173     return 1;
1174 }
1175 
1176     static int
1177 dolmmode(bit, on)
1178     int bit, on;
1179 {
1180     unsigned char c;
1181     extern int linemode;
1182 
1183     if (my_want_state_is_wont(TELOPT_LINEMODE)) {
1184 	printf("?Need to have LINEMODE option enabled first.\n");
1185 	printf("'mode ?' for help.\n");
1186 	return 0;
1187     }
1188 
1189     if (on)
1190 	c = (linemode | bit);
1191     else
1192 	c = (linemode & ~bit);
1193     lm_mode(&c, 1, 1);
1194     return 1;
1195 }
1196 
1197     int
1198 set_mode(bit)
1199     int bit;
1200 {
1201     return dolmmode(bit, 1);
1202 }
1203 
1204     int
1205 clear_mode(bit)
1206     int bit;
1207 {
1208     return dolmmode(bit, 0);
1209 }
1210 
1211 struct modelist {
1212 	char	*name;		/* command name */
1213 	char	*help;		/* help string */
1214 	int	(*handler)	/* routine which executes command */
1215 			(int);
1216 	int	needconnect;	/* Do we need to be connected to execute? */
1217 	int	arg1;
1218 };
1219 
1220 static struct modelist ModeList[] = {
1221     { "character", "Disable LINEMODE option",	docharmode, 1 },
1222 #ifdef	KLUDGELINEMODE
1223     { "",	"(or disable obsolete line-by-line mode)", 0 },
1224 #endif
1225     { "line",	"Enable LINEMODE option",	dolinemode, 1 },
1226 #ifdef	KLUDGELINEMODE
1227     { "",	"(or enable obsolete line-by-line mode)", 0 },
1228 #endif
1229     { "", "", 0 },
1230     { "",	"These require the LINEMODE option to be enabled", 0 },
1231     { "isig",	"Enable signal trapping",	set_mode, 1, MODE_TRAPSIG },
1232     { "+isig",	0,				set_mode, 1, MODE_TRAPSIG },
1233     { "-isig",	"Disable signal trapping",	clear_mode, 1, MODE_TRAPSIG },
1234     { "edit",	"Enable character editing",	set_mode, 1, MODE_EDIT },
1235     { "+edit",	0,				set_mode, 1, MODE_EDIT },
1236     { "-edit",	"Disable character editing",	clear_mode, 1, MODE_EDIT },
1237     { "softtabs", "Enable tab expansion",	set_mode, 1, MODE_SOFT_TAB },
1238     { "+softtabs", 0,				set_mode, 1, MODE_SOFT_TAB },
1239     { "-softtabs", "Disable character editing",	clear_mode, 1, MODE_SOFT_TAB },
1240     { "litecho", "Enable literal character echo", set_mode, 1, MODE_LIT_ECHO },
1241     { "+litecho", 0,				set_mode, 1, MODE_LIT_ECHO },
1242     { "-litecho", "Disable literal character echo", clear_mode, 1, MODE_LIT_ECHO },
1243     { "help",	0,				modehelp, 0 },
1244 #ifdef	KLUDGELINEMODE
1245     { "kludgeline", 0,				dokludgemode, 1 },
1246 #endif
1247     { "", "", 0 },
1248     { "?",	"Print help information",	modehelp, 0 },
1249     { 0 },
1250 };
1251 
1252 
1253     int
1254 modehelp(n)
1255     int n;
1256 {
1257     struct modelist *mt;
1258 
1259     printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
1260     for (mt = ModeList; mt->name; mt++) {
1261 	if (mt->help) {
1262 	    if (*mt->help)
1263 		printf("%-15s %s\n", mt->name, mt->help);
1264 	    else
1265 		printf("\n");
1266 	}
1267     }
1268     return 0;
1269 }
1270 
1271 #define	GETMODECMD(name) (struct modelist *) \
1272 		genget(name, (char **) ModeList, sizeof(struct modelist))
1273 
1274     static int
1275 modecmd(argc, argv)
1276     int  argc;
1277     char *argv[];
1278 {
1279     struct modelist *mt;
1280 
1281     if (argc != 2) {
1282 	printf("'mode' command requires an argument\n");
1283 	printf("'mode ?' for help.\n");
1284     } else if ((mt = GETMODECMD(argv[1])) == 0) {
1285 	fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
1286     } else if (Ambiguous(mt)) {
1287 	fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
1288     } else if (mt->needconnect && !connected) {
1289 	printf("?Need to be connected first.\n");
1290 	printf("'mode ?' for help.\n");
1291     } else if (mt->handler) {
1292 	return (*mt->handler)(mt->arg1);
1293     }
1294     return 0;
1295 }
1296 
1297 /*
1298  * The following data structures and routines implement the
1299  * "display" command.
1300  */
1301 
1302     static int
1303 display(argc, argv)
1304     int  argc;
1305     char *argv[];
1306 {
1307     struct togglelist *tl;
1308     struct setlist *sl;
1309 
1310 #define	dotog(tl)	if (tl->variable && tl->actionexplanation) { \
1311 			    if (*tl->variable) { \
1312 				printf("will"); \
1313 			    } else { \
1314 				printf("won't"); \
1315 			    } \
1316 			    printf(" %s.\n", tl->actionexplanation); \
1317 			}
1318 
1319 #define	doset(sl)   if (sl->name && *sl->name != ' ') { \
1320 			if (sl->handler == 0) \
1321 			    printf("%-15s [%s]\n", sl->name, control(*sl->charp)); \
1322 			else \
1323 			    printf("%-15s \"%s\"\n", sl->name, (char *)sl->charp); \
1324 		    }
1325 
1326     if (argc == 1) {
1327 	for (tl = Togglelist; tl->name; tl++) {
1328 	    dotog(tl);
1329 	}
1330 	printf("\n");
1331 	for (sl = Setlist; sl->name; sl++) {
1332 	    doset(sl);
1333 	}
1334     } else {
1335 	int i;
1336 
1337 	for (i = 1; i < argc; i++) {
1338 	    sl = getset(argv[i]);
1339 	    tl = GETTOGGLE(argv[i]);
1340 	    if (Ambiguous(sl) || Ambiguous(tl)) {
1341 		printf("?Ambiguous argument '%s'.\n", argv[i]);
1342 		return 0;
1343 	    } else if (!sl && !tl) {
1344 		printf("?Unknown argument '%s'.\n", argv[i]);
1345 		return 0;
1346 	    } else {
1347 		if (tl) {
1348 		    dotog(tl);
1349 		}
1350 		if (sl) {
1351 		    doset(sl);
1352 		}
1353 	    }
1354 	}
1355     }
1356 /*@*/optionstatus();
1357 #ifdef	ENCRYPTION
1358     EncryptStatus();
1359 #endif	/* ENCRYPTION */
1360     return 1;
1361 #undef	doset
1362 #undef	dotog
1363 }
1364 
1365 /*
1366  * The following are the data structures, and many of the routines,
1367  * relating to command processing.
1368  */
1369 
1370 /*
1371  * Set the escape character.
1372  */
1373 	static int
1374 setescape(argc, argv)
1375 	int argc;
1376 	char *argv[];
1377 {
1378 	char *arg;
1379 	char buf[50];
1380 
1381 	printf(
1382 	    "Deprecated usage - please use 'set escape%s%s' in the future.\n",
1383 				(argc > 2)? " ":"", (argc > 2)? argv[1]: "");
1384 	if (argc > 2)
1385 		arg = argv[1];
1386 	else {
1387 		printf("new escape character: ");
1388 		(void) fgets(buf, sizeof(buf), stdin);
1389 		arg = buf;
1390 	}
1391 	if (arg[0] != '\0')
1392 		escape = arg[0];
1393 	if (!In3270) {
1394 		printf("Escape character is '%s'.\n", control(escape));
1395 	}
1396 	(void) fflush(stdout);
1397 	return 1;
1398 }
1399 
1400     /*VARARGS*/
1401     static int
1402 togcrmod(argc, argv)
1403     int argc;
1404     char *argv[];
1405 {
1406     crmod = !crmod;
1407     printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
1408     printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
1409     (void) fflush(stdout);
1410     return 1;
1411 }
1412 
1413     /*VARARGS*/
1414     int
1415 suspend(argc, argv)
1416     int argc;
1417     char *argv[];
1418 {
1419 #ifdef	SIGTSTP
1420     setcommandmode();
1421     {
1422 	long oldrows, oldcols, newrows, newcols, err;
1423 
1424 	err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1425 	(void) kill(0, SIGTSTP);
1426 	/*
1427 	 * If we didn't get the window size before the SUSPEND, but we
1428 	 * can get them now (?), then send the NAWS to make sure that
1429 	 * we are set up for the right window size.
1430 	 */
1431 	if (TerminalWindowSize(&newrows, &newcols) && connected &&
1432 	    (err || ((oldrows != newrows) || (oldcols != newcols)))) {
1433 		sendnaws();
1434 	}
1435     }
1436     /* reget parameters in case they were changed */
1437     TerminalSaveState();
1438     setconnmode(0);
1439 #else
1440     printf("Suspend is not supported.  Try the '!' command instead\n");
1441 #endif
1442     return 1;
1443 }
1444 
1445 #if	!defined(TN3270)
1446     /*ARGSUSED*/
1447     int
1448 shell(argc, argv)
1449     int argc;
1450     char *argv[];
1451 {
1452     long oldrows, oldcols, newrows, newcols, err;
1453 
1454 #ifdef __GNUC__
1455     (void) &err;	/* XXX avoid GCC warning */
1456 #endif
1457 
1458     setcommandmode();
1459 
1460     err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1461     switch(vfork()) {
1462     case -1:
1463 	perror("Fork failed");
1464 	break;
1465 
1466     case 0:
1467 	{
1468 	    /*
1469 	     * Fire up the shell in the child.
1470 	     */
1471 	    char *shellp, *shellname;
1472 
1473 	    shellp = getenv("SHELL");
1474 	    if (shellp == NULL)
1475 		shellp = "/bin/sh";
1476 	    if ((shellname = strrchr(shellp, '/')) == 0)
1477 		shellname = shellp;
1478 	    else
1479 		shellname++;
1480 	    if (argc > 1)
1481 		execl(shellp, shellname, "-c", &saveline[1], 0);
1482 	    else
1483 		execl(shellp, shellname, 0);
1484 	    perror("execl");
1485 	    _exit(1);
1486 	}
1487     default:
1488 	    (void)wait((int *)0);	/* Wait for the shell to complete */
1489 
1490 	    if (TerminalWindowSize(&newrows, &newcols) && connected &&
1491 		(err || ((oldrows != newrows) || (oldcols != newcols)))) {
1492 		    sendnaws();
1493 	    }
1494 	    break;
1495     }
1496     return 1;
1497 }
1498 #endif	/* !defined(TN3270) */
1499 
1500     /*VARARGS*/
1501     static int
1502 bye(argc, argv)
1503     int  argc;		/* Number of arguments */
1504     char *argv[];	/* arguments */
1505 {
1506     extern int resettermname;
1507 
1508     if (connected) {
1509 	(void) shutdown(net, 2);
1510 	printf("Connection closed.\n");
1511 	(void) NetClose(net);
1512 	connected = 0;
1513 	resettermname = 1;
1514 #if	defined(AUTHENTICATION) || defined(ENCRYPTION)
1515 	auth_encrypt_connect(connected);
1516 #endif	/* defined(AUTHENTICATION) */
1517 	/* reset options */
1518 	tninit();
1519 #if	defined(TN3270)
1520 	SetIn3270();		/* Get out of 3270 mode */
1521 #endif	/* defined(TN3270) */
1522     }
1523     if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
1524 	longjmp(toplevel, 1);
1525 	/* NOTREACHED */
1526     }
1527     return 1;			/* Keep lint, etc., happy */
1528 }
1529 
1530 /*VARARGS*/
1531 int
1532 quit(argc, argv)
1533 	int argc;
1534 	char *argv[];
1535 {
1536 	(void) call(bye, "bye", "fromquit", 0);
1537 	Exit(0);
1538 	/*NOTREACHED*/
1539 }
1540 
1541 /*VARARGS*/
1542 	int
1543 logout(argc, argv)
1544 	int argc;
1545 	char *argv[];
1546 {
1547 	send_do(TELOPT_LOGOUT, 1);
1548 	(void) netflush();
1549 	return 1;
1550 }
1551 
1552 
1553 /*
1554  * The SLC command.
1555  */
1556 
1557 struct slclist {
1558 	char	*name;
1559 	char	*help;
1560 	void	(*handler)(int);
1561 	int	arg;
1562 };
1563 
1564 struct slclist SlcList[] = {
1565     { "export",	"Use local special character definitions",
1566 						slc_mode_export,	0 },
1567     { "import",	"Use remote special character definitions",
1568 						slc_mode_import,	1 },
1569     { "check",	"Verify remote special character definitions",
1570 						slc_mode_import,	0 },
1571     { "help",	0,				slc_help,		0 },
1572     { "?",	"Print help information",	slc_help,		0 },
1573     { 0 },
1574 };
1575 
1576     static void
1577 slc_help(n)
1578     int n;
1579 {
1580     struct slclist *c;
1581 
1582     for (c = SlcList; c->name; c++) {
1583 	if (c->help) {
1584 	    if (*c->help)
1585 		printf("%-15s %s\n", c->name, c->help);
1586 	    else
1587 		printf("\n");
1588 	}
1589     }
1590 }
1591 
1592     static struct slclist *
1593 getslc(name)
1594     char *name;
1595 {
1596     return (struct slclist *)
1597 		genget(name, (char **) SlcList, sizeof(struct slclist));
1598 }
1599 
1600     static int
1601 slccmd(argc, argv)
1602     int  argc;
1603     char *argv[];
1604 {
1605     struct slclist *c;
1606 
1607     if (argc != 2) {
1608 	fprintf(stderr,
1609 	    "Need an argument to 'slc' command.  'slc ?' for help.\n");
1610 	return 0;
1611     }
1612     c = getslc(argv[1]);
1613     if (c == 0) {
1614 	fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\n",
1615     				argv[1]);
1616 	return 0;
1617     }
1618     if (Ambiguous(c)) {
1619 	fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\n",
1620     				argv[1]);
1621 	return 0;
1622     }
1623     (*c->handler)(c->arg);
1624     slcstate();
1625     return 1;
1626 }
1627 
1628 /*
1629  * The ENVIRON command.
1630  */
1631 
1632 struct envlist {
1633 	char	*name;
1634 	char	*help;
1635 	struct env_lst *(*handler)(unsigned char *, unsigned char *);
1636 	int	narg;
1637 };
1638 
1639 struct envlist EnvList[] = {
1640     { "define",	"Define an environment variable",
1641 						env_define,	2 },
1642     { "undefine", "Undefine an environment variable",
1643 						env_undefine,	1 },
1644     { "export",	"Mark an environment variable for automatic export",
1645 						env_export,	1 },
1646     { "unexport", "Don't mark an environment variable for automatic export",
1647 						env_unexport,	1 },
1648     { "send",	"Send an environment variable", env_send,	1 },
1649     { "list",	"List the current environment variables",
1650 						env_list,	0 },
1651 #if defined(OLD_ENVIRON) && defined(ENV_HACK)
1652     { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)",
1653 						env_varval,    1 },
1654 #endif
1655     { "help",	0,				env_help,		0 },
1656     { "?",	"Print help information",	env_help,		0 },
1657     { 0 },
1658 };
1659 
1660     static struct env_lst *
1661 env_help(us1, us2)
1662     unsigned char *us1, *us2;
1663 {
1664     struct envlist *c;
1665 
1666     for (c = EnvList; c->name; c++) {
1667 	if (c->help) {
1668 	    if (*c->help)
1669 		printf("%-15s %s\n", c->name, c->help);
1670 	    else
1671 		printf("\n");
1672 	}
1673     }
1674     return NULL;
1675 }
1676 
1677     static struct envlist *
1678 getenvcmd(name)
1679     char *name;
1680 {
1681     return (struct envlist *)
1682 		genget(name, (char **) EnvList, sizeof(struct envlist));
1683 }
1684 
1685     int
1686 env_cmd(argc, argv)
1687     int  argc;
1688     char *argv[];
1689 {
1690     struct envlist *c;
1691 
1692     if (argc < 2) {
1693 	fprintf(stderr,
1694 	    "Need an argument to 'environ' command.  'environ ?' for help.\n");
1695 	return 0;
1696     }
1697     c = getenvcmd(argv[1]);
1698     if (c == 0) {
1699 	fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\n",
1700     				argv[1]);
1701 	return 0;
1702     }
1703     if (Ambiguous(c)) {
1704 	fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\n",
1705     				argv[1]);
1706 	return 0;
1707     }
1708     if (c->narg + 2 != argc) {
1709 	fprintf(stderr,
1710 	    "Need %s%d argument%s to 'environ %s' command.  'environ ?' for help.\n",
1711 		c->narg < argc + 2 ? "only " : "",
1712 		c->narg, c->narg == 1 ? "" : "s", c->name);
1713 	return 0;
1714     }
1715     (*c->handler)(argv[2], argv[3]);
1716     return 1;
1717 }
1718 
1719 struct env_lst {
1720 	struct env_lst *next;	/* pointer to next structure */
1721 	struct env_lst *prev;	/* pointer to previous structure */
1722 	unsigned char *var;	/* pointer to variable name */
1723 	unsigned char *value;	/* pointer to variable value */
1724 	int export;		/* 1 -> export with default list of variables */
1725 	int welldefined;	/* A well defined variable */
1726 };
1727 
1728 struct env_lst envlisthead;
1729 
1730 	struct env_lst *
1731 env_find(var)
1732 	unsigned char *var;
1733 {
1734 	struct env_lst *ep;
1735 
1736 	for (ep = envlisthead.next; ep; ep = ep->next) {
1737 		if (strcmp((char *)ep->var, (char *)var) == 0)
1738 			return(ep);
1739 	}
1740 	return(NULL);
1741 }
1742 
1743 	void
1744 env_init()
1745 {
1746 	extern char **environ;
1747 	char **epp, *cp;
1748 	struct env_lst *ep;
1749 
1750 	for (epp = environ; *epp; epp++) {
1751 		if ((cp = strchr(*epp, '=')) != NULL) {
1752 			*cp = '\0';
1753 			ep = env_define((unsigned char *)*epp,
1754 					(unsigned char *)cp+1);
1755 			ep->export = 0;
1756 			*cp = '=';
1757 		}
1758 	}
1759 	/*
1760 	 * Special case for DISPLAY variable.  If it is ":0.0" or
1761 	 * "unix:0.0", we have to get rid of "unix" and insert our
1762 	 * hostname.
1763 	 */
1764 	if ((ep = env_find("DISPLAY"))
1765 	    && ((*ep->value == ':')
1766 		|| (strncmp((char *)ep->value, "unix:", 5) == 0))) {
1767 		char hbuf[MAXHOSTNAMELEN + 1];
1768 		char *cp2 = strchr((char *)ep->value, ':');
1769 
1770 		gethostname(hbuf, sizeof hbuf);
1771 		hbuf[sizeof(hbuf) - 1] = '\0';
1772 		cp = (char *)malloc(strlen(hbuf) + strlen(cp2) + 1);
1773 		sprintf((char *)cp, "%s%s", hbuf, cp2);
1774 		free(ep->value);
1775 		ep->value = (unsigned char *)cp;
1776 	}
1777 	/*
1778 	 * If USER is not defined, but LOGNAME is, then add
1779 	 * USER with the value from LOGNAME.  By default, we
1780 	 * don't export the USER variable.
1781 	 */
1782 	if ((env_find("USER") == NULL) && (ep = env_find("LOGNAME"))) {
1783 		env_define((unsigned char *)"USER", ep->value);
1784 		env_unexport((unsigned char *)"USER", NULL);
1785 	}
1786 	env_export((unsigned char *)"DISPLAY", NULL);
1787 	env_export((unsigned char *)"PRINTER", NULL);
1788 }
1789 
1790 	struct env_lst *
1791 env_define(var, value)
1792 	unsigned char *var, *value;
1793 {
1794 	struct env_lst *ep;
1795 
1796 	if ((ep = env_find(var)) != NULL) {
1797 		if (ep->var)
1798 			free(ep->var);
1799 		if (ep->value)
1800 			free(ep->value);
1801 	} else {
1802 		ep = (struct env_lst *)malloc(sizeof(struct env_lst));
1803 		ep->next = envlisthead.next;
1804 		envlisthead.next = ep;
1805 		ep->prev = &envlisthead;
1806 		if (ep->next)
1807 			ep->next->prev = ep;
1808 	}
1809 	ep->welldefined = opt_welldefined(var);
1810 	ep->export = 1;
1811 	ep->var = (unsigned char *)strdup((char *)var);
1812 	ep->value = (unsigned char *)strdup((char *)value);
1813 	return(ep);
1814 }
1815 
1816 	struct env_lst *
1817 env_undefine(var, d)
1818 	unsigned char *var;
1819 	unsigned char *d;
1820 {
1821 	struct env_lst *ep;
1822 
1823 	if ((ep = env_find(var)) != NULL) {
1824 		ep->prev->next = ep->next;
1825 		if (ep->next)
1826 			ep->next->prev = ep->prev;
1827 		if (ep->var)
1828 			free(ep->var);
1829 		if (ep->value)
1830 			free(ep->value);
1831 		free(ep);
1832 	}
1833 	return NULL;
1834 }
1835 
1836 	struct env_lst *
1837 env_export(var, d)
1838 	unsigned char *var;
1839 	unsigned char *d;
1840 {
1841 	struct env_lst *ep;
1842 
1843 	if ((ep = env_find(var)) != NULL)
1844 		ep->export = 1;
1845 	return NULL;
1846 }
1847 
1848 	struct env_lst *
1849 env_unexport(var, d)
1850 	unsigned char *var;
1851 	unsigned char *d;
1852 {
1853 	struct env_lst *ep;
1854 
1855 	if ((ep = env_find(var)) != NULL)
1856 		ep->export = 0;
1857 	return NULL;
1858 }
1859 
1860 	struct env_lst *
1861 env_send(var, d)
1862 	unsigned char *var;
1863 	unsigned char *d;
1864 {
1865 	struct env_lst *ep;
1866 
1867 	if (my_state_is_wont(TELOPT_NEW_ENVIRON)
1868 #ifdef	OLD_ENVIRON
1869 	    && my_state_is_wont(TELOPT_OLD_ENVIRON)
1870 #endif
1871 		) {
1872 		fprintf(stderr,
1873 		    "Cannot send '%s': Telnet ENVIRON option not enabled\n",
1874 									var);
1875 		return NULL;
1876 	}
1877 	ep = env_find(var);
1878 	if (ep == 0) {
1879 		fprintf(stderr, "Cannot send '%s': variable not defined\n",
1880 									var);
1881 		return NULL;
1882 	}
1883 	env_opt_start_info();
1884 	env_opt_add(ep->var);
1885 	env_opt_end(0);
1886 	return NULL;
1887 }
1888 
1889 	struct env_lst *
1890 env_list(d1, d2)
1891 	unsigned char *d1, *d2;
1892 {
1893 	struct env_lst *ep;
1894 
1895 	for (ep = envlisthead.next; ep; ep = ep->next) {
1896 		printf("%c %-20s %s\n", ep->export ? '*' : ' ',
1897 					ep->var, ep->value);
1898 	}
1899 	return NULL;
1900 }
1901 
1902 	unsigned char *
1903 env_default(init, welldefined)
1904 	int init;
1905 	int welldefined;
1906 {
1907 	static struct env_lst *nep = NULL;
1908 
1909 	if (init) {
1910 		nep = &envlisthead;
1911 		return NULL;
1912 	}
1913 	if (nep) {
1914 		while ((nep = nep->next) != NULL) {
1915 			if (nep->export && (nep->welldefined == welldefined))
1916 				return(nep->var);
1917 		}
1918 	}
1919 	return(NULL);
1920 }
1921 
1922 	unsigned char *
1923 env_getvalue(var)
1924 	unsigned char *var;
1925 {
1926 	struct env_lst *ep;
1927 
1928 	if ((ep = env_find(var)) != NULL)
1929 		return(ep->value);
1930 	return(NULL);
1931 }
1932 
1933 #if defined(OLD_ENVIRON) && defined(ENV_HACK)
1934 	void
1935 env_varval(what)
1936 	unsigned char *what;
1937 {
1938 	extern int old_env_var, old_env_value, env_auto;
1939 	int len = strlen((char *)what);
1940 
1941 	if (len == 0)
1942 		goto unknown;
1943 
1944 	if (strncasecmp((char *)what, "status", len) == 0) {
1945 		if (env_auto)
1946 			printf("%s%s", "VAR and VALUE are/will be ",
1947 					"determined automatically\n");
1948 		if (old_env_var == OLD_ENV_VAR)
1949 			printf("VAR and VALUE set to correct definitions\n");
1950 		else
1951 			printf("VAR and VALUE definitions are reversed\n");
1952 	} else if (strncasecmp((char *)what, "auto", len) == 0) {
1953 		env_auto = 1;
1954 		old_env_var = OLD_ENV_VALUE;
1955 		old_env_value = OLD_ENV_VAR;
1956 	} else if (strncasecmp((char *)what, "right", len) == 0) {
1957 		env_auto = 0;
1958 		old_env_var = OLD_ENV_VAR;
1959 		old_env_value = OLD_ENV_VALUE;
1960 	} else if (strncasecmp((char *)what, "wrong", len) == 0) {
1961 		env_auto = 0;
1962 		old_env_var = OLD_ENV_VALUE;
1963 		old_env_value = OLD_ENV_VAR;
1964 	} else {
1965 unknown:
1966 		printf("Unknown \"varval\" command. (\"auto\", \"right\", \"wrong\", \"status\")\n");
1967 	}
1968 }
1969 #endif
1970 
1971 #if	defined(AUTHENTICATION)
1972 /*
1973  * The AUTHENTICATE command.
1974  */
1975 
1976 struct authlist {
1977 	char	*name;
1978 	char	*help;
1979 	int	(*handler)(char *);
1980 	int	narg;
1981 };
1982 
1983 struct authlist AuthList[] = {
1984     { "status",	"Display current status of authentication information",
1985 						auth_status,	0 },
1986     { "disable", "Disable an authentication type ('auth disable ?' for more)",
1987 						auth_disable,	1 },
1988     { "enable", "Enable an authentication type ('auth enable ?' for more)",
1989 						auth_enable,	1 },
1990     { "help",	0,				auth_help,		0 },
1991     { "?",	"Print help information",	auth_help,		0 },
1992     { 0 },
1993 };
1994 
1995     static int
1996 auth_help(s)
1997     char *s;
1998 {
1999     struct authlist *c;
2000 
2001     for (c = AuthList; c->name; c++) {
2002 	if (c->help) {
2003 	    if (*c->help)
2004 		printf("%-15s %s\n", c->name, c->help);
2005 	    else
2006 		printf("\n");
2007 	}
2008     }
2009     return 0;
2010 }
2011 
2012     int
2013 auth_cmd(argc, argv)
2014     int  argc;
2015     char *argv[];
2016 {
2017     struct authlist *c;
2018 
2019     if (argc < 2) {
2020 	fprintf(stderr,
2021 	    "Need an argument to 'auth' command.  'auth ?' for help.\n");
2022 	return 0;
2023     }
2024 
2025     c = (struct authlist *)
2026 		genget(argv[1], (char **) AuthList, sizeof(struct authlist));
2027     if (c == 0) {
2028 	fprintf(stderr, "'%s': unknown argument ('auth ?' for help).\n",
2029     				argv[1]);
2030 	return 0;
2031     }
2032     if (Ambiguous(c)) {
2033 	fprintf(stderr, "'%s': ambiguous argument ('auth ?' for help).\n",
2034     				argv[1]);
2035 	return 0;
2036     }
2037     if (c->narg + 2 != argc) {
2038 	fprintf(stderr,
2039 	    "Need %s%d argument%s to 'auth %s' command.  'auth ?' for help.\n",
2040 		c->narg < argc + 2 ? "only " : "",
2041 		c->narg, c->narg == 1 ? "" : "s", c->name);
2042 	return 0;
2043     }
2044     return((*c->handler)(argv[2]));
2045 }
2046 #endif
2047 
2048 #ifdef	ENCRYPTION
2049 /*
2050  * The ENCRYPT command.
2051  */
2052 
2053 struct encryptlist {
2054 	char	*name;
2055 	char	*help;
2056 	int	(*handler)(char *, char *);
2057 	int	needconnect;
2058 	int	minarg;
2059 	int	maxarg;
2060 };
2061 
2062 static int
2063 	EncryptHelp(char *, char *);
2064 typedef int (*encrypthandler)(char *, char *);
2065 
2066 struct encryptlist EncryptList[] = {
2067     { "enable", "Enable encryption. ('encrypt enable ?' for more)",
2068 						EncryptEnable, 1, 1, 2 },
2069     { "disable", "Disable encryption. ('encrypt enable ?' for more)",
2070 						EncryptDisable, 0, 1, 2 },
2071     { "type", "Set encryption type. ('encrypt type ?' for more)",
2072 						EncryptType, 0, 1, 1 },
2073     { "start", "Start encryption. ('encrypt start ?' for more)",
2074 				(encrypthandler) EncryptStart, 1, 0, 1 },
2075     { "stop", "Stop encryption. ('encrypt stop ?' for more)",
2076 				(encrypthandler) EncryptStop, 1, 0, 1 },
2077     { "input", "Start encrypting the input stream",
2078 				(encrypthandler) EncryptStartInput, 1, 0, 0 },
2079     { "-input", "Stop encrypting the input stream",
2080 				(encrypthandler) EncryptStopInput, 1, 0, 0 },
2081     { "output", "Start encrypting the output stream",
2082 				(encrypthandler) EncryptStartOutput, 1, 0, 0 },
2083     { "-output", "Stop encrypting the output stream",
2084 				(encrypthandler) EncryptStopOutput, 1, 0, 0 },
2085 
2086     { "status",       "Display current status of authentication information",
2087 				(encrypthandler) EncryptStatus,	0, 0, 0 },
2088     { "help", 0,				 EncryptHelp,	0, 0, 0 },
2089     { "?",    "Print help information",		 EncryptHelp,	0, 0, 0 },
2090     { 0 },
2091 };
2092 
2093 static int
2094 EncryptHelp(s1, s2)
2095 	char *s1, *s2;
2096 {
2097 	struct encryptlist *c;
2098 
2099 	for (c = EncryptList; c->name; c++) {
2100 		if (c->help) {
2101 			if (*c->help)
2102 				printf("%-15s %s\n", c->name, c->help);
2103 			else
2104 				printf("\n");
2105 		}
2106 	}
2107 	return (0);
2108 }
2109 
2110 int
2111 encrypt_cmd(argc, argv)
2112 	int argc;
2113 	char *argv[];
2114 {
2115 	struct encryptlist *c;
2116 
2117 	if (argc < 2) {
2118 		fprintf(stderr,
2119 		    "Need an argument to 'encrypt' command.  "
2120 		    "'encrypt ?' for help.\n");
2121 		return (0);
2122 	}
2123 
2124 	c = (struct encryptlist *)
2125 	    genget(argv[1], (char **) EncryptList, sizeof(struct encryptlist));
2126 	if (c == NULL) {
2127 		fprintf(stderr,
2128 		    "'%s': unknown argument ('encrypt ?' for help).\n",
2129 		    argv[1]);
2130 		return (0);
2131 	}
2132 	if (Ambiguous(c)) {
2133 		fprintf(stderr,
2134 		    "'%s': ambiguous argument ('encrypt ?' for help).\n",
2135 		    argv[1]);
2136 		return (0);
2137 	}
2138 	argc -= 2;
2139 	if (argc < c->minarg || argc > c->maxarg) {
2140 		if (c->minarg == c->maxarg) {
2141 			fprintf(stderr, "Need %s%d argument%s ",
2142 			    c->minarg < argc ? "only " : "", c->minarg,
2143 			    c->minarg == 1 ? "" : "s");
2144 		} else {
2145 			fprintf(stderr, "Need %s%d-%d arguments ",
2146 			    c->maxarg < argc ? "only " : "", c->minarg,
2147 			    c->maxarg);
2148 		}
2149 		fprintf(stderr,
2150 		    "to 'encrypt %s' command.  'encrypt ?' for help.\n",
2151 		    c->name);
2152 		return (0);
2153 	}
2154 	if (c->needconnect && !connected) {
2155 		if (!(argc && (isprefix(argv[2], "help") ||
2156 		    isprefix(argv[2], "?")))) {
2157 			printf("?Need to be connected first.\n");
2158 			return (0);
2159 		}
2160 	}
2161 	return ((*c->handler)(argv[2], argv[3]));
2162 }
2163 #endif	/* ENCRYPTION */
2164 
2165 #if	defined(unix) && defined(TN3270)
2166     static void
2167 filestuff(fd)
2168     int fd;
2169 {
2170     int res;
2171 
2172 #ifdef	F_GETOWN
2173     setconnmode(0);
2174     res = fcntl(fd, F_GETOWN, 0);
2175     setcommandmode();
2176 
2177     if (res == -1) {
2178 	perror("fcntl");
2179 	return;
2180     }
2181     printf("\tOwner is %d.\n", res);
2182 #endif
2183 
2184     setconnmode(0);
2185     res = fcntl(fd, F_GETFL, 0);
2186     setcommandmode();
2187 
2188     if (res == -1) {
2189 	perror("fcntl");
2190 	return;
2191     }
2192 #ifdef notdef
2193     printf("\tFlags are 0x%x: %s\n", res, decodeflags(res));
2194 #endif
2195 }
2196 #endif /* defined(unix) && defined(TN3270) */
2197 
2198 /*
2199  * Print status about the connection.
2200  */
2201     /*ARGSUSED*/
2202     static int
2203 status(argc, argv)
2204     int	 argc;
2205     char *argv[];
2206 {
2207     if (connected) {
2208 	printf("Connected to %s.\n", hostname);
2209 	if ((argc < 2) || strcmp(argv[1], "notmuch")) {
2210 	    int mode = getconnmode();
2211 
2212 	    if (my_want_state_is_will(TELOPT_LINEMODE)) {
2213 		printf("Operating with LINEMODE option\n");
2214 		printf("%s line editing\n", (mode&MODE_EDIT) ? "Local" : "No");
2215 		printf("%s catching of signals\n",
2216 					(mode&MODE_TRAPSIG) ? "Local" : "No");
2217 		slcstate();
2218 #ifdef	KLUDGELINEMODE
2219 	    } else if (kludgelinemode && my_want_state_is_dont(TELOPT_SGA)) {
2220 		printf("Operating in obsolete linemode\n");
2221 #endif
2222 	    } else {
2223 		printf("Operating in single character mode\n");
2224 		if (localchars)
2225 		    printf("Catching signals locally\n");
2226 	    }
2227 	    printf("%s character echo\n", (mode&MODE_ECHO) ? "Local" : "Remote");
2228 	    if (my_want_state_is_will(TELOPT_LFLOW))
2229 		printf("%s flow control\n", (mode&MODE_FLOW) ? "Local" : "No");
2230 #ifdef	ENCRYPTION
2231 	    encrypt_display();
2232 #endif	/* ENCRYPTION */
2233 	}
2234     } else {
2235 	printf("No connection.\n");
2236     }
2237 #   if !defined(TN3270)
2238     printf("Escape character is '%s'.\n", control(escape));
2239     (void) fflush(stdout);
2240 #   else /* !defined(TN3270) */
2241     if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
2242 	printf("Escape character is '%s'.\n", control(escape));
2243     }
2244 #   if defined(unix)
2245     if ((argc >= 2) && !strcmp(argv[1], "everything")) {
2246 	printf("SIGIO received %d time%s.\n",
2247 				sigiocount, (sigiocount == 1)? "":"s");
2248 	if (In3270) {
2249 	    printf("Process ID %d, process group %d.\n",
2250 					    getpid(), getpgrp());
2251 	    printf("Terminal input:\n");
2252 	    filestuff(tin);
2253 	    printf("Terminal output:\n");
2254 	    filestuff(tout);
2255 	    printf("Network socket:\n");
2256 	    filestuff(net);
2257 	}
2258     }
2259     if (In3270 && transcom) {
2260 	printf("Transparent mode command is '%s'.\n", transcom);
2261     }
2262 #   endif /* defined(unix) */
2263     (void) fflush(stdout);
2264     if (In3270) {
2265 	return 0;
2266     }
2267 #   endif /* defined(TN3270) */
2268     return 1;
2269 }
2270 
2271 #ifdef	SIGINFO
2272 /*
2273  * Function that gets called when SIGINFO is received.
2274  */
2275 int
2276 ayt_status()
2277 {
2278     return call(status, "status", "notmuch", 0);
2279 }
2280 #endif
2281 
2282 static const char *
2283 sockaddr_ntop(sa)
2284     struct sockaddr *sa;
2285 {
2286     static char addrbuf[NI_MAXHOST];
2287 #ifdef NI_WITHSCOPEID
2288     const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
2289 #else
2290     const int niflags = NI_NUMERICHOST;
2291 #endif
2292 
2293     if (getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf),
2294 	    NULL, 0, niflags) == 0)
2295 	return addrbuf;
2296     else
2297 	return NULL;
2298 }
2299 
2300 #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
2301 static int setpolicy (int, struct addrinfo *, char *);
2302 
2303 static int
2304 setpolicy(net, res, policy)
2305 	int net;
2306 	struct addrinfo *res;
2307 	char *policy;
2308 {
2309 	char *buf;
2310 	int level;
2311 	int optname;
2312 
2313 	if (policy == NULL)
2314 		return 0;
2315 
2316 	buf = ipsec_set_policy(policy, strlen(policy));
2317 	if (buf == NULL) {
2318 		printf("%s\n", ipsec_strerror());
2319 		return -1;
2320 	}
2321 	level = res->ai_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6;
2322 	optname = res->ai_family == AF_INET ? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY;
2323 	if (setsockopt(net, level, optname, buf, ipsec_get_policylen(buf)) < 0){
2324 		perror("setsockopt");
2325 		return -1;
2326 	}
2327 
2328 	free(buf);
2329 	return 0;
2330 }
2331 #endif
2332 
2333     int
2334 tn(argc, argv)
2335     int argc;
2336     char *argv[];
2337 {
2338     struct addrinfo hints, *res, *res0;
2339     char *cause = "telnet: unknown";
2340     int error;
2341 #if	defined(IP_OPTIONS) && defined(IPPROTO_IP)
2342     char *srp = 0;
2343     unsigned long srlen;
2344     int proto, opt;
2345 #endif
2346     char *cmd, *hostp = 0, *portp = 0;
2347     const char *user = 0;
2348 #ifdef __GNUC__	/* Avoid vfork clobbering */
2349     (void) &user;
2350 #endif
2351 
2352     if (connected) {
2353 	printf("?Already connected to %s\n", hostname);
2354 	return 0;
2355     }
2356     if (argc < 2) {
2357 	(void) strcpy(line, "open ");
2358 	printf("(to) ");
2359 	(void) fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin);
2360 	makeargv();
2361 	argc = margc;
2362 	argv = margv;
2363     }
2364     cmd = *argv;
2365     --argc; ++argv;
2366     while (argc) {
2367 	if (strcmp(*argv, "help") == 0 || isprefix(*argv, "?"))
2368 	    goto usage;
2369 	if (strcmp(*argv, "-l") == 0) {
2370 	    --argc; ++argv;
2371 	    if (argc == 0)
2372 		goto usage;
2373 	    user = *argv++;
2374 	    --argc;
2375 	    continue;
2376 	}
2377 	if (strcmp(*argv, "-a") == 0) {
2378 	    --argc; ++argv;
2379 	    autologin = 1;
2380 	    continue;
2381 	}
2382 	if (hostp == 0) {
2383 	    hostp = *argv++;
2384 	    --argc;
2385 	    continue;
2386 	}
2387 	if (portp == 0) {
2388 	    portp = *argv++;
2389 	    --argc;
2390 	    continue;
2391 	}
2392     usage:
2393 	printf("usage: %s [-l user] [-a] host-name [port]\n", cmd);
2394 	return 0;
2395     }
2396     if (hostp == 0)
2397 	goto usage;
2398 
2399     (void) strcpy(_hostname, hostp);
2400     if (hostp[0] == '@' || hostp[0] == '!') {
2401 	char *p;
2402 	hostname = NULL;
2403 	for (p = hostp + 1; *p; p++) {
2404 	    if (*p == ',' || *p == '@')
2405 		hostname = p;
2406 	}
2407 	if (hostname == NULL) {
2408 	    fprintf(stderr, "%s: bad source route specification\n", hostp);
2409 	    return 0;
2410 	}
2411 	*hostname++ = '\0';
2412     } else
2413 	hostname = hostp;
2414 
2415     if (!portp) {
2416 	telnetport = 1;
2417 	portp = "telnet";
2418     } else if (portp[0] == '-') {
2419 	/* use telnet negotiation if port number/name preceded by minus sign */
2420 	telnetport = 1;
2421 	portp++;
2422     } else
2423 	telnetport = 0;
2424 
2425     memset(&hints, 0, sizeof(hints));
2426     hints.ai_family = family;
2427     hints.ai_socktype = SOCK_STREAM;
2428     hints.ai_protocol = 0;
2429     hints.ai_flags = AI_NUMERICHOST;	/* avoid forward lookup */
2430     error = getaddrinfo(hostname, portp, &hints, &res0);
2431     if (!error) {
2432 	/* numeric */
2433 	if (doaddrlookup &&
2434 	    getnameinfo(res0->ai_addr, res0->ai_addrlen,
2435 		_hostname, sizeof(_hostname), NULL, 0, NI_NAMEREQD) == 0)
2436 	    ; /* okay */
2437 	else {
2438 	    strncpy(_hostname, hostname, sizeof(_hostname) - 1);
2439 	    _hostname[sizeof(_hostname) - 1] = '\0';
2440 	}
2441     } else {
2442 	/* FQDN - try again with forward DNS lookup */
2443 	memset(&hints, 0, sizeof(hints));
2444 	hints.ai_family = family;
2445 	hints.ai_socktype = SOCK_STREAM;
2446 	hints.ai_protocol = 0;
2447 	hints.ai_flags = AI_CANONNAME;
2448 	error = getaddrinfo(hostname, portp, &hints, &res0);
2449 	if (error == EAI_SERVICE) {
2450 	    fprintf(stderr, "tcp/%s: unknown service\n", portp);
2451 	    return 0;
2452 	} else if (error) {
2453 	    fprintf(stderr, "%s: %s\n", hostname, gai_strerror(error));
2454 	    return 0;
2455 	}
2456 	if (res0->ai_canonname) {
2457 	    (void) strncpy(_hostname, res0->ai_canonname,
2458 	        sizeof(_hostname) - 1);
2459 	} else
2460 	    (void) strncpy(_hostname, hostname, sizeof(_hostname) - 1);
2461 	_hostname[sizeof(_hostname) - 1] = '\0';
2462     }
2463     hostname = _hostname;
2464 
2465     net = -1;
2466     for (res = res0; res; res = res->ai_next) {
2467 	printf("Trying %s...\n", sockaddr_ntop(res->ai_addr));
2468 	net = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
2469 	if (net < 0) {
2470 	    cause = "telnet: socket";
2471 	    continue;
2472 	}
2473 
2474 	if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
2475 	    perror("setsockopt (SO_DEBUG)");
2476 	}
2477 	if (hostp[0] == '@' || hostp[0] == '!') {
2478 	    if ((srlen = sourceroute(res, hostp, &srp, &proto, &opt)) < 0) {
2479 		(void) NetClose(net);
2480 		net = -1;
2481 		continue;
2482 	    }
2483 	    if (srp && setsockopt(net, proto, opt, srp, srlen) < 0)
2484 		perror("setsockopt (source route)");
2485 	}
2486 
2487 #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
2488 	if (setpolicy(net, res, ipsec_policy_in) < 0) {
2489 	    (void) NetClose(net);
2490 	    net = -1;
2491 	    continue;
2492 	}
2493 	if (setpolicy(net, res, ipsec_policy_out) < 0) {
2494 	    (void) NetClose(net);
2495 	    net = -1;
2496 	    continue;
2497 	}
2498 #endif
2499 
2500 	if (connect(net, res->ai_addr, res->ai_addrlen) < 0) {
2501 	    if (res->ai_next) {
2502 		int oerrno = errno;
2503 
2504 		fprintf(stderr, "telnet: connect to address %s: ",
2505 						sockaddr_ntop(res->ai_addr));
2506 		errno = oerrno;
2507 		perror((char *)0);
2508 	    }
2509 	    cause = "telnet: Unable to connect to remote host";
2510 	    (void) NetClose(net);
2511 	    net = -1;
2512 	    continue;
2513 	}
2514 
2515 	connected++;
2516 #if	defined(AUTHENTICATION) || defined(ENCRYPTION)
2517 	auth_encrypt_connect(connected);
2518 #endif	/* defined(AUTHENTICATION) || defined(ENCRYPTION) */
2519 	break;
2520     }
2521     freeaddrinfo(res0);
2522     if (net < 0 || connected == 0) {
2523 	perror(cause);
2524 	return 0;
2525     }
2526 
2527     cmdrc(hostp, hostname);
2528     if (autologin && user == NULL) {
2529 	struct passwd *pw;
2530 
2531 	user = getenv("USER");
2532 	if (user == NULL ||
2533 	    ((pw = getpwnam(user)) && pw->pw_uid != getuid())) {
2534 		if ((pw = getpwuid(getuid())) != NULL)
2535 			user = pw->pw_name;
2536 		else
2537 			user = NULL;
2538 	}
2539     }
2540     if (user) {
2541 	env_define((unsigned char *)"USER", (unsigned char *)user);
2542 	env_export((unsigned char *)"USER", NULL);
2543     }
2544     (void) call(status, "status", "notmuch", 0);
2545     if (setjmp(peerdied) == 0)
2546 	telnet(user);
2547     (void) NetClose(net);
2548     ExitString("Connection closed by foreign host.\n",1);
2549     /*NOTREACHED*/
2550 }
2551 
2552 #define HELPINDENT ((int)sizeof ("connect"))
2553 
2554 static char
2555 	openhelp[] =	"connect to a site",
2556 	closehelp[] =	"close current connection",
2557 	logouthelp[] =	"forcibly logout remote user and close the connection",
2558 	quithelp[] =	"exit telnet",
2559 	statushelp[] =	"print status information",
2560 	helphelp[] =	"print help information",
2561 	sendhelp[] =	"transmit special characters ('send ?' for more)",
2562 	sethelp[] = 	"set operating parameters ('set ?' for more)",
2563 	unsethelp[] = 	"unset operating parameters ('unset ?' for more)",
2564 	togglestring[] ="toggle operating parameters ('toggle ?' for more)",
2565 	slchelp[] =	"change state of special charaters ('slc ?' for more)",
2566 	displayhelp[] =	"display operating parameters",
2567 #if	defined(TN3270) && defined(unix)
2568 	transcomhelp[] = "specify Unix command for transparent mode pipe",
2569 #endif	/* defined(TN3270) && defined(unix) */
2570 #if	defined(AUTHENTICATION)
2571 	authhelp[] =	"turn on (off) authentication ('auth ?' for more)",
2572 #endif
2573 #ifdef	ENCRYPTION
2574 	encrypthelp[] = "turn on (off) encryption ('encrypt ?' for more)",
2575 #endif	/* ENCRYPTION */
2576 #if	defined(unix)
2577 	zhelp[] =	"suspend telnet",
2578 #endif	/* defined(unix) */
2579 	shellhelp[] =	"invoke a subshell",
2580 	envhelp[] =	"change environment variables ('environ ?' for more)",
2581 	modestring[] = "try to enter line or character mode ('mode ?' for more)";
2582 
2583 static Command cmdtab[] = {
2584 	{ "close",	closehelp,	bye,		1 },
2585 	{ "logout",	logouthelp,	logout,		1 },
2586 	{ "display",	displayhelp,	display,	0 },
2587 	{ "mode",	modestring,	modecmd,	0 },
2588 	{ "open",	openhelp,	tn,		0 },
2589 	{ "quit",	quithelp,	quit,		0 },
2590 	{ "send",	sendhelp,	sendcmd,	0 },
2591 	{ "set",	sethelp,	setcmd,		0 },
2592 	{ "unset",	unsethelp,	unsetcmd,	0 },
2593 	{ "status",	statushelp,	status,		0 },
2594 	{ "toggle",	togglestring,	toggle,		0 },
2595 	{ "slc",	slchelp,	slccmd,		0 },
2596 #if	defined(TN3270) && defined(unix)
2597 	{ "transcom",	transcomhelp,	settranscom,	0 },
2598 #endif	/* defined(TN3270) && defined(unix) */
2599 #if	defined(AUTHENTICATION)
2600 	{ "auth",	authhelp,	auth_cmd,	0 },
2601 #endif
2602 #ifdef	ENCRYPTION
2603 	{ "encrypt",	encrypthelp,	encrypt_cmd,	0 },
2604 #endif
2605 #if	defined(unix)
2606 	{ "z",		zhelp,		suspend,	0 },
2607 #endif	/* defined(unix) */
2608 #if	defined(TN3270)
2609 	{ "!",		shellhelp,	shell,		1 },
2610 #else
2611 	{ "!",		shellhelp,	shell,		0 },
2612 #endif
2613 	{ "environ",	envhelp,	env_cmd,	0 },
2614 	{ "?",		helphelp,	help,		0 },
2615 	{ NULL,		NULL,		NULL,		0 }
2616 };
2617 
2618 static char	crmodhelp[] =	"deprecated command -- use 'toggle crmod' instead";
2619 static char	escapehelp[] =	"deprecated command -- use 'set escape' instead";
2620 
2621 static Command cmdtab2[] = {
2622 	{ "help",	0,		help,		0 },
2623 	{ "escape",	escapehelp,	setescape,	0 },
2624 	{ "crmod",	crmodhelp,	togcrmod,	0 },
2625 	{ NULL,		NULL,		NULL,		0 }
2626 };
2627 
2628 
2629 /*
2630  * Call routine with argc, argv set from args (terminated by 0).
2631  */
2632 
2633     /*VARARGS1*/
2634     static int
2635 call(intrtn_t routine, ...)
2636 {
2637     va_list ap;
2638     char *args[100];
2639     int argno = 0;
2640 
2641     va_start(ap, routine);
2642     while ((args[argno++] = va_arg(ap, char *)) != 0) {
2643 	;
2644     }
2645     va_end(ap);
2646     return (*routine)(argno-1, args);
2647 }
2648 
2649 
2650     static Command *
2651 getcmd(name)
2652     char *name;
2653 {
2654     Command *cm;
2655 
2656     if ((cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command))) != NULL)
2657 	return cm;
2658     return (Command *) genget(name, (char **) cmdtab2, sizeof(Command));
2659 }
2660 
2661     void
2662 command(top, tbuf, cnt)
2663     int top;
2664     char *tbuf;
2665     int cnt;
2666 {
2667     Command *c;
2668 
2669     setcommandmode();
2670     if (!top) {
2671 	putchar('\n');
2672 #if	defined(unix)
2673     } else {
2674 	(void) signal(SIGINT, SIG_DFL);
2675 	(void) signal(SIGQUIT, SIG_DFL);
2676 #endif	/* defined(unix) */
2677     }
2678     for (;;) {
2679 	if (rlogin == _POSIX_VDISABLE)
2680 		printf("%s> ", prompt);
2681 	if (tbuf) {
2682 	    char *cp;
2683 	    cp = line;
2684 	    while (cnt > 0 && (*cp++ = *tbuf++) != '\n')
2685 		cnt--;
2686 	    tbuf = 0;
2687 	    if (cp == line || *--cp != '\n' || cp == line)
2688 		goto getline;
2689 	    *cp = '\0';
2690 	    if (rlogin == _POSIX_VDISABLE)
2691 		printf("%s\n", line);
2692 	} else {
2693 	getline:
2694 	    if (rlogin != _POSIX_VDISABLE)
2695 		printf("%s> ", prompt);
2696 #if defined(TN3270)
2697 	    fflush(stdout);
2698 #endif
2699 	    if (fgets(line, sizeof(line), stdin) == NULL) {
2700 		if (feof(stdin) || ferror(stdin)) {
2701 		    (void) quit(0, NULL);
2702 		    /*NOTREACHED*/
2703 		}
2704 		break;
2705 	    }
2706 	}
2707 	if (line[0] == 0)
2708 	    break;
2709 	makeargv();
2710 	if (margv[0] == 0) {
2711 	    break;
2712 	}
2713 	c = getcmd(margv[0]);
2714 	if (Ambiguous(c)) {
2715 	    printf("?Ambiguous command\n");
2716 	    continue;
2717 	}
2718 	if (c == 0) {
2719 	    printf("?Invalid command\n");
2720 	    continue;
2721 	}
2722 	if (c->needconnect && !connected) {
2723 	    printf("?Need to be connected first.\n");
2724 	    continue;
2725 	}
2726 	if ((*c->handler)(margc, margv)) {
2727 	    break;
2728 	}
2729     }
2730     if (!top) {
2731 	if (!connected) {
2732 	    longjmp(toplevel, 1);
2733 	    /*NOTREACHED*/
2734 	}
2735 #if	defined(TN3270)
2736 	if (shell_active == 0) {
2737 	    setconnmode(0);
2738 	}
2739 #else	/* defined(TN3270) */
2740 	setconnmode(0);
2741 #endif	/* defined(TN3270) */
2742     }
2743 }
2744 
2745 /*
2746  * Help command.
2747  */
2748 	static int
2749 help(argc, argv)
2750 	int argc;
2751 	char *argv[];
2752 {
2753 	Command *c;
2754 
2755 	if (argc == 1) {
2756 		printf("Commands may be abbreviated.  Commands are:\n\n");
2757 		for (c = cmdtab; c->name; c++)
2758 			if (c->help) {
2759 				printf("%-*s\t%s\n", HELPINDENT, c->name,
2760 								    c->help);
2761 			}
2762 		return 0;
2763 	}
2764 	while (--argc > 0) {
2765 		char *arg;
2766 		arg = *++argv;
2767 		c = getcmd(arg);
2768 		if (Ambiguous(c))
2769 			printf("?Ambiguous help command %s\n", arg);
2770 		else if (c == (Command *)0)
2771 			printf("?Invalid help command %s\n", arg);
2772 		else
2773 			printf("%s\n", c->help);
2774 	}
2775 	return 0;
2776 }
2777 
2778 static char *rcname = 0;
2779 static char rcbuf[128];
2780 
2781 void
2782 cmdrc(m1, m2)
2783 	const char *m1, *m2;
2784 {
2785     Command *c;
2786     FILE *rcfile;
2787     int gotmachine = 0;
2788     int l1 = strlen(m1);
2789     int l2 = strlen(m2);
2790     char m1save[MAXHOSTNAMELEN + 1];
2791 
2792     if (skiprc)
2793 	return;
2794 
2795     strlcpy(m1save, m1, sizeof(m1save));
2796     m1 = m1save;
2797 
2798     if (rcname == 0) {
2799 	rcname = getenv("HOME");
2800 	if (rcname)
2801 	    strcpy(rcbuf, rcname);
2802 	else
2803 	    rcbuf[0] = '\0';
2804 	strcat(rcbuf, "/.telnetrc");
2805 	rcname = rcbuf;
2806     }
2807 
2808     if ((rcfile = fopen(rcname, "r")) == 0) {
2809 	return;
2810     }
2811 
2812     for (;;) {
2813 	if (fgets(line, sizeof(line), rcfile) == NULL)
2814 	    break;
2815 	if (line[0] == 0)
2816 	    break;
2817 	if (line[0] == '#')
2818 	    continue;
2819 	if (gotmachine) {
2820 	    if (!isspace((unsigned char)line[0]))
2821 		gotmachine = 0;
2822 	}
2823 	if (gotmachine == 0) {
2824 	    if (isspace((unsigned char)line[0]))
2825 		continue;
2826 	    if (strncasecmp(line, m1, l1) == 0)
2827 		strncpy(line, &line[l1], sizeof(line) - l1);
2828 	    else if (strncasecmp(line, m2, l2) == 0)
2829 		strncpy(line, &line[l2], sizeof(line) - l2);
2830 	    else if (strncasecmp(line, "DEFAULT", 7) == 0)
2831 		strncpy(line, &line[7], sizeof(line) - 7);
2832 	    else
2833 		continue;
2834 	    if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n')
2835 		continue;
2836 	    gotmachine = 1;
2837 	}
2838 	makeargv();
2839 	if (margv[0] == 0)
2840 	    continue;
2841 	c = getcmd(margv[0]);
2842 	if (Ambiguous(c)) {
2843 	    printf("?Ambiguous command: %s\n", margv[0]);
2844 	    continue;
2845 	}
2846 	if (c == 0) {
2847 	    printf("?Invalid command: %s\n", margv[0]);
2848 	    continue;
2849 	}
2850 	/*
2851 	 * This should never happen...
2852 	 */
2853 	if (c->needconnect && !connected) {
2854 	    printf("?Need to be connected first for %s.\n", margv[0]);
2855 	    continue;
2856 	}
2857 	(*c->handler)(margc, margv);
2858     }
2859     fclose(rcfile);
2860 }
2861 
2862 /*
2863  * Source route is handed in as
2864  *	[!]@hop1@hop2...@dst
2865  *
2866  * If the leading ! is present, it is a strict source route, otherwise it is
2867  * assmed to be a loose source route.  Note that leading ! is effective
2868  * only for IPv4 case.
2869  *
2870  * We fill in the source route option as
2871  *	hop1,hop2,hop3...dest
2872  * and return a pointer to hop1, which will
2873  * be the address to connect() to.
2874  *
2875  * Arguments:
2876  *	ai:	The address (by struct addrinfo) for the final destination.
2877  *
2878  *	arg:	Pointer to route list to decipher
2879  *
2880  *	cpp: 	Pointer to a pointer, so that sourceroute() can return
2881  *		the address of result buffer (statically alloc'ed).
2882  *
2883  *	protop/optp:
2884  *		Pointer to an integer.  The pointed variable
2885  *	lenp:	pointer to an integer that contains the
2886  *		length of *cpp if *cpp != NULL.
2887  *
2888  * Return values:
2889  *
2890  *	Returns the length of the option pointed to by *cpp.  If the
2891  *	return value is -1, there was a syntax error in the
2892  *	option, either arg contained unknown characters or too many hosts,
2893  *	or hostname cannot be resolved.
2894  *
2895  *	The caller needs to pass return value (len), *cpp, *protop and *optp
2896  *	to setsockopt(2).
2897  *
2898  *	*cpp:	Points to the result buffer.  The region is statically
2899  *		allocated by the function.
2900  *
2901  *	*protop:
2902  *		protocol # to be passed to setsockopt(2).
2903  *
2904  *	*optp:	option # to be passed to setsockopt(2).
2905  *
2906  */
2907 int
2908 sourceroute(ai, arg, cpp, protop, optp)
2909 	struct addrinfo *ai;
2910 	char *arg;
2911 	char **cpp;
2912 	int *protop;
2913 	int *optp;
2914 {
2915 #ifdef	sysV88
2916 	static IOPTN ipopt;
2917 #endif
2918 	char *cp, *cp2, *lsrp, *lsrep;
2919 	struct addrinfo hints, *res;
2920 	int len, error;
2921 	struct sockaddr_in *sin;
2922 	char c;
2923 	static char lsr[44];
2924 #ifdef INET6
2925 	struct cmsghdr *cmsg;
2926 	struct sockaddr_in6 *sin6;
2927 	static char rhbuf[1024];
2928 #endif
2929 
2930 	/*
2931 	 * Verify the arguments.
2932 	 */
2933 	if (cpp == NULL)
2934 		return -1;
2935 
2936 	cp = arg;
2937 
2938 	*cpp = NULL;
2939 
2940 	  /* init these just in case.... */
2941 	lsrp = NULL;
2942 	lsrep = NULL;
2943 #ifdef INET6
2944 	cmsg = NULL;
2945 #endif
2946 
2947 	switch (ai->ai_family) {
2948 	case AF_INET:
2949 		lsrp = lsr;
2950 		lsrep = lsrp + sizeof(lsr);
2951 
2952 		/*
2953 		 * Next, decide whether we have a loose source
2954 		 * route or a strict source route, and fill in
2955 		 * the begining of the option.
2956 		 */
2957 #ifndef	sysV88
2958 		if (*cp == '!') {
2959 			cp++;
2960 			*lsrp++ = IPOPT_SSRR;
2961 		} else
2962 			*lsrp++ = IPOPT_LSRR;
2963 #else
2964 		if (*cp == '!') {
2965 			cp++;
2966 			ipopt.io_type = IPOPT_SSRR;
2967 		} else
2968 			ipopt.io_type = IPOPT_LSRR;
2969 #endif
2970 		if (*cp != '@')
2971 			return -1;
2972 #ifndef	sysV88
2973 		lsrp++;		/* skip over length, we'll fill it in later */
2974 		*lsrp++ = 4;
2975 #endif
2976 		cp++;
2977 		*protop = IPPROTO_IP;
2978 		*optp = IP_OPTIONS;
2979 		break;
2980 #ifdef INET6
2981 	case AF_INET6:
2982 		cmsg = inet6_rthdr_init(rhbuf, IPV6_RTHDR_TYPE_0);
2983 		if (*cp != '@')
2984 			return -1;
2985 		cp++;
2986 		*protop = IPPROTO_IPV6;
2987 		*optp = IPV6_PKTOPTIONS;
2988 		break;
2989 #endif
2990 	default:
2991 		return -1;
2992 	}
2993 
2994 	memset(&hints, 0, sizeof(hints));
2995 	hints.ai_family = ai->ai_family;
2996 	hints.ai_socktype = SOCK_STREAM;
2997 
2998 	for (c = 0;;) {
2999 		if (c == ':')
3000 			cp2 = 0;
3001 		else for (cp2 = cp; (c = *cp2) != '\0'; cp2++) {
3002 			if (c == ',') {
3003 				*cp2++ = '\0';
3004 				if (*cp2 == '@')
3005 					cp2++;
3006 			} else if (c == '@') {
3007 				*cp2++ = '\0';
3008 			}
3009 #if 0	/*colon conflicts with IPv6 address*/
3010 			else if (c == ':') {
3011 				*cp2++ = '\0';
3012 			}
3013 #endif
3014 			else
3015 				continue;
3016 			break;
3017 		}
3018 		if (!c)
3019 			cp2 = 0;
3020 
3021 		error = getaddrinfo(cp, NULL, &hints, &res);
3022 		if (error) {
3023 			fprintf(stderr, "%s: %s\n", cp, gai_strerror(error));
3024 			return -1;
3025 		}
3026 		if (ai->ai_family != res->ai_family) {
3027 			freeaddrinfo(res);
3028 			return -1;
3029 		}
3030 		if (ai->ai_family == AF_INET) {
3031 			/*
3032 			 * Check to make sure there is space for address
3033 			 */
3034 			if (lsrp + 4 > lsrep) {
3035 				freeaddrinfo(res);
3036 				return -1;
3037 			}
3038 			sin = (struct sockaddr_in *)res->ai_addr;
3039 			memcpy(lsrp, &sin->sin_addr, sizeof(struct in_addr));
3040 			lsrp += sizeof(struct in_addr);
3041 		}
3042 #ifdef INET6
3043 		else if (ai->ai_family == AF_INET6) {
3044 			sin6 = (struct sockaddr_in6 *)res->ai_addr;
3045 			inet6_rthdr_add(cmsg, &sin6->sin6_addr,
3046 				IPV6_RTHDR_LOOSE);
3047 		}
3048 #endif
3049 		else {
3050 			freeaddrinfo(res);
3051 			return -1;
3052 		}
3053 		freeaddrinfo(res);
3054 		if (cp2)
3055 			cp = cp2;
3056 		else
3057 			break;
3058 	}
3059 	if (ai->ai_family == AF_INET) {
3060 		/* record the last hop */
3061 		if (lsrp + 4 > lsrep)
3062 			return -1;
3063 		sin = (struct sockaddr_in *)ai->ai_addr;
3064 		memcpy(lsrp, &sin->sin_addr, sizeof(struct in_addr));
3065 		lsrp += sizeof(struct in_addr);
3066 #ifndef	sysV88
3067 		lsr[IPOPT_OLEN] = lsrp - lsr;
3068 		if (lsr[IPOPT_OLEN] <= 7 || lsr[IPOPT_OLEN] > 40)
3069 			return -1;
3070 		*lsrp++ = IPOPT_NOP;	/*32bit word align*/
3071 		len = lsrp - lsr;
3072 		*cpp = lsr;
3073 #else
3074 		ipopt.io_len = lsrp - lsr;
3075 		if (ipopt.io_len <= 5)	/*is 3 better?*/
3076 			return -1;
3077 		*cpp = (char 8)&ipopt;
3078 #endif
3079 	}
3080 #ifdef INET6
3081 	else if (ai->ai_family == AF_INET6) {
3082 		inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE);
3083 		len = cmsg->cmsg_len;
3084 		*cpp = rhbuf;
3085 	}
3086 #endif
3087 	else
3088 		return -1;
3089 	return len;
3090 }
3091