date and time created 83/09/02 12:57:42 by edward
[unix-history] / usr / src / usr.bin / telnet / telnet.c
CommitLineData
f18adf63 1#ifndef lint
793cf58f 2static char sccsid[] = "@(#)telnet.c 4.24 (Berkeley) %G%";
f18adf63
SL
3#endif
4
a19db822
BJ
5/*
6 * User telnet program.
7 */
de3b21e8
SL
8#include <sys/types.h>
9#include <sys/socket.h>
fb8e28da 10#include <sys/ioctl.h>
de3b21e8
SL
11
12#include <netinet/in.h>
13
9c1dab9e
SL
14#define TELOPTS
15#include <arpa/telnet.h>
16
a19db822
BJ
17#include <stdio.h>
18#include <ctype.h>
19#include <errno.h>
20#include <signal.h>
a19db822 21#include <setjmp.h>
9f005877 22#include <netdb.h>
de3b21e8 23
a19db822 24#define strip(x) ((x)&0177)
a19db822
BJ
25
26char ttyobuf[BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
a50d5753 27char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
a19db822
BJ
28
29char hisopts[256];
30char myopts[256];
31
32char doopt[] = { IAC, DO, '%', 'c', 0 };
33char dont[] = { IAC, DONT, '%', 'c', 0 };
34char will[] = { IAC, WILL, '%', 'c', 0 };
35char wont[] = { IAC, WONT, '%', 'c', 0 };
36
37int connected;
38int net;
fb8e28da 39int showoptions = 0;
19baf46e 40int options;
4971ba8c 41int debug = 0;
fb8e28da 42int crmod = 0;
a19db822 43char *prompt;
fb8e28da 44char escape = CTRL(]);
a19db822
BJ
45
46char line[200];
47int margc;
48char *margv[20];
49
50jmp_buf toplevel;
51jmp_buf peerdied;
52
53extern int errno;
54
55int tn(), quit(), suspend(), bye(), help();
de372207 56int setescape(), status(), toggle(), setoptions();
5d6648c9 57int setcrmod(), setdebug();
a19db822 58
a50d5753 59#define HELPINDENT (sizeof ("connect"))
a19db822
BJ
60
61struct cmd {
f18adf63
SL
62 char *name; /* command name */
63 char *help; /* help string */
64 int (*handler)(); /* routine which executes command */
a19db822
BJ
65};
66
f18adf63
SL
67char openhelp[] = "connect to a site";
68char closehelp[] = "close current connection";
69char quithelp[] = "exit telnet";
70char zhelp[] = "suspend telnet";
71char debughelp[] = "toggle debugging";
72char escapehelp[] = "set escape character";
73char statushelp[] = "print status information";
74char helphelp[] = "print help information";
75char optionshelp[] = "toggle viewing of options processing";
76char crmodhelp[] = "toggle mapping of received carriage returns";
a19db822
BJ
77
78struct cmd cmdtab[] = {
f18adf63
SL
79 { "open", openhelp, tn },
80 { "close", closehelp, bye },
81 { "quit", quithelp, quit },
a19db822 82 { "z", zhelp, suspend },
f18adf63
SL
83 { "escape", escapehelp, setescape },
84 { "status", statushelp, status },
85 { "options", optionshelp, setoptions },
86 { "crmod", crmodhelp, setcrmod },
87 { "debug", debughelp, setdebug },
88 { "?", helphelp, help },
a19db822
BJ
89 0
90};
91
fb8e28da 92struct sockaddr_in sin;
a19db822
BJ
93
94int intr(), deadpeer();
95char *control();
96struct cmd *getcmd();
9f005877 97struct servent *sp;
a19db822 98
ce292c83
SL
99struct tchars otc;
100struct ltchars oltc;
101struct sgttyb ottyb;
a50d5753 102
a19db822
BJ
103main(argc, argv)
104 int argc;
105 char *argv[];
106{
9f005877
SL
107 sp = getservbyname("telnet", "tcp");
108 if (sp == 0) {
109 fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
110 exit(1);
111 }
ce292c83
SL
112 ioctl(0, TIOCGETP, (char *)&ottyb);
113 ioctl(0, TIOCGETC, (char *)&otc);
114 ioctl(0, TIOCGLTC, (char *)&oltc);
a19db822
BJ
115 setbuf(stdin, 0);
116 setbuf(stdout, 0);
117 prompt = argv[0];
19baf46e 118 if (argc > 1 && !strcmp(argv[1], "-d"))
793cf58f 119 debug = SO_DEBUG, argv++, argc--;
a19db822
BJ
120 if (argc != 1) {
121 if (setjmp(toplevel) != 0)
122 exit(0);
123 tn(argc, argv);
124 }
125 setjmp(toplevel);
126 for (;;)
127 command(1);
128}
129
150ad7e5
SL
130char *hostname;
131char hnamebuf[32];
a19db822
BJ
132
133tn(argc, argv)
134 int argc;
135 char *argv[];
136{
137 register int c;
150ad7e5 138 register struct hostent *host;
a19db822
BJ
139
140 if (connected) {
150ad7e5 141 printf("?Already connected to %s\n", hostname);
a19db822
BJ
142 return;
143 }
144 if (argc < 2) {
145 strcpy(line, "Connect ");
146 printf("(to) ");
147 gets(&line[strlen(line)]);
148 makeargv();
149 argc = margc;
150 argv = margv;
151 }
152 if (argc > 3) {
153 printf("usage: %s host-name [port]\n", argv[0]);
154 return;
155 }
9f005877 156 host = gethostbyname(argv[1]);
150ad7e5 157 if (host) {
de3b21e8
SL
158 sin.sin_family = host->h_addrtype;
159 bcopy(host->h_addr, (caddr_t)&sin.sin_addr, host->h_length);
150ad7e5
SL
160 hostname = host->h_name;
161 } else {
de3b21e8 162 sin.sin_family = AF_INET;
150ad7e5
SL
163 sin.sin_addr.s_addr = inet_addr(argv[1]);
164 if (sin.sin_addr.s_addr == -1) {
165 printf("%s: unknown host\n", argv[1]);
166 return;
167 }
168 strcpy(hnamebuf, argv[1]);
169 hostname = hnamebuf;
a19db822 170 }
9f005877 171 sin.sin_port = sp->s_port;
de372207
SL
172 if (argc == 3) {
173 sin.sin_port = atoi(argv[2]);
174 if (sin.sin_port < 0) {
175 printf("%s: bad port number\n", argv[2]);
176 return;
177 }
5eba30a3 178 sin.sin_port = htons(sin.sin_port);
de372207 179 }
9de2cbb6 180 net = socket(AF_INET, SOCK_STREAM, 0, 0);
de3b21e8
SL
181 if (net < 0) {
182 perror("telnet: socket");
a19db822
BJ
183 return;
184 }
f18adf63
SL
185 if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
186 perror("setsockopt (SO_DEBUG)");
4ca10280
SL
187 signal(SIGINT, intr);
188 signal(SIGPIPE, deadpeer);
a19db822 189 printf("Trying...\n");
de3b21e8
SL
190 if (connect(net, (caddr_t)&sin, sizeof (sin), 0) < 0) {
191 perror("telnet: connect");
4ca10280 192 signal(SIGINT, SIG_DFL);
a19db822
BJ
193 return;
194 }
a19db822
BJ
195 connected++;
196 call(status, "status", 0);
197 if (setjmp(peerdied) == 0)
198 telnet(net);
199 fprintf(stderr, "Connection closed by foreign host.\n");
200 exit(1);
201}
202
203/*
204 * Print status about the connection.
205 */
206/*VARARGS*/
207status()
208{
209 if (connected)
150ad7e5 210 printf("Connected to %s.\n", hostname);
a19db822
BJ
211 else
212 printf("No connection.\n");
213 printf("Escape character is '%s'.\n", control(escape));
fb8e28da 214 fflush(stdout);
a19db822
BJ
215}
216
217makeargv()
218{
219 register char *cp;
220 register char **argp = margv;
221
222 margc = 0;
223 for (cp = line; *cp;) {
224 while (isspace(*cp))
225 cp++;
226 if (*cp == '\0')
227 break;
228 *argp++ = cp;
229 margc += 1;
230 while (*cp != '\0' && !isspace(*cp))
231 cp++;
232 if (*cp == '\0')
233 break;
234 *cp++ = '\0';
235 }
236 *argp++ = 0;
237}
238
239/*VARARGS*/
240suspend()
241{
242 register int save;
243
244 save = mode(0);
a50d5753
SL
245 kill(0, SIGTSTP);
246 /* reget parameters in case they were changed */
ce292c83
SL
247 ioctl(0, TIOCGETP, (char *)&ottyb);
248 ioctl(0, TIOCGETC, (char *)&otc);
249 ioctl(0, TIOCGLTC, (char *)&oltc);
a50d5753 250 (void) mode(save);
a19db822
BJ
251}
252
253/*VARARGS*/
254bye()
255{
a50d5753 256 register char *op;
a19db822 257
a50d5753 258 (void) mode(0);
a19db822 259 if (connected) {
f18adf63 260 shutdown(net, 2);
a19db822
BJ
261 printf("Connection closed.\n");
262 close(net);
263 connected = 0;
a50d5753
SL
264 /* reset his options */
265 for (op = hisopts; op < &hisopts[256]; op++)
266 *op = 0;
a19db822
BJ
267 }
268}
269
270/*VARARGS*/
271quit()
272{
273 call(bye, "bye", 0);
274 exit(0);
275}
276
277/*
278 * Help command.
a19db822
BJ
279 */
280help(argc, argv)
281 int argc;
282 char *argv[];
283{
284 register struct cmd *c;
285
286 if (argc == 1) {
287 printf("Commands may be abbreviated. Commands are:\n\n");
288 for (c = cmdtab; c->name; c++)
289 printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
290 return;
291 }
292 while (--argc > 0) {
293 register char *arg;
294 arg = *++argv;
295 c = getcmd(arg);
296 if (c == (struct cmd *)-1)
297 printf("?Ambiguous help command %s\n", arg);
298 else if (c == (struct cmd *)0)
299 printf("?Invalid help command %s\n", arg);
300 else
301 printf("%s\n", c->help);
302 }
303}
304
305/*
306 * Call routine with argc, argv set from args (terminated by 0).
307 * VARARGS2
308 */
309call(routine, args)
310 int (*routine)();
311 int args;
312{
313 register int *argp;
314 register int argc;
315
316 for (argc = 0, argp = &args; *argp++ != 0; argc++)
317 ;
318 (*routine)(argc, &args);
319}
320
ce292c83
SL
321struct tchars notc = { -1, -1, -1, -1, -1, -1 };
322struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
fb8e28da 323
a19db822
BJ
324mode(f)
325 register int f;
326{
a50d5753 327 static int prevmode = 0;
ce292c83
SL
328 struct tchars *tc;
329 struct ltchars *ltc;
330 struct sgttyb sb;
331 int onoff, old;
a50d5753
SL
332
333 if (prevmode == f)
334 return (f);
335 old = prevmode;
336 prevmode = f;
ce292c83 337 sb = ottyb;
a19db822 338 switch (f) {
a50d5753 339
a19db822 340 case 0:
a19db822 341 onoff = 0;
fb8e28da 342 tc = &otc;
ce292c83 343 ltc = &oltc;
a19db822
BJ
344 break;
345
346 case 1:
a19db822 347 case 2:
ce292c83 348 sb.sg_flags |= CBREAK;
a50d5753 349 if (f == 1)
ce292c83 350 sb.sg_flags &= ~(ECHO|CRMOD);
a50d5753 351 else
ce292c83
SL
352 sb.sg_flags |= ECHO|CRMOD;
353 sb.sg_erase = sb.sg_kill = -1;
fb8e28da 354 tc = &notc;
ce292c83 355 ltc = &noltc;
a19db822 356 onoff = 1;
fb8e28da
SL
357 break;
358
359 default:
360 return;
a19db822 361 }
ce292c83
SL
362 ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
363 ioctl(fileno(stdin), TIOCSETC, (char *)tc);
364 ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
a19db822
BJ
365 ioctl(fileno(stdin), FIONBIO, &onoff);
366 ioctl(fileno(stdout), FIONBIO, &onoff);
367 return (old);
368}
369
370char sibuf[BUFSIZ], *sbp;
371char tibuf[BUFSIZ], *tbp;
372int scc, tcc;
373
374/*
375 * Select from tty and network...
376 */
377telnet(s)
378 int s;
379{
380 register int c;
381 int tin = fileno(stdin), tout = fileno(stdout);
382 int on = 1;
383
a50d5753 384 (void) mode(2);
a19db822
BJ
385 ioctl(s, FIONBIO, &on);
386 for (;;) {
387 int ibits = 0, obits = 0;
388
389 if (nfrontp - nbackp)
390 obits |= (1 << s);
391 else
392 ibits |= (1 << tin);
393 if (tfrontp - tbackp)
394 obits |= (1 << tout);
395 else
396 ibits |= (1 << s);
397 if (scc < 0 && tcc < 0)
398 break;
de3b21e8 399 select(16, &ibits, &obits, 0, 0);
a19db822
BJ
400 if (ibits == 0 && obits == 0) {
401 sleep(5);
402 continue;
403 }
404
405 /*
406 * Something to read from the network...
407 */
408 if (ibits & (1 << s)) {
a50d5753 409 scc = read(s, sibuf, sizeof (sibuf));
a19db822
BJ
410 if (scc < 0 && errno == EWOULDBLOCK)
411 scc = 0;
412 else {
413 if (scc <= 0)
414 break;
415 sbp = sibuf;
416 }
417 }
418
419 /*
420 * Something to read from the tty...
421 */
422 if (ibits & (1 << tin)) {
a50d5753 423 tcc = read(tin, tibuf, sizeof (tibuf));
a19db822
BJ
424 if (tcc < 0 && errno == EWOULDBLOCK)
425 tcc = 0;
426 else {
427 if (tcc <= 0)
428 break;
429 tbp = tibuf;
430 }
431 }
432
433 while (tcc > 0) {
434 register int c;
435
436 if ((&netobuf[BUFSIZ] - nfrontp) < 2)
437 break;
438 c = *tbp++ & 0377, tcc--;
439 if (strip(c) == escape) {
440 command(0);
441 tcc = 0;
442 break;
443 }
f18adf63
SL
444 if (c == IAC)
445 *nfrontp++ = c;
a19db822
BJ
446 *nfrontp++ = c;
447 }
448 if ((obits & (1 << s)) && (nfrontp - nbackp) > 0)
449 netflush(s);
450 if (scc > 0)
451 telrcv();
452 if ((obits & (1 << tout)) && (tfrontp - tbackp) > 0)
453 ttyflush(tout);
454 }
a50d5753 455 (void) mode(0);
a19db822
BJ
456}
457
458command(top)
459 int top;
460{
461 register struct cmd *c;
462 int oldmode, wasopen;
463
464 oldmode = mode(0);
465 if (!top)
466 putchar('\n');
467 else
4ca10280 468 signal(SIGINT, SIG_DFL);
a19db822
BJ
469 for (;;) {
470 printf("%s> ", prompt);
baba6eb5
SL
471 if (gets(line) == 0) {
472 if (feof(stdin)) {
473 clearerr(stdin);
474 putchar('\n');
475 }
a19db822 476 break;
baba6eb5 477 }
a19db822
BJ
478 if (line[0] == 0)
479 break;
480 makeargv();
481 c = getcmd(margv[0]);
482 if (c == (struct cmd *)-1) {
483 printf("?Ambiguous command\n");
484 continue;
485 }
486 if (c == 0) {
487 printf("?Invalid command\n");
488 continue;
489 }
490 (*c->handler)(margc, margv);
491 if (c->handler != help)
492 break;
493 }
494 if (!top) {
495 if (!connected)
496 longjmp(toplevel, 1);
a50d5753 497 (void) mode(oldmode);
a19db822
BJ
498 }
499}
500
501/*
502 * Telnet receiver states for fsm
503 */
504#define TS_DATA 0
505#define TS_IAC 1
506#define TS_WILL 2
507#define TS_WONT 3
508#define TS_DO 4
509#define TS_DONT 5
510
511telrcv()
512{
513 register int c;
514 static int state = TS_DATA;
515
516 while (scc > 0) {
517 c = *sbp++ & 0377, scc--;
518 switch (state) {
519
520 case TS_DATA:
fb8e28da 521 if (c == IAC) {
a19db822 522 state = TS_IAC;
fb8e28da
SL
523 continue;
524 }
525 *tfrontp++ = c;
526 /*
527 * This hack is needed since we can't set
528 * CRMOD on output only. Machines like MULTICS
529 * like to send \r without \n; since we must
530 * turn off CRMOD to get proper input, the mapping
531 * is done here (sigh).
532 */
533 if (c == '\r' && crmod)
534 *tfrontp++ = '\n';
a19db822
BJ
535 continue;
536
537 case TS_IAC:
538 switch (c) {
539
540 case WILL:
541 state = TS_WILL;
542 continue;
543
544 case WONT:
545 state = TS_WONT;
546 continue;
547
548 case DO:
549 state = TS_DO;
550 continue;
551
552 case DONT:
553 state = TS_DONT;
554 continue;
555
556 case DM:
557 ioctl(fileno(stdout), TIOCFLUSH, 0);
558 break;
559
560 case NOP:
561 case GA:
562 break;
563
564 default:
565 break;
566 }
567 state = TS_DATA;
568 continue;
569
570 case TS_WILL:
9f005877 571 printoption("RCVD", will, c, !hisopts[c]);
a19db822
BJ
572 if (!hisopts[c])
573 willoption(c);
574 state = TS_DATA;
575 continue;
576
577 case TS_WONT:
9f005877 578 printoption("RCVD", wont, c, hisopts[c]);
a19db822
BJ
579 if (hisopts[c])
580 wontoption(c);
581 state = TS_DATA;
582 continue;
583
584 case TS_DO:
9f005877 585 printoption("RCVD", doopt, c, !myopts[c]);
a19db822
BJ
586 if (!myopts[c])
587 dooption(c);
588 state = TS_DATA;
589 continue;
590
591 case TS_DONT:
9f005877 592 printoption("RCVD", dont, c, myopts[c]);
a19db822
BJ
593 if (myopts[c]) {
594 myopts[c] = 0;
595 sprintf(nfrontp, wont, c);
a50d5753 596 nfrontp += sizeof (wont) - 2;
9f005877 597 printoption("SENT", wont, c);
a19db822
BJ
598 }
599 state = TS_DATA;
600 continue;
601 }
602 }
603}
604
605willoption(option)
606 int option;
607{
608 char *fmt;
609
610 switch (option) {
611
612 case TELOPT_ECHO:
a50d5753 613 (void) mode(1);
a19db822
BJ
614
615 case TELOPT_SGA:
616 hisopts[option] = 1;
617 fmt = doopt;
618 break;
619
620 case TELOPT_TM:
621 fmt = dont;
622 break;
623
624 default:
625 fmt = dont;
626 break;
627 }
de372207 628 sprintf(nfrontp, fmt, option);
a50d5753 629 nfrontp += sizeof (dont) - 2;
9f005877 630 printoption("SENT", fmt, option);
a19db822
BJ
631}
632
633wontoption(option)
634 int option;
635{
636 char *fmt;
637
638 switch (option) {
639
640 case TELOPT_ECHO:
a50d5753 641 (void) mode(2);
a19db822
BJ
642
643 case TELOPT_SGA:
644 hisopts[option] = 0;
645 fmt = dont;
646 break;
647
648 default:
649 fmt = dont;
650 }
651 sprintf(nfrontp, fmt, option);
a50d5753 652 nfrontp += sizeof (doopt) - 2;
9f005877 653 printoption("SENT", fmt, option);
a19db822
BJ
654}
655
656dooption(option)
657 int option;
658{
659 char *fmt;
660
661 switch (option) {
662
663 case TELOPT_TM:
664 fmt = wont;
665 break;
680e328e
SL
666
667 case TELOPT_ECHO:
668 (void) mode(2);
669 fmt = will;
670 hisopts[option] = 0;
671 break;
a19db822
BJ
672
673 case TELOPT_SGA:
674 fmt = will;
675 break;
676
677 default:
678 fmt = wont;
679 break;
680 }
681 sprintf(nfrontp, fmt, option);
a50d5753 682 nfrontp += sizeof (doopt) - 2;
9f005877 683 printoption("SENT", fmt, option);
a19db822
BJ
684}
685
686/*
687 * Set the escape character.
688 */
689setescape(argc, argv)
690 int argc;
691 char *argv[];
692{
693 register char *arg;
694 char buf[50];
695
696 if (argc > 2)
697 arg = argv[1];
698 else {
699 printf("new escape character: ");
700 gets(buf);
701 arg = buf;
702 }
703 if (arg[0] != '\0')
704 escape = arg[0];
705 printf("Escape character is '%s'.\n", control(escape));
fb8e28da 706 fflush(stdout);
a19db822
BJ
707}
708
de372207
SL
709/*VARARGS*/
710setoptions()
711{
fb8e28da 712
de372207
SL
713 showoptions = !showoptions;
714 printf("%s show option processing.\n", showoptions ? "Will" : "Wont");
fb8e28da
SL
715 fflush(stdout);
716}
717
718/*VARARGS*/
719setcrmod()
720{
721
722 crmod = !crmod;
723 printf("%s map carriage return on output.\n", crmod ? "Will" : "Wont");
724 fflush(stdout);
de372207
SL
725}
726
4971ba8c
SL
727/*VARARGS*/
728setdebug()
729{
730
731 debug = !debug;
732 printf("%s turn on socket level debugging.\n",
733 debug ? "Will" : "Wont");
734 fflush(stdout);
793cf58f 735 if (debug && net > 0 && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
f18adf63 736 perror("setsockopt (SO_DEBUG)");
4971ba8c
SL
737}
738
a19db822
BJ
739/*
740 * Construct a control character sequence
741 * for a special character.
742 */
743char *
744control(c)
745 register int c;
746{
747 static char buf[3];
748
749 if (c == 0177)
750 return ("^?");
751 if (c >= 040) {
752 buf[0] = c;
753 buf[1] = 0;
754 } else {
755 buf[0] = '^';
756 buf[1] = '@'+c;
757 buf[2] = 0;
758 }
759 return (buf);
760}
761
762struct cmd *
763getcmd(name)
764 register char *name;
765{
766 register char *p, *q;
767 register struct cmd *c, *found;
768 register int nmatches, longest;
769
770 longest = 0;
771 nmatches = 0;
772 found = 0;
773 for (c = cmdtab; p = c->name; c++) {
774 for (q = name; *q == *p++; q++)
775 if (*q == 0) /* exact match? */
776 return (c);
777 if (!*q) { /* the name was a prefix */
778 if (q - name > longest) {
779 longest = q - name;
780 nmatches = 1;
781 found = c;
782 } else if (q - name == longest)
783 nmatches++;
784 }
785 }
786 if (nmatches > 1)
787 return ((struct cmd *)-1);
788 return (found);
789}
790
791deadpeer()
792{
a50d5753 793 (void) mode(0);
a19db822
BJ
794 longjmp(peerdied, -1);
795}
796
797intr()
798{
a50d5753 799 (void) mode(0);
a19db822
BJ
800 longjmp(toplevel, -1);
801}
802
803ttyflush(fd)
804{
805 int n;
806
807 if ((n = tfrontp - tbackp) > 0)
808 n = write(fd, tbackp, n);
9f005877
SL
809 if (n < 0)
810 return;
a19db822
BJ
811 tbackp += n;
812 if (tbackp == tfrontp)
813 tbackp = tfrontp = ttyobuf;
814}
815
816netflush(fd)
817{
818 int n;
819
820 if ((n = nfrontp - nbackp) > 0)
821 n = write(fd, nbackp, n);
f103d8a9
SL
822 if (n < 0) {
823 if (errno != ENOBUFS && errno != EWOULDBLOCK) {
a50d5753 824 (void) mode(0);
150ad7e5 825 perror(hostname);
f103d8a9
SL
826 close(fd);
827 longjmp(peerdied, -1);
828 /*NOTREACHED*/
829 }
a19db822 830 n = 0;
f103d8a9 831 }
a19db822
BJ
832 nbackp += n;
833 if (nbackp == nfrontp)
834 nbackp = nfrontp = netobuf;
835}
de372207 836
6cebba9f
BJ
837/*VARARGS*/
838printoption(direction, fmt, option, what)
de372207 839 char *direction, *fmt;
6cebba9f 840 int option, what;
de372207 841{
9f005877
SL
842 if (!showoptions)
843 return;
de372207
SL
844 printf("%s ", direction);
845 if (fmt == doopt)
846 fmt = "do";
847 else if (fmt == dont)
848 fmt = "dont";
849 else if (fmt == will)
850 fmt = "will";
851 else if (fmt == wont)
852 fmt = "wont";
853 else
854 fmt = "???";
855 if (option < TELOPT_SUPDUP)
6cebba9f 856 printf("%s %s", fmt, telopts[option]);
de372207 857 else
6cebba9f
BJ
858 printf("%s %d", fmt, option);
859 if (*direction == '<') {
860 printf("\r\n");
861 return;
862 }
863 printf(" (%s)\r\n", what ? "reply" : "don't reply");
de372207 864}