xref: /csrg-svn/usr.bin/tftp/main.c (revision 62309)
122403Sdist /*
2*62309Sbostic  * Copyright (c) 1983, 1993
3*62309Sbostic  *	The Regents of the University of California.  All rights reserved.
433821Sbostic  *
542770Sbostic  * %sccs.include.redist.c%
622403Sdist  */
722403Sdist 
814553Ssam #ifndef lint
9*62309Sbostic static char copyright[] =
10*62309Sbostic "@(#) Copyright (c) 1983, 1993\n\
11*62309Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1233821Sbostic #endif /* not lint */
137769Ssam 
1422403Sdist #ifndef lint
15*62309Sbostic static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 06/06/93";
1633821Sbostic #endif /* not lint */
1722403Sdist 
1826095Sminshall /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
1926095Sminshall 
207769Ssam /*
217769Ssam  * TFTP User Program -- Command Interface.
227769Ssam  */
237769Ssam #include <sys/types.h>
247769Ssam #include <sys/socket.h>
2513017Ssam #include <sys/file.h>
269219Ssam 
279219Ssam #include <netinet/in.h>
289219Ssam 
2960062Storek #include <arpa/inet.h>
3060062Storek 
3160062Storek #include <ctype.h>
3260062Storek #include <errno.h>
3360062Storek #include <netdb.h>
3460062Storek #include <setjmp.h>
357769Ssam #include <signal.h>
367769Ssam #include <stdio.h>
3760062Storek #include <stdlib.h>
3860062Storek #include <string.h>
3960062Storek #include <unistd.h>
407769Ssam 
4160062Storek #include "extern.h"
4260062Storek 
4313017Ssam #define	TIMEOUT		5		/* secs between rexmt's */
4413017Ssam 
4560062Storek struct	sockaddr_in peeraddr;
467769Ssam int	f;
4726095Sminshall short   port;
487769Ssam int	trace;
4926095Sminshall int	verbose;
507769Ssam int	connected;
517769Ssam char	mode[32];
527769Ssam char	line[200];
537769Ssam int	margc;
547769Ssam char	*margv[20];
557769Ssam char	*prompt = "tftp";
567769Ssam jmp_buf	toplevel;
5746861Sbostic void	intr();
588384Ssam struct	servent *sp;
597769Ssam 
6060062Storek void	get __P((int, char **));
6160062Storek void	help __P((int, char **));
6260062Storek void	modecmd __P((int, char **));
6360062Storek void	put __P((int, char **));
6460062Storek void	quit __P((int, char **));
6560062Storek void	setascii __P((int, char **));
6660062Storek void	setbinary __P((int, char **));
6760062Storek void	setpeer __P((int, char **));
6860062Storek void	setrexmt __P((int, char **));
6960062Storek void	settimeout __P((int, char **));
7060062Storek void	settrace __P((int, char **));
7160062Storek void	setverbose __P((int, char **));
7260062Storek void	status __P((int, char **));
737769Ssam 
7460062Storek static __dead void command __P((void));
7560062Storek 
7660062Storek static void getusage __P((char *));
7760062Storek static void makeargv __P((void));
7860062Storek static void putusage __P((char *));
7960062Storek static void settftpmode __P((char *));
8060062Storek 
817769Ssam #define HELPINDENT (sizeof("connect"))
827769Ssam 
837769Ssam struct cmd {
847769Ssam 	char	*name;
857769Ssam 	char	*help;
8660062Storek 	void	(*handler) __P((int, char **));
877769Ssam };
887769Ssam 
8926095Sminshall char	vhelp[] = "toggle verbose mode";
907769Ssam char	thelp[] = "toggle packet tracing";
917769Ssam char	chelp[] = "connect to remote tftp";
927769Ssam char	qhelp[] = "exit tftp";
937769Ssam char	hhelp[] = "print help information";
947769Ssam char	shelp[] = "send file";
957769Ssam char	rhelp[] = "receive file";
967769Ssam char	mhelp[] = "set file transfer mode";
977769Ssam char	sthelp[] = "show current status";
9813017Ssam char	xhelp[] = "set per-packet retransmission timeout";
9913017Ssam char	ihelp[] = "set total retransmission timeout";
10026095Sminshall char    ashelp[] = "set mode to netascii";
10126095Sminshall char    bnhelp[] = "set mode to octet";
1027769Ssam 
1037769Ssam struct cmd cmdtab[] = {
1047769Ssam 	{ "connect",	chelp,		setpeer },
10526095Sminshall 	{ "mode",       mhelp,          modecmd },
1067769Ssam 	{ "put",	shelp,		put },
1077769Ssam 	{ "get",	rhelp,		get },
1087769Ssam 	{ "quit",	qhelp,		quit },
10926095Sminshall 	{ "verbose",	vhelp,		setverbose },
1107769Ssam 	{ "trace",	thelp,		settrace },
1117769Ssam 	{ "status",	sthelp,		status },
11226095Sminshall 	{ "binary",     bnhelp,         setbinary },
11326095Sminshall 	{ "ascii",      ashelp,         setascii },
11413017Ssam 	{ "rexmt",	xhelp,		setrexmt },
11513017Ssam 	{ "timeout",	ihelp,		settimeout },
1167769Ssam 	{ "?",		hhelp,		help },
11760062Storek 	{ 0 }
1187769Ssam };
1197769Ssam 
1207769Ssam struct	cmd *getcmd();
1217769Ssam char	*tail();
1227769Ssam char	*index();
1237769Ssam char	*rindex();
1247769Ssam 
12560062Storek int
main(argc,argv)1267769Ssam main(argc, argv)
12760062Storek 	int argc;
1287769Ssam 	char *argv[];
1297769Ssam {
13026095Sminshall 	struct sockaddr_in sin;
13113017Ssam 
1328384Ssam 	sp = getservbyname("tftp", "udp");
1338384Ssam 	if (sp == 0) {
1348384Ssam 		fprintf(stderr, "tftp: udp/tftp: unknown service\n");
1358384Ssam 		exit(1);
1368384Ssam 	}
13726110Sminshall 	f = socket(AF_INET, SOCK_DGRAM, 0);
1387769Ssam 	if (f < 0) {
13913017Ssam 		perror("tftp: socket");
1407769Ssam 		exit(3);
1417769Ssam 	}
14260062Storek 	bzero((char *)&sin, sizeof(sin));
14326095Sminshall 	sin.sin_family = AF_INET;
14460062Storek 	if (bind(f, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
14513017Ssam 		perror("tftp: bind");
14613017Ssam 		exit(1);
14713017Ssam 	}
14826095Sminshall 	strcpy(mode, "netascii");
14913017Ssam 	signal(SIGINT, intr);
1507769Ssam 	if (argc > 1) {
1517769Ssam 		if (setjmp(toplevel) != 0)
1527769Ssam 			exit(0);
1537769Ssam 		setpeer(argc, argv);
1547769Ssam 	}
15560062Storek 	if (setjmp(toplevel) != 0)
15660062Storek 		(void)putchar('\n');
15760062Storek 	command();
1587769Ssam }
1597769Ssam 
16026095Sminshall char    hostname[100];
1617769Ssam 
16260062Storek void
setpeer(argc,argv)1637769Ssam setpeer(argc, argv)
1647769Ssam 	int argc;
1657769Ssam 	char *argv[];
1667769Ssam {
1678384Ssam 	struct hostent *host;
1687769Ssam 
1697769Ssam 	if (argc < 2) {
1707769Ssam 		strcpy(line, "Connect ");
1717769Ssam 		printf("(to) ");
1727769Ssam 		gets(&line[strlen(line)]);
1737769Ssam 		makeargv();
1747769Ssam 		argc = margc;
1757769Ssam 		argv = margv;
1767769Ssam 	}
1777769Ssam 	if (argc > 3) {
1787769Ssam 		printf("usage: %s host-name [port]\n", argv[0]);
1797769Ssam 		return;
1807769Ssam 	}
1818384Ssam 	host = gethostbyname(argv[1]);
1828384Ssam 	if (host) {
18360062Storek 		peeraddr.sin_family = host->h_addrtype;
18460062Storek 		bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
18526095Sminshall 		strcpy(hostname, host->h_name);
1868384Ssam 	} else {
18760062Storek 		peeraddr.sin_family = AF_INET;
18860062Storek 		peeraddr.sin_addr.s_addr = inet_addr(argv[1]);
18960062Storek 		if (peeraddr.sin_addr.s_addr == -1) {
1908384Ssam 			connected = 0;
1918384Ssam 			printf("%s: unknown host\n", argv[1]);
1928384Ssam 			return;
1938384Ssam 		}
19426095Sminshall 		strcpy(hostname, argv[1]);
1957769Ssam 	}
19626095Sminshall 	port = sp->s_port;
1977769Ssam 	if (argc == 3) {
19826095Sminshall 		port = atoi(argv[2]);
19926095Sminshall 		if (port < 0) {
2007769Ssam 			printf("%s: bad port number\n", argv[2]);
2017769Ssam 			connected = 0;
2027769Ssam 			return;
2037769Ssam 		}
20426095Sminshall 		port = htons(port);
2058384Ssam 	}
2067769Ssam 	connected = 1;
2077769Ssam }
2087769Ssam 
2097769Ssam struct	modes {
2107769Ssam 	char *m_name;
2117769Ssam 	char *m_mode;
2127769Ssam } modes[] = {
21326095Sminshall 	{ "ascii",	"netascii" },
21426095Sminshall 	{ "netascii",   "netascii" },
21526095Sminshall 	{ "binary",     "octet" },
21626095Sminshall 	{ "image",      "octet" },
21726103Sminshall 	{ "octet",     "octet" },
21826095Sminshall /*      { "mail",       "mail" },       */
2197769Ssam 	{ 0,		0 }
2207769Ssam };
2217769Ssam 
22260062Storek void
modecmd(argc,argv)22326095Sminshall modecmd(argc, argv)
22460062Storek 	int argc;
2257769Ssam 	char *argv[];
2267769Ssam {
2277769Ssam 	register struct modes *p;
22826095Sminshall 	char *sep;
2297769Ssam 
2307769Ssam 	if (argc < 2) {
2317769Ssam 		printf("Using %s mode to transfer files.\n", mode);
2327769Ssam 		return;
2337769Ssam 	}
23426095Sminshall 	if (argc == 2) {
23526095Sminshall 		for (p = modes; p->m_name; p++)
23626095Sminshall 			if (strcmp(argv[1], p->m_name) == 0)
23726095Sminshall 				break;
23826095Sminshall 		if (p->m_name) {
23960062Storek 			settftpmode(p->m_mode);
24026095Sminshall 			return;
24126095Sminshall 		}
2427769Ssam 		printf("%s: unknown mode\n", argv[1]);
24326095Sminshall 		/* drop through and print usage message */
24426095Sminshall 	}
24526095Sminshall 
24626095Sminshall 	printf("usage: %s [", argv[0]);
24726095Sminshall 	sep = " ";
24826095Sminshall 	for (p = modes; p->m_name; p++) {
24926095Sminshall 		printf("%s%s", sep, p->m_name);
25026095Sminshall 		if (*sep == ' ')
25126095Sminshall 			sep = " | ";
25226095Sminshall 	}
25326095Sminshall 	printf(" ]\n");
25426095Sminshall 	return;
2557769Ssam }
2567769Ssam 
25760062Storek void
setbinary(argc,argv)25826095Sminshall setbinary(argc, argv)
25960062Storek 	int argc;
26060062Storek 	char *argv[];
26160062Storek {
26260062Storek 
26360062Storek 	settftpmode("octet");
26426095Sminshall }
26526095Sminshall 
26660062Storek void
setascii(argc,argv)26726095Sminshall setascii(argc, argv)
26860062Storek 	int argc;
26960062Storek 	char *argv[];
27060062Storek {
27160062Storek 
27260062Storek 	settftpmode("netascii");
27326095Sminshall }
27426095Sminshall 
27560062Storek static void
settftpmode(newmode)27660062Storek settftpmode(newmode)
27760062Storek 	char *newmode;
27826095Sminshall {
27926095Sminshall 	strcpy(mode, newmode);
28026095Sminshall 	if (verbose)
28126095Sminshall 		printf("mode set to %s\n", mode);
28226095Sminshall }
28326095Sminshall 
28426095Sminshall 
2857769Ssam /*
2867769Ssam  * Send file(s).
2877769Ssam  */
28860062Storek void
put(argc,argv)2897769Ssam put(argc, argv)
29060062Storek 	int argc;
2917769Ssam 	char *argv[];
2927769Ssam {
2937769Ssam 	int fd;
29426095Sminshall 	register int n;
2957769Ssam 	register char *cp, *targ;
2967769Ssam 
2977769Ssam 	if (argc < 2) {
2987769Ssam 		strcpy(line, "send ");
2997769Ssam 		printf("(file) ");
3007769Ssam 		gets(&line[strlen(line)]);
3017769Ssam 		makeargv();
3027769Ssam 		argc = margc;
3037769Ssam 		argv = margv;
3047769Ssam 	}
3057769Ssam 	if (argc < 2) {
3067769Ssam 		putusage(argv[0]);
3077769Ssam 		return;
3087769Ssam 	}
3097769Ssam 	targ = argv[argc - 1];
3107769Ssam 	if (index(argv[argc - 1], ':')) {
3118384Ssam 		char *cp;
3128384Ssam 		struct hostent *hp;
3137769Ssam 
3147769Ssam 		for (n = 1; n < argc - 1; n++)
3157769Ssam 			if (index(argv[n], ':')) {
3167769Ssam 				putusage(argv[0]);
3177769Ssam 				return;
3187769Ssam 			}
3198384Ssam 		cp = argv[argc - 1];
3208384Ssam 		targ = index(cp, ':');
3217769Ssam 		*targ++ = 0;
3228384Ssam 		hp = gethostbyname(cp);
32335788Sbostic 		if (hp == NULL) {
32435788Sbostic 			fprintf(stderr, "tftp: %s: ", cp);
32535788Sbostic 			herror((char *)NULL);
3267769Ssam 			return;
3277769Ssam 		}
32860062Storek 		bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length);
32960062Storek 		peeraddr.sin_family = hp->h_addrtype;
3307769Ssam 		connected = 1;
33126095Sminshall 		strcpy(hostname, hp->h_name);
3327769Ssam 	}
3337769Ssam 	if (!connected) {
3347769Ssam 		printf("No target machine specified.\n");
3357769Ssam 		return;
3367769Ssam 	}
3377769Ssam 	if (argc < 4) {
3387769Ssam 		cp = argc == 2 ? tail(targ) : argv[1];
33913017Ssam 		fd = open(cp, O_RDONLY);
3407769Ssam 		if (fd < 0) {
34113017Ssam 			fprintf(stderr, "tftp: "); perror(cp);
3427769Ssam 			return;
3437769Ssam 		}
34426095Sminshall 		if (verbose)
34526095Sminshall 			printf("putting %s to %s:%s [%s]\n",
34626095Sminshall 				cp, hostname, targ, mode);
34760062Storek 		peeraddr.sin_port = port;
34826095Sminshall 		sendfile(fd, targ, mode);
3497769Ssam 		return;
3507769Ssam 	}
35126095Sminshall 				/* this assumes the target is a directory */
35226095Sminshall 				/* on a remote unix system.  hmmmm.  */
3537769Ssam 	cp = index(targ, '\0');
3547769Ssam 	*cp++ = '/';
3557769Ssam 	for (n = 1; n < argc - 1; n++) {
3567769Ssam 		strcpy(cp, tail(argv[n]));
35713017Ssam 		fd = open(argv[n], O_RDONLY);
3587769Ssam 		if (fd < 0) {
35913017Ssam 			fprintf(stderr, "tftp: "); perror(argv[n]);
3607769Ssam 			continue;
3617769Ssam 		}
36226095Sminshall 		if (verbose)
36326095Sminshall 			printf("putting %s to %s:%s [%s]\n",
36426095Sminshall 				argv[n], hostname, targ, mode);
36560062Storek 		peeraddr.sin_port = port;
36626095Sminshall 		sendfile(fd, targ, mode);
3677769Ssam 	}
3687769Ssam }
3697769Ssam 
37060062Storek static void
putusage(s)3717769Ssam putusage(s)
3727769Ssam 	char *s;
3737769Ssam {
3747769Ssam 	printf("usage: %s file ... host:target, or\n", s);
3757769Ssam 	printf("       %s file ... target (when already connected)\n", s);
3767769Ssam }
3777769Ssam 
3787769Ssam /*
3797769Ssam  * Receive file(s).
3807769Ssam  */
38160062Storek void
get(argc,argv)3827769Ssam get(argc, argv)
38360062Storek 	int argc;
3847769Ssam 	char *argv[];
3857769Ssam {
3867769Ssam 	int fd;
38726095Sminshall 	register int n;
3887769Ssam 	register char *cp;
3897769Ssam 	char *src;
3907769Ssam 
3917769Ssam 	if (argc < 2) {
3927769Ssam 		strcpy(line, "get ");
3937769Ssam 		printf("(files) ");
3947769Ssam 		gets(&line[strlen(line)]);
3957769Ssam 		makeargv();
3967769Ssam 		argc = margc;
3977769Ssam 		argv = margv;
3987769Ssam 	}
3997769Ssam 	if (argc < 2) {
4007769Ssam 		getusage(argv[0]);
4017769Ssam 		return;
4027769Ssam 	}
40326095Sminshall 	if (!connected) {
40426095Sminshall 		for (n = 1; n < argc ; n++)
4057769Ssam 			if (index(argv[n], ':') == 0) {
4067769Ssam 				getusage(argv[0]);
4077769Ssam 				return;
4087769Ssam 			}
40926095Sminshall 	}
41026095Sminshall 	for (n = 1; n < argc ; n++) {
4117769Ssam 		src = index(argv[n], ':');
4127769Ssam 		if (src == NULL)
4137769Ssam 			src = argv[n];
4147769Ssam 		else {
4158384Ssam 			struct hostent *hp;
4168384Ssam 
4177769Ssam 			*src++ = 0;
4188384Ssam 			hp = gethostbyname(argv[n]);
41935788Sbostic 			if (hp == NULL) {
42035788Sbostic 				fprintf(stderr, "tftp: %s: ", argv[n]);
42135788Sbostic 				herror((char *)NULL);
4227769Ssam 				continue;
4237769Ssam 			}
42460062Storek 			bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
42560062Storek 			    hp->h_length);
42660062Storek 			peeraddr.sin_family = hp->h_addrtype;
4277769Ssam 			connected = 1;
42826095Sminshall 			strcpy(hostname, hp->h_name);
4297769Ssam 		}
4307769Ssam 		if (argc < 4) {
4317769Ssam 			cp = argc == 3 ? argv[2] : tail(src);
4327769Ssam 			fd = creat(cp, 0644);
4337769Ssam 			if (fd < 0) {
43413017Ssam 				fprintf(stderr, "tftp: "); perror(cp);
4357769Ssam 				return;
4367769Ssam 			}
43726095Sminshall 			if (verbose)
43826095Sminshall 				printf("getting from %s:%s to %s [%s]\n",
43926095Sminshall 					hostname, src, cp, mode);
44060062Storek 			peeraddr.sin_port = port;
44126095Sminshall 			recvfile(fd, src, mode);
4427769Ssam 			break;
4437769Ssam 		}
44426095Sminshall 		cp = tail(src);         /* new .. jdg */
44526095Sminshall 		fd = creat(cp, 0644);
4467769Ssam 		if (fd < 0) {
44726095Sminshall 			fprintf(stderr, "tftp: "); perror(cp);
4487769Ssam 			continue;
4497769Ssam 		}
45026095Sminshall 		if (verbose)
45126095Sminshall 			printf("getting from %s:%s to %s [%s]\n",
45226095Sminshall 				hostname, src, cp, mode);
45360062Storek 		peeraddr.sin_port = port;
45426095Sminshall 		recvfile(fd, src, mode);
4557769Ssam 	}
4567769Ssam }
4577769Ssam 
45860062Storek static void
getusage(s)4597769Ssam getusage(s)
46060062Storek 	char *s;
4617769Ssam {
4627769Ssam 	printf("usage: %s host:file host:file ... file, or\n", s);
4637769Ssam 	printf("       %s file file ... file if connected\n", s);
4647769Ssam }
4657769Ssam 
46613017Ssam int	rexmtval = TIMEOUT;
46713017Ssam 
46860062Storek void
setrexmt(argc,argv)46913017Ssam setrexmt(argc, argv)
47060062Storek 	int argc;
47113017Ssam 	char *argv[];
47213017Ssam {
47313017Ssam 	int t;
47413017Ssam 
47513017Ssam 	if (argc < 2) {
47613017Ssam 		strcpy(line, "Rexmt-timeout ");
47713017Ssam 		printf("(value) ");
47813017Ssam 		gets(&line[strlen(line)]);
47913017Ssam 		makeargv();
48013017Ssam 		argc = margc;
48113017Ssam 		argv = margv;
48213017Ssam 	}
48313017Ssam 	if (argc != 2) {
48413017Ssam 		printf("usage: %s value\n", argv[0]);
48513017Ssam 		return;
48613017Ssam 	}
48713017Ssam 	t = atoi(argv[1]);
48813017Ssam 	if (t < 0)
48960062Storek 		printf("%s: bad value\n", argv[1]);
49013017Ssam 	else
49113017Ssam 		rexmtval = t;
49213017Ssam }
49313017Ssam 
49413017Ssam int	maxtimeout = 5 * TIMEOUT;
49513017Ssam 
49660062Storek void
settimeout(argc,argv)49713017Ssam settimeout(argc, argv)
49860062Storek 	int argc;
49913017Ssam 	char *argv[];
50013017Ssam {
50113017Ssam 	int t;
50213017Ssam 
50313017Ssam 	if (argc < 2) {
50413017Ssam 		strcpy(line, "Maximum-timeout ");
50513017Ssam 		printf("(value) ");
50613017Ssam 		gets(&line[strlen(line)]);
50713017Ssam 		makeargv();
50813017Ssam 		argc = margc;
50913017Ssam 		argv = margv;
51013017Ssam 	}
51113017Ssam 	if (argc != 2) {
51213017Ssam 		printf("usage: %s value\n", argv[0]);
51313017Ssam 		return;
51413017Ssam 	}
51513017Ssam 	t = atoi(argv[1]);
51613017Ssam 	if (t < 0)
51760062Storek 		printf("%s: bad value\n", argv[1]);
51813017Ssam 	else
51913017Ssam 		maxtimeout = t;
52013017Ssam }
52113017Ssam 
52260062Storek void
status(argc,argv)5237769Ssam status(argc, argv)
52460062Storek 	int argc;
5257769Ssam 	char *argv[];
5267769Ssam {
5277769Ssam 	if (connected)
5288384Ssam 		printf("Connected to %s.\n", hostname);
5297769Ssam 	else
5307769Ssam 		printf("Not connected.\n");
53126095Sminshall 	printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
53226095Sminshall 		verbose ? "on" : "off", trace ? "on" : "off");
53313017Ssam 	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
53413017Ssam 		rexmtval, maxtimeout);
5357769Ssam }
5367769Ssam 
53746861Sbostic void
intr()5387769Ssam intr()
5397769Ssam {
54060062Storek 
54126095Sminshall 	signal(SIGALRM, SIG_IGN);
54216382Ssam 	alarm(0);
5437769Ssam 	longjmp(toplevel, -1);
5447769Ssam }
5457769Ssam 
5467769Ssam char *
tail(filename)5477769Ssam tail(filename)
5487769Ssam 	char *filename;
5497769Ssam {
5507769Ssam 	register char *s;
5517769Ssam 
5527769Ssam 	while (*filename) {
5537769Ssam 		s = rindex(filename, '/');
5547769Ssam 		if (s == NULL)
5557769Ssam 			break;
5567769Ssam 		if (s[1])
5577769Ssam 			return (s + 1);
5587769Ssam 		*s = '\0';
5597769Ssam 	}
5607769Ssam 	return (filename);
5617769Ssam }
5627769Ssam 
5637769Ssam /*
5647769Ssam  * Command parser.
5657769Ssam  */
56660062Storek static __dead void
command()56760062Storek command()
5687769Ssam {
5697769Ssam 	register struct cmd *c;
5707769Ssam 
5717769Ssam 	for (;;) {
5727769Ssam 		printf("%s> ", prompt);
57326103Sminshall 		if (gets(line) == 0) {
57426103Sminshall 			if (feof(stdin)) {
57560062Storek 				exit(0);
57626103Sminshall 			} else {
57726103Sminshall 				continue;
57826103Sminshall 			}
57926103Sminshall 		}
5807769Ssam 		if (line[0] == 0)
58113017Ssam 			continue;
5827769Ssam 		makeargv();
58356221Sandrew 		if (margc == 0)
58456221Sandrew 			continue;
5857769Ssam 		c = getcmd(margv[0]);
5867769Ssam 		if (c == (struct cmd *)-1) {
5877769Ssam 			printf("?Ambiguous command\n");
5887769Ssam 			continue;
5897769Ssam 		}
5907769Ssam 		if (c == 0) {
5917769Ssam 			printf("?Invalid command\n");
5927769Ssam 			continue;
5937769Ssam 		}
5947769Ssam 		(*c->handler)(margc, margv);
5957769Ssam 	}
5967769Ssam }
5977769Ssam 
5987769Ssam struct cmd *
getcmd(name)5997769Ssam getcmd(name)
6007769Ssam 	register char *name;
6017769Ssam {
6027769Ssam 	register char *p, *q;
6037769Ssam 	register struct cmd *c, *found;
6047769Ssam 	register int nmatches, longest;
6057769Ssam 
6067769Ssam 	longest = 0;
6077769Ssam 	nmatches = 0;
6087769Ssam 	found = 0;
60960062Storek 	for (c = cmdtab; (p = c->name) != NULL; c++) {
6107769Ssam 		for (q = name; *q == *p++; q++)
6117769Ssam 			if (*q == 0)		/* exact match? */
6127769Ssam 				return (c);
6137769Ssam 		if (!*q) {			/* the name was a prefix */
6147769Ssam 			if (q - name > longest) {
6157769Ssam 				longest = q - name;
6167769Ssam 				nmatches = 1;
6177769Ssam 				found = c;
6187769Ssam 			} else if (q - name == longest)
6197769Ssam 				nmatches++;
6207769Ssam 		}
6217769Ssam 	}
6227769Ssam 	if (nmatches > 1)
6237769Ssam 		return ((struct cmd *)-1);
6247769Ssam 	return (found);
6257769Ssam }
6267769Ssam 
6277769Ssam /*
6287769Ssam  * Slice a string up into argc/argv.
6297769Ssam  */
63060062Storek static void
makeargv()6317769Ssam makeargv()
6327769Ssam {
6337769Ssam 	register char *cp;
6347769Ssam 	register char **argp = margv;
6357769Ssam 
6367769Ssam 	margc = 0;
6377769Ssam 	for (cp = line; *cp;) {
6387769Ssam 		while (isspace(*cp))
6397769Ssam 			cp++;
6407769Ssam 		if (*cp == '\0')
6417769Ssam 			break;
6427769Ssam 		*argp++ = cp;
6437769Ssam 		margc += 1;
6447769Ssam 		while (*cp != '\0' && !isspace(*cp))
6457769Ssam 			cp++;
6467769Ssam 		if (*cp == '\0')
6477769Ssam 			break;
6487769Ssam 		*cp++ = '\0';
6497769Ssam 	}
6507769Ssam 	*argp++ = 0;
6517769Ssam }
6527769Ssam 
65360062Storek void
quit(argc,argv)65460062Storek quit(argc, argv)
65560062Storek 	int argc;
65660062Storek 	char *argv[];
6577769Ssam {
65860062Storek 
6597769Ssam 	exit(0);
6607769Ssam }
6617769Ssam 
6627769Ssam /*
6637769Ssam  * Help command.
6647769Ssam  */
66560062Storek void
help(argc,argv)6667769Ssam help(argc, argv)
6677769Ssam 	int argc;
6687769Ssam 	char *argv[];
6697769Ssam {
6707769Ssam 	register struct cmd *c;
6717769Ssam 
6727769Ssam 	if (argc == 1) {
6737769Ssam 		printf("Commands may be abbreviated.  Commands are:\n\n");
6747769Ssam 		for (c = cmdtab; c->name; c++)
67560062Storek 			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
6767769Ssam 		return;
6777769Ssam 	}
6787769Ssam 	while (--argc > 0) {
6797769Ssam 		register char *arg;
6807769Ssam 		arg = *++argv;
6817769Ssam 		c = getcmd(arg);
6827769Ssam 		if (c == (struct cmd *)-1)
6837769Ssam 			printf("?Ambiguous help command %s\n", arg);
6847769Ssam 		else if (c == (struct cmd *)0)
6857769Ssam 			printf("?Invalid help command %s\n", arg);
6867769Ssam 		else
6877769Ssam 			printf("%s\n", c->help);
6887769Ssam 	}
6897769Ssam }
6907769Ssam 
69160062Storek void
settrace(argc,argv)69260062Storek settrace(argc, argv)
69360062Storek 	int argc;
69460062Storek 	char **argv;
6957769Ssam {
6967769Ssam 	trace = !trace;
6977769Ssam 	printf("Packet tracing %s.\n", trace ? "on" : "off");
6987769Ssam }
69926095Sminshall 
70060062Storek void
setverbose(argc,argv)70160062Storek setverbose(argc, argv)
70260062Storek 	int argc;
70360062Storek 	char **argv;
70426095Sminshall {
70526095Sminshall 	verbose = !verbose;
70626095Sminshall 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
70726095Sminshall }
708