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