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