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