cleanups, add manual page
[unix-history] / usr / src / usr.bin / telnet / commands.c
CommitLineData
897ce52e
KB
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
b36fc510
KB
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.
897ce52e
KB
16 */
17
18#ifndef lint
260ef0a4 19static char sccsid[] = "@(#)commands.c 1.10 (Berkeley) %G%";
897ce52e
KB
20#endif /* not lint */
21
59782978
GM
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>
f165dbec 29#include <varargs.h>
59782978
GM
30
31#include <arpa/telnet.h>
32
2dde2af0
GM
33#include "general.h"
34
115a5494
GM
35#include "ring.h"
36
59782978
GM
37#include "externs.h"
38#include "defines.h"
39#include "types.h"
40
41char *hostname;
42
43#define Ambiguous(s) ((char *)s == ambiguous)
44static char *ambiguous; /* special return value for command routines */
45
46typedef 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
54static char line[200];
55static int margc;
56static char *margv[20];
57
58/*
59 * Various utility routines.
60 */
61
62static void
63makeargv()
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
92static char **
93genget(name, table, next)
94char *name; /* name to match */
95char **table; /* name entry in table */
96char **(*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
133static
134special(s)
135register 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 */
160static char *
161control(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
190struct 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\f
201#define SENDQUESTION -1
202#define SENDESCAPE -3
203
204static 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
219static 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
231static char **
232getnextsend(name)
233char *name;
234{
235 struct sendlist *c = (struct sendlist *) name;
236
237 return (char **) (c+1);
238}
239
240static struct sendlist *
241getsend(name)
242char *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
255static
256sendcmd(argc, argv)
257int argc;
258char **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\f
349/*
350 * The following are the routines and data structures referred
351 * to by the arguments to the "toggle" command.
352 */
353
354static
355lclchars()
356{
357 donelclchars = 1;
358 return 1;
359}
360
361static
362togdebug()
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
380static int
381togcrlf()
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
392static int
393togbinary()
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
421extern int togglehelp();
422
423struct 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
432static 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
500static
501togglehelp()
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
513static char **
514getnexttoggle(name)
515char *name;
516{
517 struct togglelist *c = (struct togglelist *) name;
518
519 return (char **) (c+1);
520}
521
522static struct togglelist *
523gettoggle(name)
524char *name;
525{
526 return (struct togglelist *)
527 genget(name, (char **) Togglelist, getnexttoggle);
528}
529
530static
531toggle(argc, argv)
532int argc;
533char *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\f
575/*
576 * The following perform the "set" command.
577 */
578
579struct setlist {
580 char *name; /* name */
581 char *help; /* help information */
582 char *charp; /* where it is located at */
583};
584
585static 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
599static char **
600getnextset(name)
601char *name;
602{
603 struct setlist *c = (struct setlist *)name;
604
605 return (char **) (c+1);
606}
607
608static struct setlist *
609getset(name)
610char *name;
611{
612 return (struct setlist *) genget(name, (char **) Setlist, getnextset);
613}
614
615static
616setcmd(argc, argv)
617int argc;
618char *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\f
658/*
659 * The following are the data structures and routines for the
660 * 'mode' command.
661 */
662
663static
664dolinemode()
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
675static
676docharmode()
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
687static 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
693static char **
694getnextmode(name)
695char *name;
696{
697 Command *c = (Command *) name;
698
699 return (char **) (c+1);
700}
701
702static Command *
703getmodecmd(name)
704char *name;
705{
706 return (Command *) genget(name, (char **) Mode_commands, getnextmode);
707}
708
709static
710modecmd(argc, argv)
711int argc;
712char *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\f
736/*
737 * The following data structures and routines implement the
738 * "display" command.
739 */
740
741static
742display(argc, argv)
743int argc;
744char *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\f
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 */
805static
806setescape(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: ");
80a47e22 820 (void) gets(buf);
59782978
GM
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 }
80a47e22 828 (void) fflush(stdout);
59782978
GM
829 return 1;
830}
831
832/*VARARGS*/
833static
834togcrmod()
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");
80a47e22 839 (void) fflush(stdout);
59782978
GM
840 return 1;
841}
842
843/*VARARGS*/
844suspend()
845{
846 setcommandmode();
847#if defined(unix)
80a47e22 848 (void) kill(0, SIGTSTP);
59782978
GM
849#endif /* defined(unix) */
850 /* reget parameters in case they were changed */
851 TerminalSaveState();
852 setconnmode();
853 return 1;
854}
855
856/*VARARGS*/
857static
858bye(argc, argv)
859int argc; /* Number of arguments */
860char *argv[]; /* arguments */
861{
862 if (connected) {
80a47e22 863 (void) shutdown(net, 2);
59782978 864 printf("Connection closed.\n");
80a47e22 865 (void) NetClose(net);
59782978
GM
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*/
881quit()
882{
883 (void) call(bye, "bye", "fromquit", 0);
884 Exit(0);
59782978
GM
885 return 1; /* just to keep lint happy */
886}
887
888/*
889 * Print status about the connection.
890 */
80a47e22 891/*ARGSUSED*/
59782978
GM
892static
893status(argc, argv)
894int argc;
895char *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));
80a47e22 911 (void) fflush(stdout);
59782978
GM
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) */
80a47e22 921 (void) fflush(stdout);
59782978
GM
922 if (In3270) {
923 return 0;
924 }
925# endif /* defined(TN3270) */
926 return 1;
927}
928
59782978
GM
929
930
931int
932tn(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];
80a47e22 940 unsigned long inet_addr();
59782978
GM
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) ");
80a47e22 954 (void) gets(&line[strlen(line)]);
59782978
GM
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 {
80a47e22
GM
1002#if !defined(htons)
1003 u_short htons();
1004#endif /* !defined(htons) */
1005
59782978
GM
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) {
80a47e22 1014 fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
59782978
GM
1015 return 0;
1016 }
1017 sin.sin_port = sp->s_port;
1018 }
1019 telnetport = 1;
1020 }
59782978
GM
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;
80a47e22 1036 extern char *inet_ntoa();
59782978
GM
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");
59782978
GM
1052 return 0;
1053 }
1054 connected++;
1055 } while (connected == 0);
80a47e22 1056 (void) call(status, "status", "notmuch", 0);
59782978
GM
1057 if (setjmp(peerdied) == 0)
1058 telnet();
80a47e22 1059 (void) NetClose(net);
115a5494 1060 ExitString("Connection closed by foreign host.\n",1);
59782978
GM
1061 /*NOTREACHED*/
1062}
1063
1064
1065#define HELPINDENT (sizeof ("connect"))
1066
1067static 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
1088extern int help(), shell();
1089
1090static 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
1113static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead";
1114static char escapehelp[] = "deprecated command -- use 'set escape' instead";
1115
1116static Command cmdtab2[] = {
1117 { "help", helphelp, help, 0, 0 },
1118 { "escape", escapehelp, setescape, 1, 0 },
1119 { "crmod", crmodhelp, togcrmod, 1, 0 },
1120 0
1121};
1122
f165dbec 1123
59782978
GM
1124/*
1125 * Call routine with argc, argv set from args (terminated by 0).
59782978 1126 */
f165dbec 1127
0ea35455 1128/*VARARGS1*/
59782978 1129static
f165dbec
GM
1130call(va_alist)
1131va_dcl
59782978 1132{
f165dbec
GM
1133 va_list ap;
1134 typedef int (*intrtn_t)();
1135 intrtn_t routine;
1136 char *args[100];
f165dbec
GM
1137 int argno = 0;
1138
1139 va_start(ap);
1140 routine = (va_arg(ap, intrtn_t));
260ef0a4 1141 while ((args[argno++] = va_arg(ap, char *)) != 0) {
f165dbec 1142 ;
260ef0a4 1143 }
f165dbec 1144 va_end(ap);
260ef0a4 1145 return (*routine)(argno-1, args);
59782978
GM
1146}
1147
f165dbec 1148
59782978
GM
1149static char **
1150getnextcmd(name)
1151char *name;
1152{
1153 Command *c = (Command *) name;
1154
1155 return (char **) (c+1);
1156}
1157
1158static Command *
1159getcmd(name)
1160char *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
1171void
1172command(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\f
1228/*
1229 * Help command.
1230 */
1231static
1232help(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}