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