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