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