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