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