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