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