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