xref: /netbsd-src/usr.bin/tftp/main.c (revision eb961d0e02b7a46a9acfa877b02df48df6637278)
1 /*	$NetBSD: main.c,v 1.21 2006/01/31 17:36:56 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.21 2006/01/31 17:36:56 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 	strcpy(mode, "netascii");
172 	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 			strcpy(mode, "octet");
180 			tsize = 1;
181 			tout = 1;
182 			break;
183 		default:
184 			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 	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 		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 			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 			close(f);
264 			f = -1;
265 			cause = "setsockopt SNDBUF";
266 		}
267 		if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt, sizeof(soopt))
268 		    < 0) {
269 			close(f);
270 			f = -1;
271 			cause = "setsockopt RCVBUF";
272 		}
273 	}
274 
275 	if (f < 0)
276 		warn("%s", cause);
277 	else {
278 		/* res->ai_addr <= sizeof(peeraddr) is guaranteed */
279 		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 		strcpy(line, "Connect ");
299 		printf("(to) ");
300 		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 		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 		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 		printf("%s: unknown mode\n", argv[1]);
349 		/* drop through and print usage message */
350 	}
351 
352 	printf("usage: %s [", argv[0]);
353 	sep = " ";
354 	for (p = modes; p->m_name; p++) {
355 		printf("%s%s", sep, p->m_name);
356 		if (*sep == ' ')
357 			sep = " | ";
358 	}
359 	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 	strcpy(mode, newmode);
388 	if (verbose)
389 		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 		strcpy(line, "send ");
407 		printf("(file) ");
408 		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 		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 			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 		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 			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 	printf("usage: %s file ... host:target, or\n", s);
475 	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 		strcpy(line, "get ");
493 		printf("(files) ");
494 		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 				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 			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 	printf("usage: %s host:file host:file ... file, or\n", s);
557 	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 		strcpy(line, "blksize ");
569 		printf("(blksize) ");
570 		fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
571 		makeargv();
572 		argc = margc;
573 		argv = margv;
574 	}
575 	if (argc != 2) {
576 		printf("usage: %s value\n", argv[0]);
577 		return;
578 	}
579 	t = atoi(argv[1]);
580 	if (t < 8 || t > 65464)
581 		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 		strcpy(line, "Rexmt-timeout ");
598 		printf("(value) ");
599 		fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
600 		makeargv();
601 		argc = margc;
602 		argv = margv;
603 	}
604 	if (argc != 2) {
605 		printf("usage: %s value\n", argv[0]);
606 		return;
607 	}
608 	t = atoi(argv[1]);
609 	if (t < 0)
610 		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 		strcpy(line, "Maximum-timeout ");
626 		printf("(value) ");
627 		fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
628 		makeargv();
629 		argc = margc;
630 		argv = margv;
631 	}
632 	if (argc != 2) {
633 		printf("usage: %s value\n", argv[0]);
634 		return;
635 	}
636 	t = atoi(argv[1]);
637 	if (t < 0)
638 		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 		printf("Connected to %s.\n", hostname);
651 	else
652 		printf("Not connected.\n");
653 	printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
654 		verbose ? "on" : "off", trace ? "on" : "off");
655 	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 	signal(SIGALRM, SIG_IGN);
666 	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 		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 			printf("?Ambiguous command\n");
712 			continue;
713 		}
714 		if (c == 0) {
715 			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 		printf("Commands may be abbreviated.  Commands are:\n\n");
799 		for (c = cmdtab; c->name; c++)
800 			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 			printf("?Ambiguous help command %s\n", arg);
809 		else if (c == (struct cmd *)0)
810 			printf("?Invalid help command %s\n", arg);
811 		else
812 			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 	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 	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 	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 	printf("Timeout option %s.\n", tout ? "on" : "off");
854 }
855