main.c revision 1.23 1 /* $NetBSD: main.c,v 1.23 2006/04/09 18:47:49 christos Exp $ */
2
3 /*
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
35 The Regents of the University of California. All rights reserved.\n");
36 #if 0
37 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
38 #else
39 __RCSID("$NetBSD: main.c,v 1.23 2006/04/09 18:47:49 christos Exp $");
40 #endif
41 #endif /* not lint */
42
43 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
44
45 /*
46 * TFTP User Program -- Command Interface.
47 */
48 #include <sys/types.h>
49 #include <sys/socket.h>
50
51 #include <netinet/in.h>
52
53 #include <arpa/inet.h>
54 #include <arpa/tftp.h>
55
56 #include <ctype.h>
57 #include <fcntl.h>
58 #include <err.h>
59 #include <errno.h>
60 #include <netdb.h>
61 #include <setjmp.h>
62 #include <signal.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
67
68 #include "extern.h"
69
70 #define TIMEOUT 5 /* secs between rexmt's */
71 #define LBUFLEN 200 /* size of input buffer */
72
73 struct sockaddr_storage peeraddr;
74 int f;
75 int trace;
76 int verbose;
77 int tsize=0;
78 int tout=0;
79 size_t def_blksize=SEGSIZE;
80 size_t blksize=SEGSIZE;
81 int connected;
82 char mode[32];
83 char line[LBUFLEN];
84 int margc;
85 char *margv[20];
86 const char *prompt = "tftp";
87 jmp_buf toplevel;
88
89 void get __P((int, char **));
90 void help __P((int, char **));
91 void modecmd __P((int, char **));
92 void put __P((int, char **));
93 void quit __P((int, char **));
94 void setascii __P((int, char **));
95 void setbinary __P((int, char **));
96 void setpeer0 __P((const char *, const char *));
97 void setpeer __P((int, char **));
98 void setrexmt __P((int, char **));
99 void settimeout __P((int, char **));
100 void settrace __P((int, char **));
101 void setverbose __P((int, char **));
102 void setblksize __P((int, char **));
103 void settsize __P((int, char **));
104 void settimeoutopt __P((int, char **));
105 void status __P((int, char **));
106 char *tail __P((char *));
107 int main __P((int, char *[]));
108 void intr __P((int));
109 const struct cmd *getcmd __P((char *));
110
111 static __dead void command __P((void));
112
113 static void getusage __P((char *));
114 static void makeargv __P((void));
115 static void putusage __P((char *));
116 static void settftpmode __P((const char *));
117
118 #define HELPINDENT (sizeof("connect"))
119
120 struct cmd {
121 const char *name;
122 const char *help;
123 void (*handler) __P((int, char **));
124 };
125
126 const char vhelp[] = "toggle verbose mode";
127 const char thelp[] = "toggle packet tracing";
128 const char tshelp[] = "toggle extended tsize option";
129 const char tohelp[] = "toggle extended timeout option";
130 const char blhelp[] = "set an alternative blocksize (def. 512)";
131 const char chelp[] = "connect to remote tftp";
132 const char qhelp[] = "exit tftp";
133 const char hhelp[] = "print help information";
134 const char shelp[] = "send file";
135 const char rhelp[] = "receive file";
136 const char mhelp[] = "set file transfer mode";
137 const char sthelp[] = "show current status";
138 const char xhelp[] = "set per-packet retransmission timeout";
139 const char ihelp[] = "set total retransmission timeout";
140 const char ashelp[] = "set mode to netascii";
141 const char bnhelp[] = "set mode to octet";
142
143 const struct cmd cmdtab[] = {
144 { "connect", chelp, setpeer },
145 { "mode", mhelp, modecmd },
146 { "put", shelp, put },
147 { "get", rhelp, get },
148 { "quit", qhelp, quit },
149 { "verbose", vhelp, setverbose },
150 { "blksize", blhelp, setblksize },
151 { "tsize", tshelp, settsize },
152 { "trace", thelp, settrace },
153 { "status", sthelp, status },
154 { "binary", bnhelp, setbinary },
155 { "ascii", ashelp, setascii },
156 { "rexmt", xhelp, setrexmt },
157 { "timeout", ihelp, settimeout },
158 { "tout", tohelp, settimeoutopt },
159 { "?", hhelp, help },
160 { 0 }
161 };
162
163 int
164 main(argc, argv)
165 int argc;
166 char *argv[];
167 {
168 int c;
169
170 f = -1;
171 (void)strlcpy(mode, "netascii", sizeof(mode));
172 (void)signal(SIGINT, intr);
173
174 setprogname(argv[0]);
175 while ((c = getopt(argc, argv, "e")) != -1) {
176 switch (c) {
177 case 'e':
178 blksize = MAXSEGSIZE;
179 (void)strlcpy(mode, "octet", sizeof(mode));
180 tsize = 1;
181 tout = 1;
182 break;
183 default:
184 (void)printf("usage: %s [-e] host-name [port]\n",
185 getprogname());
186 exit(1);
187 }
188 }
189 argc -= optind;
190 argv += optind;
191
192 if (argc >= 1) {
193 if (setjmp(toplevel) != 0)
194 exit(0);
195 argc++;
196 argv--;
197 setpeer(argc, argv);
198 }
199 if (setjmp(toplevel) != 0)
200 (void)putchar('\n');
201 command();
202 return (0);
203 }
204
205 char hostname[100];
206
207 void
208 setpeer0(host, port)
209 const char *host;
210 const char *port;
211 {
212 struct addrinfo hints, *res0, *res;
213 int error, soopt;
214 struct sockaddr_storage ss;
215 const char *cause = "unknown";
216
217 if (connected) {
218 (void)close(f);
219 f = -1;
220 }
221 connected = 0;
222
223 (void)memset(&hints, 0, sizeof(hints));
224 hints.ai_family = PF_UNSPEC;
225 hints.ai_socktype = SOCK_DGRAM;
226 hints.ai_protocol = IPPROTO_UDP;
227 hints.ai_flags = AI_CANONNAME;
228 if (!port)
229 port = "tftp";
230 error = getaddrinfo(host, port, &hints, &res0);
231 if (error) {
232 warnx("%s", gai_strerror(error));
233 return;
234 }
235
236 for (res = res0; res; res = res->ai_next) {
237 if (res->ai_addrlen > sizeof(peeraddr))
238 continue;
239 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
240 if (f < 0) {
241 cause = "socket";
242 continue;
243 }
244
245 (void)memset(&ss, 0, sizeof(ss));
246 ss.ss_family = res->ai_family;
247 ss.ss_len = res->ai_addrlen;
248 if (bind(f, (struct sockaddr *)(void *)&ss,
249 (socklen_t)ss.ss_len) < 0) {
250 cause = "bind";
251 (void)close(f);
252 f = -1;
253 continue;
254 }
255
256 break;
257 }
258
259 if (f >= 0) {
260 soopt = 65536;
261 if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &soopt, sizeof(soopt))
262 < 0) {
263 (void)close(f);
264 f = -1;
265 cause = "setsockopt SNDBUF";
266 }
267 else if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt, sizeof(soopt))
268 < 0) {
269 (void)close(f);
270 f = -1;
271 cause = "setsockopt RCVBUF";
272 }
273 }
274
275 if (f < 0 || res == NULL)
276 warn("%s", cause);
277 else {
278 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */
279 (void)memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
280 if (res->ai_canonname) {
281 (void)strlcpy(hostname, res->ai_canonname,
282 sizeof(hostname));
283 } else
284 (void)strlcpy(hostname, host, sizeof(hostname));
285 connected = 1;
286 }
287
288 freeaddrinfo(res0);
289 }
290
291 void
292 setpeer(argc, argv)
293 int argc;
294 char *argv[];
295 {
296
297 if (argc < 2) {
298 (void)strlcpy(line, "Connect ", sizeof(line));
299 (void)printf("(to) ");
300 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
301 makeargv();
302 argc = margc;
303 argv = margv;
304 }
305 if ((argc < 2) || (argc > 3)) {
306 (void)printf("usage: %s [-e] host-name [port]\n", getprogname());
307 return;
308 }
309 if (argc == 2)
310 setpeer0(argv[1], NULL);
311 else
312 setpeer0(argv[1], argv[2]);
313 }
314
315 struct modes {
316 const char *m_name;
317 const char *m_mode;
318 } modes[] = {
319 { "ascii", "netascii" },
320 { "netascii", "netascii" },
321 { "binary", "octet" },
322 { "image", "octet" },
323 { "octet", "octet" },
324 /* { "mail", "mail" }, */
325 { 0, 0 }
326 };
327
328 void
329 modecmd(argc, argv)
330 int argc;
331 char *argv[];
332 {
333 struct modes *p;
334 const char *sep;
335
336 if (argc < 2) {
337 (void)printf("Using %s mode to transfer files.\n", mode);
338 return;
339 }
340 if (argc == 2) {
341 for (p = modes; p->m_name; p++)
342 if (strcmp(argv[1], p->m_name) == 0)
343 break;
344 if (p->m_name) {
345 settftpmode(p->m_mode);
346 return;
347 }
348 (void)printf("%s: unknown mode\n", argv[1]);
349 /* drop through and print usage message */
350 }
351
352 (void)printf("usage: %s [", argv[0]);
353 sep = " ";
354 for (p = modes; p->m_name; p++) {
355 (void)printf("%s%s", sep, p->m_name);
356 if (*sep == ' ')
357 sep = " | ";
358 }
359 (void)printf(" ]\n");
360 return;
361 }
362
363 void
364 /*ARGSUSED*/
365 setbinary(argc, argv)
366 int argc;
367 char *argv[];
368 {
369
370 settftpmode("octet");
371 }
372
373 void
374 /*ARGSUSED*/
375 setascii(argc, argv)
376 int argc;
377 char *argv[];
378 {
379
380 settftpmode("netascii");
381 }
382
383 static void
384 settftpmode(newmode)
385 const char *newmode;
386 {
387 (void)strlcpy(mode, newmode, sizeof(mode));
388 if (verbose)
389 (void)printf("mode set to %s\n", mode);
390 }
391
392
393 /*
394 * Send file(s).
395 */
396 void
397 put(argc, argv)
398 int argc;
399 char *argv[];
400 {
401 int fd;
402 int n;
403 char *targ, *p;
404
405 if (argc < 2) {
406 (void)strlcpy(line, "send ", sizeof(line));
407 (void)printf("(file) ");
408 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
409 makeargv();
410 argc = margc;
411 argv = margv;
412 }
413 if (argc < 2) {
414 putusage(argv[0]);
415 return;
416 }
417 targ = argv[argc - 1];
418 if (strrchr(argv[argc - 1], ':')) {
419 char *cp;
420
421 for (n = 1; n < argc - 1; n++)
422 if (strchr(argv[n], ':')) {
423 putusage(argv[0]);
424 return;
425 }
426 cp = argv[argc - 1];
427 targ = strrchr(cp, ':');
428 *targ++ = 0;
429 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
430 cp[strlen(cp) - 1] = '\0';
431 cp++;
432 }
433 setpeer0(cp, NULL);
434 }
435 if (!connected) {
436 (void)printf("No target machine specified.\n");
437 return;
438 }
439 if (argc < 4) {
440 char *cp = argc == 2 ? tail(targ) : argv[1];
441 fd = open(cp, O_RDONLY);
442 if (fd < 0) {
443 warn("%s", cp);
444 return;
445 }
446 if (verbose)
447 (void)printf("putting %s to %s:%s [%s]\n",
448 cp, hostname, targ, mode);
449 sendfile(fd, targ, mode);
450 return;
451 }
452 /* this assumes the target is a directory */
453 /* on a remote unix system. hmmmm. */
454 p = strchr(targ, '\0');
455 *p++ = '/';
456 for (n = 1; n < argc - 1; n++) {
457 (void)strcpy(p, tail(argv[n]));
458 fd = open(argv[n], O_RDONLY);
459 if (fd < 0) {
460 warn("%s", argv[n]);
461 continue;
462 }
463 if (verbose)
464 (void)printf("putting %s to %s:%s [%s]\n",
465 argv[n], hostname, targ, mode);
466 sendfile(fd, targ, mode);
467 }
468 }
469
470 static void
471 putusage(s)
472 char *s;
473 {
474 (void)printf("usage: %s file ... host:target, or\n", s);
475 (void)printf(" %s file ... target (when already connected)\n", s);
476 }
477
478 /*
479 * Receive file(s).
480 */
481 void
482 get(argc, argv)
483 int argc;
484 char *argv[];
485 {
486 int fd;
487 int n;
488 char *p;
489 char *src;
490
491 if (argc < 2) {
492 (void)strlcpy(line, "get ", sizeof(line));
493 (void)printf("(files) ");
494 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
495 makeargv();
496 argc = margc;
497 argv = margv;
498 }
499 if (argc < 2) {
500 getusage(argv[0]);
501 return;
502 }
503 if (!connected) {
504 for (n = 1; n < argc ; n++)
505 if (strrchr(argv[n], ':') == 0) {
506 getusage(argv[0]);
507 return;
508 }
509 }
510 for (n = 1; n < argc ; n++) {
511 src = strrchr(argv[n], ':');
512 if (src == NULL)
513 src = argv[n];
514 else {
515 char *cp;
516 *src++ = 0;
517 cp = argv[n];
518 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
519 cp[strlen(cp) - 1] = '\0';
520 cp++;
521 }
522 setpeer0(cp, NULL);
523 if (!connected)
524 continue;
525 }
526 if (argc < 4) {
527 char *cp = argc == 3 ? argv[2] : tail(src);
528 fd = creat(cp, 0644);
529 if (fd < 0) {
530 warn("%s", cp);
531 return;
532 }
533 if (verbose)
534 (void)printf("getting from %s:%s to %s [%s]\n",
535 hostname, src, cp, mode);
536 recvfile(fd, src, mode);
537 break;
538 }
539 p = tail(src); /* new .. jdg */
540 fd = creat(p, 0644);
541 if (fd < 0) {
542 warn("%s", p);
543 continue;
544 }
545 if (verbose)
546 (void)printf("getting from %s:%s to %s [%s]\n",
547 hostname, src, p, mode);
548 recvfile(fd, src, mode);
549 }
550 }
551
552 static void
553 getusage(s)
554 char *s;
555 {
556 (void)printf("usage: %s host:file host:file ... file, or\n", s);
557 (void)printf(" %s file file ... file if connected\n", s);
558 }
559
560 void
561 setblksize(argc, argv)
562 int argc;
563 char *argv[];
564 {
565 int t;
566
567 if (argc < 2) {
568 (void)strlcpy(line, "blksize ", sizeof(line));
569 (void)printf("(blksize) ");
570 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
571 makeargv();
572 argc = margc;
573 argv = margv;
574 }
575 if (argc != 2) {
576 (void)printf("usage: %s value\n", argv[0]);
577 return;
578 }
579 t = atoi(argv[1]);
580 if (t < 8 || t > 65464)
581 (void)printf("%s: bad value\n", argv[1]);
582 else
583 blksize = t;
584 }
585
586 unsigned int def_rexmtval = TIMEOUT;
587 unsigned int rexmtval = TIMEOUT;
588
589 void
590 setrexmt(argc, argv)
591 int argc;
592 char *argv[];
593 {
594 int t;
595
596 if (argc < 2) {
597 (void)strlcpy(line, "Rexmt-timeout ", sizeof(line));
598 (void)printf("(value) ");
599 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
600 makeargv();
601 argc = margc;
602 argv = margv;
603 }
604 if (argc != 2) {
605 (void)printf("usage: %s value\n", argv[0]);
606 return;
607 }
608 t = atoi(argv[1]);
609 if (t < 0)
610 (void)printf("%s: bad value\n", argv[1]);
611 else
612 rexmtval = t;
613 }
614
615 int maxtimeout = 5 * TIMEOUT;
616
617 void
618 settimeout(argc, argv)
619 int argc;
620 char *argv[];
621 {
622 int t;
623
624 if (argc < 2) {
625 (void)strlcpy(line, "Maximum-timeout ", sizeof(line));
626 (void)printf("(value) ");
627 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
628 makeargv();
629 argc = margc;
630 argv = margv;
631 }
632 if (argc != 2) {
633 (void)printf("usage: %s value\n", argv[0]);
634 return;
635 }
636 t = atoi(argv[1]);
637 if (t < 0)
638 (void)printf("%s: bad value\n", argv[1]);
639 else
640 maxtimeout = t;
641 }
642
643 void
644 /*ARGSUSED*/
645 status(argc, argv)
646 int argc;
647 char *argv[];
648 {
649 if (connected)
650 (void)printf("Connected to %s.\n", hostname);
651 else
652 (void)printf("Not connected.\n");
653 (void)printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
654 verbose ? "on" : "off", trace ? "on" : "off");
655 (void)printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
656 rexmtval, maxtimeout);
657 }
658
659 void
660 /*ARGSUSED*/
661 intr(dummy)
662 int dummy;
663 {
664
665 (void)signal(SIGALRM, SIG_IGN);
666 (void)alarm(0);
667 longjmp(toplevel, -1);
668 }
669
670 char *
671 tail(filename)
672 char *filename;
673 {
674 char *s;
675
676 while (*filename) {
677 s = strrchr(filename, '/');
678 if (s == NULL)
679 break;
680 if (s[1])
681 return (s + 1);
682 *s = '\0';
683 }
684 return (filename);
685 }
686
687 /*
688 * Command parser.
689 */
690 static __dead void
691 command()
692 {
693 const struct cmd *c;
694
695 for (;;) {
696 (void)printf("%s> ", prompt);
697 if (fgets(line, LBUFLEN, stdin) == 0) {
698 if (feof(stdin)) {
699 exit(0);
700 } else {
701 continue;
702 }
703 }
704 if ((line[0] == 0) || (line[0] == '\n'))
705 continue;
706 makeargv();
707 if (margc == 0)
708 continue;
709 c = getcmd(margv[0]);
710 if (c == (struct cmd *)-1) {
711 (void)printf("?Ambiguous command\n");
712 continue;
713 }
714 if (c == 0) {
715 (void)printf("?Invalid command\n");
716 continue;
717 }
718 (*c->handler)(margc, margv);
719 }
720 }
721
722 const struct cmd *
723 getcmd(name)
724 char *name;
725 {
726 const char *p, *q;
727 const struct cmd *c, *found;
728 int nmatches, longest;
729
730 longest = 0;
731 nmatches = 0;
732 found = 0;
733 for (c = cmdtab; (p = c->name) != NULL; c++) {
734 for (q = name; *q == *p++; q++)
735 if (*q == 0) /* exact match? */
736 return (c);
737 if (!*q) { /* the name was a prefix */
738 if (q - name > longest) {
739 longest = q - name;
740 nmatches = 1;
741 found = c;
742 } else if (q - name == longest)
743 nmatches++;
744 }
745 }
746 if (nmatches > 1)
747 return ((struct cmd *)-1);
748 return (found);
749 }
750
751 /*
752 * Slice a string up into argc/argv.
753 */
754 static void
755 makeargv()
756 {
757 char *cp;
758 char **argp = margv;
759
760 margc = 0;
761 for (cp = line; *cp;) {
762 while (isspace((unsigned char)*cp))
763 cp++;
764 if (*cp == '\0')
765 break;
766 *argp++ = cp;
767 margc += 1;
768 while (*cp != '\0' && !isspace((unsigned char)*cp))
769 cp++;
770 if (*cp == '\0')
771 break;
772 *cp++ = '\0';
773 }
774 *argp++ = 0;
775 }
776
777 void
778 /*ARGSUSED*/
779 quit(argc, argv)
780 int argc;
781 char *argv[];
782 {
783
784 exit(0);
785 }
786
787 /*
788 * Help command.
789 */
790 void
791 help(argc, argv)
792 int argc;
793 char *argv[];
794 {
795 const struct cmd *c;
796
797 if (argc == 1) {
798 (void)printf("Commands may be abbreviated. Commands are:\n\n");
799 for (c = cmdtab; c->name; c++)
800 (void)printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
801 return;
802 }
803 while (--argc > 0) {
804 char *arg;
805 arg = *++argv;
806 c = getcmd(arg);
807 if (c == (struct cmd *)-1)
808 (void)printf("?Ambiguous help command %s\n", arg);
809 else if (c == (struct cmd *)0)
810 (void)printf("?Invalid help command %s\n", arg);
811 else
812 (void)printf("%s\n", c->help);
813 }
814 }
815
816 void
817 /*ARGSUSED*/
818 settrace(argc, argv)
819 int argc;
820 char **argv;
821 {
822 trace = !trace;
823 (void)printf("Packet tracing %s.\n", trace ? "on" : "off");
824 }
825
826 void
827 /*ARGSUSED*/
828 setverbose(argc, argv)
829 int argc;
830 char **argv;
831 {
832 verbose = !verbose;
833 (void)printf("Verbose mode %s.\n", verbose ? "on" : "off");
834 }
835
836 void
837 /*ARGSUSED*/
838 settsize(argc, argv)
839 int argc;
840 char **argv;
841 {
842 tsize = !tsize;
843 (void)printf("Tsize mode %s.\n", tsize ? "on" : "off");
844 }
845
846 void
847 /*ARGSUSED*/
848 settimeoutopt(argc, argv)
849 int argc;
850 char **argv;
851 {
852 tout = !tout;
853 (void)printf("Timeout option %s.\n", tout ? "on" : "off");
854 }
855