xref: /original-bsd/usr.bin/telnet/commands.c (revision f703d710)
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 #include <netinet/in.h>
4 
5 #include <signal.h>
6 #include <netdb.h>
7 #include <ctype.h>
8 
9 #include <arpa/telnet.h>
10 
11 #include "ring.h"
12 
13 #include "externs.h"
14 #include "defines.h"
15 #include "types.h"
16 
17 char	*hostname;
18 
19 #define Ambiguous(s)	((char *)s == ambiguous)
20 static char *ambiguous;		/* special return value for command routines */
21 
22 typedef struct {
23 	char	*name;		/* command name */
24 	char	*help;		/* help string */
25 	int	(*handler)();	/* routine which executes command */
26 	int	dohelp;		/* Should we give general help information? */
27 	int	needconnect;	/* Do we need to be connected to execute? */
28 } Command;
29 
30 static char line[200];
31 static int margc;
32 static char *margv[20];
33 
34 /*
35  * Various utility routines.
36  */
37 
38 static void
39 makeargv()
40 {
41     register char *cp;
42     register char **argp = margv;
43 
44     margc = 0;
45     cp = line;
46     if (*cp == '!') {		/* Special case shell escape */
47 	*argp++ = "!";		/* No room in string to get this */
48 	margc++;
49 	cp++;
50     }
51     while (*cp) {
52 	while (isspace(*cp))
53 	    cp++;
54 	if (*cp == '\0')
55 	    break;
56 	*argp++ = cp;
57 	margc += 1;
58 	while (*cp != '\0' && !isspace(*cp))
59 	    cp++;
60 	if (*cp == '\0')
61 	    break;
62 	*cp++ = '\0';
63     }
64     *argp++ = 0;
65 }
66 
67 
68 static char **
69 genget(name, table, next)
70 char	*name;		/* name to match */
71 char	**table;		/* name entry in table */
72 char	**(*next)();	/* routine to return next entry in table */
73 {
74 	register char *p, *q;
75 	register char **c, **found;
76 	register int nmatches, longest;
77 
78 	if (name == 0) {
79 	    return 0;
80 	}
81 	longest = 0;
82 	nmatches = 0;
83 	found = 0;
84 	for (c = table; (p = *c) != 0; c = (*next)(c)) {
85 		for (q = name;
86 		    (*q == *p) || (isupper(*q) && tolower(*q) == *p); p++, q++)
87 			if (*q == 0)		/* exact match? */
88 				return (c);
89 		if (!*q) {			/* the name was a prefix */
90 			if (q - name > longest) {
91 				longest = q - name;
92 				nmatches = 1;
93 				found = c;
94 			} else if (q - name == longest)
95 				nmatches++;
96 		}
97 	}
98 	if (nmatches > 1)
99 		return (char **)ambiguous;
100 	return (found);
101 }
102 
103 /*
104  * Make a character string into a number.
105  *
106  * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
107  */
108 
109 static
110 special(s)
111 register char *s;
112 {
113 	register char c;
114 	char b;
115 
116 	switch (*s) {
117 	case '^':
118 		b = *++s;
119 		if (b == '?') {
120 		    c = b | 0x40;		/* DEL */
121 		} else {
122 		    c = b & 0x1f;
123 		}
124 		break;
125 	default:
126 		c = *s;
127 		break;
128 	}
129 	return c;
130 }
131 
132 /*
133  * Construct a control character sequence
134  * for a special character.
135  */
136 static char *
137 control(c)
138 	register int c;
139 {
140 	static char buf[3];
141 
142 	if (c == 0x7f)
143 		return ("^?");
144 	if (c == '\377') {
145 		return "off";
146 	}
147 	if (c >= 0x20) {
148 		buf[0] = c;
149 		buf[1] = 0;
150 	} else {
151 		buf[0] = '^';
152 		buf[1] = '@'+c;
153 		buf[2] = 0;
154 	}
155 	return (buf);
156 }
157 
158 
159 
160 /*
161  *	The following are data structures and routines for
162  *	the "send" command.
163  *
164  */
165 
166 struct sendlist {
167     char	*name;		/* How user refers to it (case independent) */
168     int		what;		/* Character to be sent (<0 ==> special) */
169     char	*help;		/* Help information (0 ==> no help) */
170 #if	defined(NOT43)
171     int		(*routine)();	/* Routine to perform (for special ops) */
172 #else	/* defined(NOT43) */
173     void	(*routine)();	/* Routine to perform (for special ops) */
174 #endif	/* defined(NOT43) */
175 };
176 
177 #define	SENDQUESTION	-1
178 #define	SENDESCAPE	-3
179 
180 static struct sendlist Sendlist[] = {
181     { "ao", AO, "Send Telnet Abort output" },
182     { "ayt", AYT, "Send Telnet 'Are You There'" },
183     { "brk", BREAK, "Send Telnet Break" },
184     { "ec", EC, "Send Telnet Erase Character" },
185     { "el", EL, "Send Telnet Erase Line" },
186     { "escape", SENDESCAPE, "Send current escape character" },
187     { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
188     { "ip", IP, "Send Telnet Interrupt Process" },
189     { "nop", NOP, "Send Telnet 'No operation'" },
190     { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
191     { "?", SENDQUESTION, "Display send options" },
192     { 0 }
193 };
194 
195 static struct sendlist Sendlist2[] = {		/* some synonyms */
196 	{ "break", BREAK, 0 },
197 
198 	{ "intp", IP, 0 },
199 	{ "interrupt", IP, 0 },
200 	{ "intr", IP, 0 },
201 
202 	{ "help", SENDQUESTION, 0 },
203 
204 	{ 0 }
205 };
206 
207 static char **
208 getnextsend(name)
209 char *name;
210 {
211     struct sendlist *c = (struct sendlist *) name;
212 
213     return (char **) (c+1);
214 }
215 
216 static struct sendlist *
217 getsend(name)
218 char *name;
219 {
220     struct sendlist *sl;
221 
222     if ((sl = (struct sendlist *)
223 			genget(name, (char **) Sendlist, getnextsend)) != 0) {
224 	return sl;
225     } else {
226 	return (struct sendlist *)
227 				genget(name, (char **) Sendlist2, getnextsend);
228     }
229 }
230 
231 static
232 sendcmd(argc, argv)
233 int	argc;
234 char	**argv;
235 {
236     int what;		/* what we are sending this time */
237     int count;		/* how many bytes we are going to need to send */
238     int i;
239     int question = 0;	/* was at least one argument a question */
240     struct sendlist *s;	/* pointer to current command */
241 
242     if (argc < 2) {
243 	printf("need at least one argument for 'send' command\n");
244 	printf("'send ?' for help\n");
245 	return 0;
246     }
247     /*
248      * First, validate all the send arguments.
249      * In addition, we see how much space we are going to need, and
250      * whether or not we will be doing a "SYNCH" operation (which
251      * flushes the network queue).
252      */
253     count = 0;
254     for (i = 1; i < argc; i++) {
255 	s = getsend(argv[i]);
256 	if (s == 0) {
257 	    printf("Unknown send argument '%s'\n'send ?' for help.\n",
258 			argv[i]);
259 	    return 0;
260 	} else if (Ambiguous(s)) {
261 	    printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
262 			argv[i]);
263 	    return 0;
264 	}
265 	switch (s->what) {
266 	case SENDQUESTION:
267 	    break;
268 	case SENDESCAPE:
269 	    count += 1;
270 	    break;
271 	case SYNCH:
272 	    count += 2;
273 	    break;
274 	default:
275 	    count += 2;
276 	    break;
277 	}
278     }
279     /* Now, do we have enough room? */
280     if (NETROOM() < count) {
281 	printf("There is not enough room in the buffer TO the network\n");
282 	printf("to process your request.  Nothing will be done.\n");
283 	printf("('send synch' will throw away most data in the network\n");
284 	printf("buffer, if this might help.)\n");
285 	return 0;
286     }
287     /* OK, they are all OK, now go through again and actually send */
288     for (i = 1; i < argc; i++) {
289 	if ((s = getsend(argv[i])) == 0) {
290 	    fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
291 	    quit();
292 	    /*NOTREACHED*/
293 	}
294 	if (s->routine) {
295 	    (*s->routine)(s);
296 	} else {
297 	    switch (what = s->what) {
298 	    case SYNCH:
299 		dosynch();
300 		break;
301 	    case SENDQUESTION:
302 		for (s = Sendlist; s->name; s++) {
303 		    if (s->help) {
304 			printf(s->name);
305 			if (s->help) {
306 			    printf("\t%s", s->help);
307 			}
308 			printf("\n");
309 		    }
310 		}
311 		question = 1;
312 		break;
313 	    case SENDESCAPE:
314 		NETADD(escape);
315 		break;
316 	    default:
317 		NET2ADD(IAC, what);
318 		break;
319 	    }
320 	}
321     }
322     return !question;
323 }
324 
325 /*
326  * The following are the routines and data structures referred
327  * to by the arguments to the "toggle" command.
328  */
329 
330 static
331 lclchars()
332 {
333     donelclchars = 1;
334     return 1;
335 }
336 
337 static
338 togdebug()
339 {
340 #ifndef	NOT43
341     if (net > 0 &&
342 	(SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
343 	    perror("setsockopt (SO_DEBUG)");
344     }
345 #else	/* NOT43 */
346     if (debug) {
347 	if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
348 	    perror("setsockopt (SO_DEBUG)");
349     } else
350 	printf("Cannot turn off socket debugging\n");
351 #endif	/* NOT43 */
352     return 1;
353 }
354 
355 
356 static int
357 togcrlf()
358 {
359     if (crlf) {
360 	printf("Will send carriage returns as telnet <CR><LF>.\n");
361     } else {
362 	printf("Will send carriage returns as telnet <CR><NUL>.\n");
363     }
364     return 1;
365 }
366 
367 
368 static int
369 togbinary()
370 {
371     donebinarytoggle = 1;
372 
373     if (myopts[TELOPT_BINARY] == 0) {	/* Go into binary mode */
374 	NET2ADD(IAC, DO);
375 	NETADD(TELOPT_BINARY);
376 	printoption("<SENT", doopt, TELOPT_BINARY, 0);
377 	NET2ADD(IAC, WILL);
378 	NETADD(TELOPT_BINARY);
379 	printoption("<SENT", doopt, TELOPT_BINARY, 0);
380 	hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 1;
381 	printf("Negotiating binary mode with remote host.\n");
382     } else {				/* Turn off binary mode */
383 	NET2ADD(IAC, DONT);
384 	NETADD(TELOPT_BINARY);
385 	printoption("<SENT", dont, TELOPT_BINARY, 0);
386 	NET2ADD(IAC, DONT);
387 	NETADD(TELOPT_BINARY);
388 	printoption("<SENT", dont, TELOPT_BINARY, 0);
389 	hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 0;
390 	printf("Negotiating network ascii mode with remote host.\n");
391     }
392     return 1;
393 }
394 
395 
396 
397 extern int togglehelp();
398 
399 struct togglelist {
400     char	*name;		/* name of toggle */
401     char	*help;		/* help message */
402     int		(*handler)();	/* routine to do actual setting */
403     int		dohelp;		/* should we display help information */
404     int		*variable;
405     char	*actionexplanation;
406 };
407 
408 static struct togglelist Togglelist[] = {
409     { "autoflush",
410 	"toggle flushing of output when sending interrupt characters",
411 	    0,
412 		1,
413 		    &autoflush,
414 			"flush output when sending interrupt characters" },
415     { "autosynch",
416 	"toggle automatic sending of interrupt characters in urgent mode",
417 	    0,
418 		1,
419 		    &autosynch,
420 			"send interrupt characters in urgent mode" },
421     { "binary",
422 	"toggle sending and receiving of binary data",
423 	    togbinary,
424 		1,
425 		    0,
426 			0 },
427     { "crlf",
428 	"toggle sending carriage returns as telnet <CR><LF>",
429 	    togcrlf,
430 		1,
431 		    &crlf,
432 			0 },
433     { "crmod",
434 	"toggle mapping of received carriage returns",
435 	    0,
436 		1,
437 		    &crmod,
438 			"map carriage return on output" },
439     { "localchars",
440 	"toggle local recognition of certain control characters",
441 	    lclchars,
442 		1,
443 		    &localchars,
444 			"recognize certain control characters" },
445     { " ", "", 0, 1 },		/* empty line */
446     { "debug",
447 	"(debugging) toggle debugging",
448 	    togdebug,
449 		1,
450 		    &debug,
451 			"turn on socket level debugging" },
452     { "netdata",
453 	"(debugging) toggle printing of hexadecimal network data",
454 	    0,
455 		1,
456 		    &netdata,
457 			"print hexadecimal representation of network traffic" },
458     { "options",
459 	"(debugging) toggle viewing of options processing",
460 	    0,
461 		1,
462 		    &showoptions,
463 			"show option processing" },
464     { " ", "", 0, 1 },		/* empty line */
465     { "?",
466 	"display help information",
467 	    togglehelp,
468 		1 },
469     { "help",
470 	"display help information",
471 	    togglehelp,
472 		0 },
473     { 0 }
474 };
475 
476 static
477 togglehelp()
478 {
479     struct togglelist *c;
480 
481     for (c = Togglelist; c->name; c++) {
482 	if (c->dohelp) {
483 	    printf("%s\t%s\n", c->name, c->help);
484 	}
485     }
486     return 0;
487 }
488 
489 static char **
490 getnexttoggle(name)
491 char *name;
492 {
493     struct togglelist *c = (struct togglelist *) name;
494 
495     return (char **) (c+1);
496 }
497 
498 static struct togglelist *
499 gettoggle(name)
500 char *name;
501 {
502     return (struct togglelist *)
503 			genget(name, (char **) Togglelist, getnexttoggle);
504 }
505 
506 static
507 toggle(argc, argv)
508 int	argc;
509 char	*argv[];
510 {
511     int retval = 1;
512     char *name;
513     struct togglelist *c;
514 
515     if (argc < 2) {
516 	fprintf(stderr,
517 	    "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
518 	return 0;
519     }
520     argc--;
521     argv++;
522     while (argc--) {
523 	name = *argv++;
524 	c = gettoggle(name);
525 	if (Ambiguous(c)) {
526 	    fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
527 					name);
528 	    return 0;
529 	} else if (c == 0) {
530 	    fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
531 					name);
532 	    return 0;
533 	} else {
534 	    if (c->variable) {
535 		*c->variable = !*c->variable;		/* invert it */
536 		if (c->actionexplanation) {
537 		    printf("%s %s.\n", *c->variable? "Will" : "Won't",
538 							c->actionexplanation);
539 		}
540 		printf("%s %s.\n", *c->variable? "Will" : "Won't",
541 							c->actionexplanation);
542 	    }
543 	    if (c->handler) {
544 		retval &= (*c->handler)(c);
545 	    }
546 	}
547     }
548     return retval;
549 }
550 
551 /*
552  * The following perform the "set" command.
553  */
554 
555 struct setlist {
556     char *name;				/* name */
557     char *help;				/* help information */
558     char *charp;			/* where it is located at */
559 };
560 
561 static struct setlist Setlist[] = {
562     { "echo", 	"character to toggle local echoing on/off", &echoc },
563     { "escape",	"character to escape back to telnet command mode", &escape },
564     { " ", "" },
565     { " ", "The following need 'localchars' to be toggled true", 0 },
566     { "erase",	"character to cause an Erase Character", &termEraseChar },
567     { "flushoutput", "character to cause an Abort Oubput", &termFlushChar },
568     { "interrupt", "character to cause an Interrupt Process", &termIntChar },
569     { "kill",	"character to cause an Erase Line", &termKillChar },
570     { "quit",	"character to cause a Break", &termQuitChar },
571     { "eof",	"character to cause an EOF ", &termEofChar },
572     { 0 }
573 };
574 
575 static char **
576 getnextset(name)
577 char *name;
578 {
579     struct setlist *c = (struct setlist *)name;
580 
581     return (char **) (c+1);
582 }
583 
584 static struct setlist *
585 getset(name)
586 char *name;
587 {
588     return (struct setlist *) genget(name, (char **) Setlist, getnextset);
589 }
590 
591 static
592 setcmd(argc, argv)
593 int	argc;
594 char	*argv[];
595 {
596     int value;
597     struct setlist *ct;
598 
599     /* XXX back we go... sigh */
600     if (argc != 3) {
601 	if ((argc == 2) &&
602 		    ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
603 	    for (ct = Setlist; ct->name; ct++) {
604 		printf("%s\t%s\n", ct->name, ct->help);
605 	    }
606 	    printf("?\tdisplay help information\n");
607 	} else {
608 	    printf("Format is 'set Name Value'\n'set ?' for help.\n");
609 	}
610 	return 0;
611     }
612 
613     ct = getset(argv[1]);
614     if (ct == 0) {
615 	fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
616 			argv[1]);
617 	return 0;
618     } else if (Ambiguous(ct)) {
619 	fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
620 			argv[1]);
621 	return 0;
622     } else {
623 	if (strcmp("off", argv[2])) {
624 	    value = special(argv[2]);
625 	} else {
626 	    value = -1;
627 	}
628 	*(ct->charp) = value;
629 	printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
630     }
631     return 1;
632 }
633 
634 /*
635  * The following are the data structures and routines for the
636  * 'mode' command.
637  */
638 
639 static
640 dolinemode()
641 {
642     if (hisopts[TELOPT_SGA]) {
643 	wontoption(TELOPT_SGA, 0);
644     }
645     if (hisopts[TELOPT_ECHO]) {
646 	wontoption(TELOPT_ECHO, 0);
647     }
648     return 1;
649 }
650 
651 static
652 docharmode()
653 {
654     if (!hisopts[TELOPT_SGA]) {
655 	willoption(TELOPT_SGA, 0);
656     }
657     if (!hisopts[TELOPT_ECHO]) {
658 	willoption(TELOPT_ECHO, 0);
659     }
660     return 1;
661 }
662 
663 static Command Mode_commands[] = {
664     { "character",	"character-at-a-time mode",	docharmode, 1, 1 },
665     { "line",		"line-by-line mode",		dolinemode, 1, 1 },
666     { 0 },
667 };
668 
669 static char **
670 getnextmode(name)
671 char *name;
672 {
673     Command *c = (Command *) name;
674 
675     return (char **) (c+1);
676 }
677 
678 static Command *
679 getmodecmd(name)
680 char *name;
681 {
682     return (Command *) genget(name, (char **) Mode_commands, getnextmode);
683 }
684 
685 static
686 modecmd(argc, argv)
687 int	argc;
688 char	*argv[];
689 {
690     Command *mt;
691 
692     if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
693 	printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
694 	for (mt = Mode_commands; mt->name; mt++) {
695 	    printf("%s\t%s\n", mt->name, mt->help);
696 	}
697 	return 0;
698     }
699     mt = getmodecmd(argv[1]);
700     if (mt == 0) {
701 	fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
702 	return 0;
703     } else if (Ambiguous(mt)) {
704 	fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
705 	return 0;
706     } else {
707 	(*mt->handler)();
708     }
709     return 1;
710 }
711 
712 /*
713  * The following data structures and routines implement the
714  * "display" command.
715  */
716 
717 static
718 display(argc, argv)
719 int	argc;
720 char	*argv[];
721 {
722 #define	dotog(tl)	if (tl->variable && tl->actionexplanation) { \
723 			    if (*tl->variable) { \
724 				printf("will"); \
725 			    } else { \
726 				printf("won't"); \
727 			    } \
728 			    printf(" %s.\n", tl->actionexplanation); \
729 			}
730 
731 #define	doset(sl)   if (sl->name && *sl->name != ' ') { \
732 			printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
733 		    }
734 
735     struct togglelist *tl;
736     struct setlist *sl;
737 
738     if (argc == 1) {
739 	for (tl = Togglelist; tl->name; tl++) {
740 	    dotog(tl);
741 	}
742 	printf("\n");
743 	for (sl = Setlist; sl->name; sl++) {
744 	    doset(sl);
745 	}
746     } else {
747 	int i;
748 
749 	for (i = 1; i < argc; i++) {
750 	    sl = getset(argv[i]);
751 	    tl = gettoggle(argv[i]);
752 	    if (Ambiguous(sl) || Ambiguous(tl)) {
753 		printf("?Ambiguous argument '%s'.\n", argv[i]);
754 		return 0;
755 	    } else if (!sl && !tl) {
756 		printf("?Unknown argument '%s'.\n", argv[i]);
757 		return 0;
758 	    } else {
759 		if (tl) {
760 		    dotog(tl);
761 		}
762 		if (sl) {
763 		    doset(sl);
764 		}
765 	    }
766 	}
767     }
768     return 1;
769 #undef	doset
770 #undef	dotog
771 }
772 
773 /*
774  * The following are the data structures, and many of the routines,
775  * relating to command processing.
776  */
777 
778 /*
779  * Set the escape character.
780  */
781 static
782 setescape(argc, argv)
783 	int argc;
784 	char *argv[];
785 {
786 	register char *arg;
787 	char buf[50];
788 
789 	printf(
790 	    "Deprecated usage - please use 'set escape%s%s' in the future.\n",
791 				(argc > 2)? " ":"", (argc > 2)? argv[1]: "");
792 	if (argc > 2)
793 		arg = argv[1];
794 	else {
795 		printf("new escape character: ");
796 		gets(buf);
797 		arg = buf;
798 	}
799 	if (arg[0] != '\0')
800 		escape = arg[0];
801 	if (!In3270) {
802 		printf("Escape character is '%s'.\n", control(escape));
803 	}
804 	fflush(stdout);
805 	return 1;
806 }
807 
808 /*VARARGS*/
809 static
810 togcrmod()
811 {
812     crmod = !crmod;
813     printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
814     printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
815     fflush(stdout);
816     return 1;
817 }
818 
819 /*VARARGS*/
820 suspend()
821 {
822 	setcommandmode();
823 #if	defined(unix)
824 	kill(0, SIGTSTP);
825 #endif	/* defined(unix) */
826 	/* reget parameters in case they were changed */
827 	TerminalSaveState();
828 	setconnmode();
829 	return 1;
830 }
831 
832 /*VARARGS*/
833 static
834 bye(argc, argv)
835 int	argc;		/* Number of arguments */
836 char	*argv[];	/* arguments */
837 {
838     if (connected) {
839 	shutdown(net, 2);
840 	printf("Connection closed.\n");
841 	NetClose(net);
842 	connected = 0;
843 	/* reset options */
844 	tninit();
845 #if	defined(TN3270)
846 	SetIn3270();		/* Get out of 3270 mode */
847 #endif	/* defined(TN3270) */
848     }
849     if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
850 	longjmp(toplevel, 1);
851 	/* NOTREACHED */
852     }
853     return 1;			/* Keep lint, etc., happy */
854 }
855 
856 /*VARARGS*/
857 quit()
858 {
859 	(void) call(bye, "bye", "fromquit", 0);
860 	Exit(0);
861 	/*NOTREACHED*/
862 	return 1;			/* just to keep lint happy */
863 }
864 
865 /*
866  * Print status about the connection.
867  */
868 static
869 status(argc, argv)
870 int	argc;
871 char	*argv[];
872 {
873     if (connected) {
874 	printf("Connected to %s.\n", hostname);
875 	if (argc < 2) {
876 	    printf("Operating in %s.\n",
877 				modelist[getconnmode()].modedescriptions);
878 	    if (localchars) {
879 		printf("Catching signals locally.\n");
880 	    }
881 	}
882     } else {
883 	printf("No connection.\n");
884     }
885 #   if !defined(TN3270)
886     printf("Escape character is '%s'.\n", control(escape));
887     fflush(stdout);
888 #   else /* !defined(TN3270) */
889     if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
890 	printf("Escape character is '%s'.\n", control(escape));
891     }
892 #   if defined(unix)
893     if (In3270 && transcom) {
894        printf("Transparent mode command is '%s'.\n", transcom);
895     }
896 #   endif /* defined(unix) */
897     fflush(stdout);
898     if (In3270) {
899 	return 0;
900     }
901 #   endif /* defined(TN3270) */
902     return 1;
903 }
904 
905 #if	defined(TN3270) && defined(unix)
906 static
907 settranscom(argc, argv)
908 	int argc;
909 	char *argv[];
910 {
911 	int i, len = 0;
912 	char *strcpy(), *strcat();
913 
914 	if (argc == 1 && transcom) {
915 	   transcom = 0;
916 	}
917 	if (argc == 1) {
918 	   return;
919 	}
920 	for (i = 1; i < argc; ++i) {
921 	    len += 1 + strlen(argv[1]);
922 	}
923 	transcom = tline;
924 	(void) strcpy(transcom, argv[1]);
925 	for (i = 2; i < argc; ++i) {
926 	    (void) strcat(transcom, " ");
927 	    (void) strcat(transcom, argv[i]);
928 	}
929 }
930 #endif	/* defined(TN3270) && defined(unix) */
931 
932 
933 
934 int
935 tn(argc, argv)
936 	int argc;
937 	char *argv[];
938 {
939     register struct hostent *host = 0;
940     struct sockaddr_in sin;
941     struct servent *sp = 0;
942     static char	hnamebuf[32];
943 
944 
945 #if defined(MSDOS)
946     char *cp;
947 #endif	/* defined(MSDOS) */
948 
949     if (connected) {
950 	printf("?Already connected to %s\n", hostname);
951 	return 0;
952     }
953     if (argc < 2) {
954 	(void) strcpy(line, "Connect ");
955 	printf("(to) ");
956 	gets(&line[strlen(line)]);
957 	makeargv();
958 	argc = margc;
959 	argv = margv;
960     }
961     if ((argc < 2) || (argc > 3)) {
962 	printf("usage: %s host-name [port]\n", argv[0]);
963 	return 0;
964     }
965 #if	defined(MSDOS)
966     for (cp = argv[1]; *cp; cp++) {
967 	if (isupper(*cp)) {
968 	    *cp = tolower(*cp);
969 	}
970     }
971 #endif	/* defined(MSDOS) */
972     sin.sin_addr.s_addr = inet_addr(argv[1]);
973     if (sin.sin_addr.s_addr != -1) {
974 	sin.sin_family = AF_INET;
975 	(void) strcpy(hnamebuf, argv[1]);
976 	hostname = hnamebuf;
977     } else {
978 	host = gethostbyname(argv[1]);
979 	if (host) {
980 	    sin.sin_family = host->h_addrtype;
981 #if	defined(h_addr)		/* In 4.3, this is a #define */
982 	    memcpy((caddr_t)&sin.sin_addr,
983 				host->h_addr_list[0], host->h_length);
984 #else	/* defined(h_addr) */
985 	    memcpy((caddr_t)&sin.sin_addr, host->h_addr, host->h_length);
986 #endif	/* defined(h_addr) */
987 	    hostname = host->h_name;
988 	} else {
989 	    printf("%s: unknown host\n", argv[1]);
990 	    return 0;
991 	}
992     }
993     if (argc == 3) {
994 	sin.sin_port = atoi(argv[2]);
995 	if (sin.sin_port == 0) {
996 	    sp = getservbyname(argv[2], "tcp");
997 	    if (sp)
998 		sin.sin_port = sp->s_port;
999 	    else {
1000 		printf("%s: bad port number\n", argv[2]);
1001 		return 0;
1002 	    }
1003 	} else {
1004 	    sin.sin_port = atoi(argv[2]);
1005 	    sin.sin_port = htons(sin.sin_port);
1006 	}
1007 	telnetport = 0;
1008     } else {
1009 	if (sp == 0) {
1010 	    sp = getservbyname("telnet", "tcp");
1011 	    if (sp == 0) {
1012 		fprintf(stderr, "telnet: tcp/telnet: unknown service\n",1);
1013 		return 0;
1014 	    }
1015 	    sin.sin_port = sp->s_port;
1016 	}
1017 	telnetport = 1;
1018     }
1019 #if	defined(unix)
1020     signal(SIGINT, intr);
1021     signal(SIGQUIT, intr2);
1022     signal(SIGPIPE, deadpeer);
1023 #endif	/* defined(unix) */
1024     printf("Trying...\n");
1025     do {
1026 	net = socket(AF_INET, SOCK_STREAM, 0);
1027 	if (net < 0) {
1028 	    perror("telnet: socket");
1029 	    return 0;
1030 	}
1031 	if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
1032 		perror("setsockopt (SO_DEBUG)");
1033 	}
1034 
1035 	if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
1036 #if	defined(h_addr)		/* In 4.3, this is a #define */
1037 	    if (host && host->h_addr_list[1]) {
1038 		int oerrno = errno;
1039 
1040 		fprintf(stderr, "telnet: connect to address %s: ",
1041 						inet_ntoa(sin.sin_addr));
1042 		errno = oerrno;
1043 		perror((char *)0);
1044 		host->h_addr_list++;
1045 		memcpy((caddr_t)&sin.sin_addr,
1046 			host->h_addr_list[0], host->h_length);
1047 		fprintf(stderr, "Trying %s...\n",
1048 			inet_ntoa(sin.sin_addr));
1049 		(void) NetClose(net);
1050 		continue;
1051 	    }
1052 #endif	/* defined(h_addr) */
1053 	    perror("telnet: Unable to connect to remote host");
1054 #if defined(unix)
1055 	    signal(SIGINT, SIG_DFL);
1056 	    signal(SIGQUIT, SIG_DFL);
1057 #endif	/* defined(unix) */
1058 	    return 0;
1059 	    }
1060 	connected++;
1061     } while (connected == 0);
1062     call(status, "status", "notmuch", 0);
1063     if (setjmp(peerdied) == 0)
1064 	telnet();
1065     NetClose(net);
1066     ExitString("Connection closed by foreign host.\n",1);
1067     /*NOTREACHED*/
1068 }
1069 
1070 
1071 #define HELPINDENT (sizeof ("connect"))
1072 
1073 static char
1074 	openhelp[] =	"connect to a site",
1075 	closehelp[] =	"close current connection",
1076 	quithelp[] =	"exit telnet",
1077 	statushelp[] =	"print status information",
1078 	helphelp[] =	"print help information",
1079 	sendhelp[] =	"transmit special characters ('send ?' for more)",
1080 	sethelp[] = 	"set operating parameters ('set ?' for more)",
1081 	togglestring[] ="toggle operating parameters ('toggle ?' for more)",
1082 	displayhelp[] =	"display operating parameters",
1083 #if	defined(TN3270) && defined(unix)
1084 	transcomhelp[] = "specify Unix command for transparent mode pipe",
1085 #endif	/* defined(TN3270) && defined(unix) */
1086 #if	defined(unix)
1087 	zhelp[] =	"suspend telnet",
1088 #endif	/* defined(unix */
1089 #if	defined(TN3270)
1090 	shellhelp[] =	"invoke a subshell",
1091 #endif	/* defined(TN3270) */
1092 	modehelp[] = "try to enter line-by-line or character-at-a-time mode";
1093 
1094 extern int	help(), shell();
1095 
1096 static Command cmdtab[] = {
1097 	{ "close",	closehelp,	bye,		1, 1 },
1098 	{ "display",	displayhelp,	display,	1, 0 },
1099 	{ "mode",	modehelp,	modecmd,	1, 1 },
1100 	{ "open",	openhelp,	tn,		1, 0 },
1101 	{ "quit",	quithelp,	quit,		1, 0 },
1102 	{ "send",	sendhelp,	sendcmd,	1, 1 },
1103 	{ "set",	sethelp,	setcmd,		1, 0 },
1104 	{ "status",	statushelp,	status,		1, 0 },
1105 	{ "toggle",	togglestring,	toggle,		1, 0 },
1106 #if	defined(TN3270) && defined(unix)
1107 	{ "transcom",	transcomhelp,	settranscom,	1, 0 },
1108 #endif	/* defined(TN3270) && defined(unix) */
1109 #if	defined(unix)
1110 	{ "z",		zhelp,		suspend,	1, 0 },
1111 #endif	/* defined(unix) */
1112 #if	defined(TN3270)
1113 	{ "!",		shellhelp,	shell,		1, 1 },
1114 #endif	/* defined(TN3270) */
1115 	{ "?",		helphelp,	help,		1, 0 },
1116 	0
1117 };
1118 
1119 static char	crmodhelp[] =	"deprecated command -- use 'toggle crmod' instead";
1120 static char	escapehelp[] =	"deprecated command -- use 'set escape' instead";
1121 
1122 static Command cmdtab2[] = {
1123 	{ "help",	helphelp,	help,		0, 0 },
1124 	{ "escape",	escapehelp,	setescape,	1, 0 },
1125 	{ "crmod",	crmodhelp,	togcrmod,	1, 0 },
1126 	0
1127 };
1128 
1129 /*
1130  * Call routine with argc, argv set from args (terminated by 0).
1131  * VARARGS2
1132  */
1133 static
1134 call(routine, args)
1135 	int (*routine)();
1136 	char *args;
1137 {
1138 	register char **argp;
1139 	register int argc;
1140 
1141 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
1142 		;
1143 	return (*routine)(argc, &args);
1144 }
1145 
1146 static char **
1147 getnextcmd(name)
1148 char *name;
1149 {
1150     Command *c = (Command *) name;
1151 
1152     return (char **) (c+1);
1153 }
1154 
1155 static Command *
1156 getcmd(name)
1157 char *name;
1158 {
1159     Command *cm;
1160 
1161     if ((cm = (Command *) genget(name, (char **) cmdtab, getnextcmd)) != 0) {
1162 	return cm;
1163     } else {
1164 	return (Command *) genget(name, (char **) cmdtab2, getnextcmd);
1165     }
1166 }
1167 
1168 void
1169 command(top)
1170 	int top;
1171 {
1172     register Command *c;
1173 
1174     setcommandmode();
1175     if (!top) {
1176 	putchar('\n');
1177     } else {
1178 #if	defined(unix)
1179 	signal(SIGINT, SIG_DFL);
1180 	signal(SIGQUIT, SIG_DFL);
1181 #endif	/* defined(unix) */
1182     }
1183     for (;;) {
1184 	printf("%s> ", prompt);
1185 	if (gets(line) == NULL) {
1186 	    if (feof(stdin) || ferror(stdin))
1187 		quit();
1188 	    break;
1189 	}
1190 	if (line[0] == 0)
1191 	    break;
1192 	makeargv();
1193 	c = getcmd(margv[0]);
1194 	if (Ambiguous(c)) {
1195 	    printf("?Ambiguous command\n");
1196 	    continue;
1197 	}
1198 	if (c == 0) {
1199 	    printf("?Invalid command\n");
1200 	    continue;
1201 	}
1202 	if (c->needconnect && !connected) {
1203 	    printf("?Need to be connected first.\n");
1204 	    continue;
1205 	}
1206 	if ((*c->handler)(margc, margv)) {
1207 	    break;
1208 	}
1209     }
1210     if (!top) {
1211 	if (!connected) {
1212 	    longjmp(toplevel, 1);
1213 	    /*NOTREACHED*/
1214 	}
1215 #if	defined(TN3270)
1216 	if (shell_active == 0) {
1217 	    setconnmode();
1218 	}
1219 #else	/* defined(TN3270) */
1220 	setconnmode();
1221 #endif	/* defined(TN3270) */
1222     }
1223 }
1224 
1225 /*
1226  * Help command.
1227  */
1228 static
1229 help(argc, argv)
1230 	int argc;
1231 	char *argv[];
1232 {
1233 	register Command *c;
1234 
1235 	if (argc == 1) {
1236 		printf("Commands may be abbreviated.  Commands are:\n\n");
1237 		for (c = cmdtab; c->name; c++)
1238 			if (c->dohelp) {
1239 				printf("%-*s\t%s\n", HELPINDENT, c->name,
1240 								    c->help);
1241 			}
1242 		return 0;
1243 	}
1244 	while (--argc > 0) {
1245 		register char *arg;
1246 		arg = *++argv;
1247 		c = getcmd(arg);
1248 		if (Ambiguous(c))
1249 			printf("?Ambiguous help command %s\n", arg);
1250 		else if (c == (Command *)0)
1251 			printf("?Invalid help command %s\n", arg);
1252 		else
1253 			printf("%s\n", c->help);
1254 	}
1255 	return 0;
1256 }
1257