main.c revision 87708
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1983, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
3487708Smarkm#include <sys/cdefs.h>
3587708Smarkm
3687708Smarkm__FBSDID("$FreeBSD: head/usr.bin/tftp/main.c 87708 2001-12-11 23:43:15Z markm $");
3787708Smarkm
381590Srgrimes#ifndef lint
3928202Scharnierstatic const char copyright[] =
401590Srgrimes"@(#) Copyright (c) 1983, 1993\n\
411590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
4287708Smarkm#endif
431590Srgrimes
441590Srgrimes#ifndef lint
4587708Smarkmstatic const char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
4628202Scharnier#endif
471590Srgrimes
481590Srgrimes/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
491590Srgrimes
501590Srgrimes/*
511590Srgrimes * TFTP User Program -- Command Interface.
521590Srgrimes */
5328202Scharnier#include <sys/param.h>
541590Srgrimes#include <sys/types.h>
551590Srgrimes#include <sys/socket.h>
561590Srgrimes#include <sys/file.h>
5736792Simp#include <sys/param.h>
581590Srgrimes
591590Srgrimes#include <netinet/in.h>
601590Srgrimes
611590Srgrimes#include <arpa/inet.h>
621590Srgrimes
631590Srgrimes#include <ctype.h>
6428202Scharnier#include <err.h>
6585155Sbde#include <histedit.h>
661590Srgrimes#include <netdb.h>
671590Srgrimes#include <setjmp.h>
681590Srgrimes#include <signal.h>
691590Srgrimes#include <stdio.h>
701590Srgrimes#include <stdlib.h>
711590Srgrimes#include <string.h>
721590Srgrimes#include <unistd.h>
731590Srgrimes
741590Srgrimes#include "extern.h"
751590Srgrimes
7685155Sbde#define	MAXLINE		200
771590Srgrimes#define	TIMEOUT		5		/* secs between rexmt's */
781590Srgrimes
791590Srgrimesstruct	sockaddr_in peeraddr;
801590Srgrimesint	f;
811590Srgrimesshort   port;
821590Srgrimesint	trace;
831590Srgrimesint	verbose;
841590Srgrimesint	connected;
851590Srgrimeschar	mode[32];
8685120Smdoddchar	line[MAXLINE];
871590Srgrimesint	margc;
881590Srgrimeschar	*margv[20];
891590Srgrimesjmp_buf	toplevel;
901590Srgrimesstruct	servent *sp;
911590Srgrimes
921590Srgrimesvoid	get __P((int, char **));
931590Srgrimesvoid	help __P((int, char **));
9487708Smarkmvoid	intr __P((int));
951590Srgrimesvoid	modecmd __P((int, char **));
961590Srgrimesvoid	put __P((int, char **));
971590Srgrimesvoid	quit __P((int, char **));
981590Srgrimesvoid	setascii __P((int, char **));
991590Srgrimesvoid	setbinary __P((int, char **));
1001590Srgrimesvoid	setpeer __P((int, char **));
1011590Srgrimesvoid	setrexmt __P((int, char **));
1021590Srgrimesvoid	settimeout __P((int, char **));
1031590Srgrimesvoid	settrace __P((int, char **));
1041590Srgrimesvoid	setverbose __P((int, char **));
1051590Srgrimesvoid	status __P((int, char **));
1061590Srgrimes
10718286Sbdestatic void command __P((void)) __dead2;
10885155Sbdestatic const char *command_prompt __P((void));
1091590Srgrimes
1101590Srgrimesstatic void getusage __P((char *));
1111590Srgrimesstatic void makeargv __P((void));
1121590Srgrimesstatic void putusage __P((char *));
11387708Smarkmstatic void settftpmode __P((const char *));
1141590Srgrimes
11587708Smarkmchar	*tail __P((char *));
11687708Smarkmstruct	cmd *getcmd __P((char *));
11787708Smarkm
1181590Srgrimes#define HELPINDENT (sizeof("connect"))
1191590Srgrimes
1201590Srgrimesstruct cmd {
12187708Smarkm	const char	*name;
1221590Srgrimes	char	*help;
1231590Srgrimes	void	(*handler) __P((int, char **));
1241590Srgrimes};
1251590Srgrimes
1261590Srgrimeschar	vhelp[] = "toggle verbose mode";
1271590Srgrimeschar	thelp[] = "toggle packet tracing";
1281590Srgrimeschar	chelp[] = "connect to remote tftp";
1291590Srgrimeschar	qhelp[] = "exit tftp";
1301590Srgrimeschar	hhelp[] = "print help information";
1311590Srgrimeschar	shelp[] = "send file";
1321590Srgrimeschar	rhelp[] = "receive file";
1331590Srgrimeschar	mhelp[] = "set file transfer mode";
1341590Srgrimeschar	sthelp[] = "show current status";
1351590Srgrimeschar	xhelp[] = "set per-packet retransmission timeout";
1361590Srgrimeschar	ihelp[] = "set total retransmission timeout";
1371590Srgrimeschar    ashelp[] = "set mode to netascii";
1381590Srgrimeschar    bnhelp[] = "set mode to octet";
1391590Srgrimes
1401590Srgrimesstruct cmd cmdtab[] = {
1411590Srgrimes	{ "connect",	chelp,		setpeer },
1421590Srgrimes	{ "mode",       mhelp,          modecmd },
1431590Srgrimes	{ "put",	shelp,		put },
1441590Srgrimes	{ "get",	rhelp,		get },
1451590Srgrimes	{ "quit",	qhelp,		quit },
1461590Srgrimes	{ "verbose",	vhelp,		setverbose },
1471590Srgrimes	{ "trace",	thelp,		settrace },
1481590Srgrimes	{ "status",	sthelp,		status },
1491590Srgrimes	{ "binary",     bnhelp,         setbinary },
1501590Srgrimes	{ "ascii",      ashelp,         setascii },
1511590Srgrimes	{ "rexmt",	xhelp,		setrexmt },
1521590Srgrimes	{ "timeout",	ihelp,		settimeout },
1531590Srgrimes	{ "?",		hhelp,		help },
15487708Smarkm	{ NULL, NULL, NULL }
1551590Srgrimes};
1561590Srgrimes
1571590Srgrimesint
1581590Srgrimesmain(argc, argv)
1591590Srgrimes	int argc;
1601590Srgrimes	char *argv[];
1611590Srgrimes{
16287708Smarkm	struct sockaddr_in lsin;
1631590Srgrimes
1641590Srgrimes	sp = getservbyname("tftp", "udp");
16528202Scharnier	if (sp == 0)
16628202Scharnier		errx(1, "udp/tftp: unknown service");
1671590Srgrimes	f = socket(AF_INET, SOCK_DGRAM, 0);
16828202Scharnier	if (f < 0)
16928202Scharnier		err(3, "socket");
17087708Smarkm	bzero((char *)&lsin, sizeof(lsin));
17187708Smarkm	lsin.sin_family = AF_INET;
17287708Smarkm	if (bind(f, (struct sockaddr *)&lsin, sizeof(lsin)) < 0)
17328202Scharnier		err(1, "bind");
1741590Srgrimes	strcpy(mode, "netascii");
1751590Srgrimes	signal(SIGINT, intr);
1761590Srgrimes	if (argc > 1) {
1771590Srgrimes		if (setjmp(toplevel) != 0)
1781590Srgrimes			exit(0);
1791590Srgrimes		setpeer(argc, argv);
1801590Srgrimes	}
1811590Srgrimes	if (setjmp(toplevel) != 0)
1821590Srgrimes		(void)putchar('\n');
1831590Srgrimes	command();
1841590Srgrimes}
1851590Srgrimes
18628202Scharnierchar    hostname[MAXHOSTNAMELEN];
1871590Srgrimes
1881590Srgrimesvoid
1891590Srgrimessetpeer(argc, argv)
1901590Srgrimes	int argc;
1911590Srgrimes	char *argv[];
1921590Srgrimes{
1931590Srgrimes	struct hostent *host;
1941590Srgrimes
1951590Srgrimes	if (argc < 2) {
1961590Srgrimes		strcpy(line, "Connect ");
1971590Srgrimes		printf("(to) ");
19813068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
1991590Srgrimes		makeargv();
2001590Srgrimes		argc = margc;
2011590Srgrimes		argv = margv;
2021590Srgrimes	}
2031590Srgrimes	if (argc > 3) {
2041590Srgrimes		printf("usage: %s host-name [port]\n", argv[0]);
2051590Srgrimes		return;
2061590Srgrimes	}
2071590Srgrimes	host = gethostbyname(argv[1]);
2081590Srgrimes	if (host) {
2091590Srgrimes		peeraddr.sin_family = host->h_addrtype;
21036792Simp		bcopy(host->h_addr, &peeraddr.sin_addr,
21136792Simp			MIN(sizeof(peeraddr.sin_addr), host->h_length));
21236792Simp		strncpy(hostname, host->h_name, sizeof(hostname));
2131590Srgrimes	} else {
2141590Srgrimes		peeraddr.sin_family = AF_INET;
2151590Srgrimes		peeraddr.sin_addr.s_addr = inet_addr(argv[1]);
2161590Srgrimes		if (peeraddr.sin_addr.s_addr == -1) {
2171590Srgrimes			connected = 0;
2181590Srgrimes			printf("%s: unknown host\n", argv[1]);
2191590Srgrimes			return;
2201590Srgrimes		}
22136792Simp		strncpy(hostname, argv[1], sizeof(hostname));
2221590Srgrimes	}
22336792Simp	hostname[sizeof(hostname) - 1] = '\0';
2241590Srgrimes	port = sp->s_port;
2251590Srgrimes	if (argc == 3) {
2261590Srgrimes		port = atoi(argv[2]);
2271590Srgrimes		if (port < 0) {
2281590Srgrimes			printf("%s: bad port number\n", argv[2]);
2291590Srgrimes			connected = 0;
2301590Srgrimes			return;
2311590Srgrimes		}
2321590Srgrimes		port = htons(port);
2331590Srgrimes	}
2341590Srgrimes	connected = 1;
2351590Srgrimes}
2361590Srgrimes
2371590Srgrimesstruct	modes {
23887708Smarkm	const char *m_name;
23987708Smarkm	const char *m_mode;
2401590Srgrimes} modes[] = {
2411590Srgrimes	{ "ascii",	"netascii" },
2421590Srgrimes	{ "netascii",   "netascii" },
2431590Srgrimes	{ "binary",     "octet" },
2441590Srgrimes	{ "image",      "octet" },
2451590Srgrimes	{ "octet",     "octet" },
2461590Srgrimes/*      { "mail",       "mail" },       */
2471590Srgrimes	{ 0,		0 }
2481590Srgrimes};
2491590Srgrimes
2501590Srgrimesvoid
2511590Srgrimesmodecmd(argc, argv)
2521590Srgrimes	int argc;
2531590Srgrimes	char *argv[];
2541590Srgrimes{
25587708Smarkm	struct modes *p;
25687708Smarkm	const char *sep;
2571590Srgrimes
2581590Srgrimes	if (argc < 2) {
2591590Srgrimes		printf("Using %s mode to transfer files.\n", mode);
2601590Srgrimes		return;
2611590Srgrimes	}
2621590Srgrimes	if (argc == 2) {
2631590Srgrimes		for (p = modes; p->m_name; p++)
2641590Srgrimes			if (strcmp(argv[1], p->m_name) == 0)
2651590Srgrimes				break;
2661590Srgrimes		if (p->m_name) {
2671590Srgrimes			settftpmode(p->m_mode);
2681590Srgrimes			return;
2691590Srgrimes		}
2701590Srgrimes		printf("%s: unknown mode\n", argv[1]);
2711590Srgrimes		/* drop through and print usage message */
2721590Srgrimes	}
2731590Srgrimes
2741590Srgrimes	printf("usage: %s [", argv[0]);
2751590Srgrimes	sep = " ";
2761590Srgrimes	for (p = modes; p->m_name; p++) {
2771590Srgrimes		printf("%s%s", sep, p->m_name);
2781590Srgrimes		if (*sep == ' ')
2791590Srgrimes			sep = " | ";
2801590Srgrimes	}
2811590Srgrimes	printf(" ]\n");
2821590Srgrimes	return;
2831590Srgrimes}
2841590Srgrimes
2851590Srgrimesvoid
2861590Srgrimessetbinary(argc, argv)
28787708Smarkm	int argc __unused;
28887708Smarkm	char *argv[] __unused;
2898874Srgrimes{
2901590Srgrimes
2911590Srgrimes	settftpmode("octet");
2921590Srgrimes}
2931590Srgrimes
2941590Srgrimesvoid
2951590Srgrimessetascii(argc, argv)
29687708Smarkm	int argc __unused;
29787708Smarkm	char *argv[] __unused;
2981590Srgrimes{
2991590Srgrimes
3001590Srgrimes	settftpmode("netascii");
3011590Srgrimes}
3021590Srgrimes
3031590Srgrimesstatic void
3041590Srgrimessettftpmode(newmode)
30587708Smarkm	const char *newmode;
3061590Srgrimes{
3071590Srgrimes	strcpy(mode, newmode);
3081590Srgrimes	if (verbose)
3091590Srgrimes		printf("mode set to %s\n", mode);
3101590Srgrimes}
3111590Srgrimes
3121590Srgrimes
3131590Srgrimes/*
3141590Srgrimes * Send file(s).
3151590Srgrimes */
3161590Srgrimesvoid
3171590Srgrimesput(argc, argv)
3181590Srgrimes	int argc;
3191590Srgrimes	char *argv[];
3201590Srgrimes{
3211590Srgrimes	int fd;
32287708Smarkm	int n;
32387708Smarkm	char *cp, *targ;
3241590Srgrimes
3251590Srgrimes	if (argc < 2) {
3261590Srgrimes		strcpy(line, "send ");
3271590Srgrimes		printf("(file) ");
32813068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
3291590Srgrimes		makeargv();
3301590Srgrimes		argc = margc;
3311590Srgrimes		argv = margv;
3321590Srgrimes	}
3331590Srgrimes	if (argc < 2) {
3341590Srgrimes		putusage(argv[0]);
3351590Srgrimes		return;
3361590Srgrimes	}
3371590Srgrimes	targ = argv[argc - 1];
3381590Srgrimes	if (index(argv[argc - 1], ':')) {
33987708Smarkm		char *lcp;
3401590Srgrimes		struct hostent *hp;
3411590Srgrimes
3421590Srgrimes		for (n = 1; n < argc - 1; n++)
3431590Srgrimes			if (index(argv[n], ':')) {
3441590Srgrimes				putusage(argv[0]);
3451590Srgrimes				return;
3461590Srgrimes			}
34787708Smarkm		lcp = argv[argc - 1];
34887708Smarkm		targ = index(lcp, ':');
3491590Srgrimes		*targ++ = 0;
35087708Smarkm		hp = gethostbyname(lcp);
3511590Srgrimes		if (hp == NULL) {
35287708Smarkm			fprintf(stderr, "tftp: %s: ", lcp);
3531590Srgrimes			herror((char *)NULL);
3541590Srgrimes			return;
3551590Srgrimes		}
35636792Simp		bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
35736792Simp			MIN(sizeof(peeraddr.sin_addr), hp->h_length));
3581590Srgrimes		peeraddr.sin_family = hp->h_addrtype;
3591590Srgrimes		connected = 1;
36036792Simp		strncpy(hostname, hp->h_name, sizeof(hostname));
36136792Simp		hostname[sizeof(hostname) - 1] = '\0';
3621590Srgrimes	}
3631590Srgrimes	if (!connected) {
3641590Srgrimes		printf("No target machine specified.\n");
3651590Srgrimes		return;
3661590Srgrimes	}
3671590Srgrimes	if (argc < 4) {
3681590Srgrimes		cp = argc == 2 ? tail(targ) : argv[1];
3691590Srgrimes		fd = open(cp, O_RDONLY);
3701590Srgrimes		if (fd < 0) {
37128202Scharnier			warn("%s", cp);
3721590Srgrimes			return;
3731590Srgrimes		}
3741590Srgrimes		if (verbose)
3751590Srgrimes			printf("putting %s to %s:%s [%s]\n",
3761590Srgrimes				cp, hostname, targ, mode);
3771590Srgrimes		peeraddr.sin_port = port;
37840765Sdg		xmitfile(fd, targ, mode);
3791590Srgrimes		return;
3801590Srgrimes	}
3811590Srgrimes				/* this assumes the target is a directory */
3821590Srgrimes				/* on a remote unix system.  hmmmm.  */
3838874Srgrimes	cp = index(targ, '\0');
3841590Srgrimes	*cp++ = '/';
3851590Srgrimes	for (n = 1; n < argc - 1; n++) {
3861590Srgrimes		strcpy(cp, tail(argv[n]));
3871590Srgrimes		fd = open(argv[n], O_RDONLY);
3881590Srgrimes		if (fd < 0) {
38928202Scharnier			warn("%s", argv[n]);
3901590Srgrimes			continue;
3911590Srgrimes		}
3921590Srgrimes		if (verbose)
3931590Srgrimes			printf("putting %s to %s:%s [%s]\n",
3941590Srgrimes				argv[n], hostname, targ, mode);
3951590Srgrimes		peeraddr.sin_port = port;
39640765Sdg		xmitfile(fd, targ, mode);
3971590Srgrimes	}
3981590Srgrimes}
3991590Srgrimes
4001590Srgrimesstatic void
4011590Srgrimesputusage(s)
4021590Srgrimes	char *s;
4031590Srgrimes{
4041590Srgrimes	printf("usage: %s file ... host:target, or\n", s);
4051590Srgrimes	printf("       %s file ... target (when already connected)\n", s);
4061590Srgrimes}
4071590Srgrimes
4081590Srgrimes/*
4091590Srgrimes * Receive file(s).
4101590Srgrimes */
4111590Srgrimesvoid
4121590Srgrimesget(argc, argv)
4131590Srgrimes	int argc;
4141590Srgrimes	char *argv[];
4151590Srgrimes{
4161590Srgrimes	int fd;
41787708Smarkm	int n;
41887708Smarkm	char *cp;
4191590Srgrimes	char *src;
4201590Srgrimes
4211590Srgrimes	if (argc < 2) {
4221590Srgrimes		strcpy(line, "get ");
4231590Srgrimes		printf("(files) ");
42413068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
4251590Srgrimes		makeargv();
4261590Srgrimes		argc = margc;
4271590Srgrimes		argv = margv;
4281590Srgrimes	}
4291590Srgrimes	if (argc < 2) {
4301590Srgrimes		getusage(argv[0]);
4311590Srgrimes		return;
4321590Srgrimes	}
4331590Srgrimes	if (!connected) {
4341590Srgrimes		for (n = 1; n < argc ; n++)
4351590Srgrimes			if (index(argv[n], ':') == 0) {
4361590Srgrimes				getusage(argv[0]);
4371590Srgrimes				return;
4381590Srgrimes			}
4391590Srgrimes	}
4401590Srgrimes	for (n = 1; n < argc ; n++) {
4411590Srgrimes		src = index(argv[n], ':');
4421590Srgrimes		if (src == NULL)
4431590Srgrimes			src = argv[n];
4441590Srgrimes		else {
4451590Srgrimes			struct hostent *hp;
4461590Srgrimes
4471590Srgrimes			*src++ = 0;
4481590Srgrimes			hp = gethostbyname(argv[n]);
4491590Srgrimes			if (hp == NULL) {
4501590Srgrimes				fprintf(stderr, "tftp: %s: ", argv[n]);
4511590Srgrimes				herror((char *)NULL);
4521590Srgrimes				continue;
4531590Srgrimes			}
4541590Srgrimes			bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
45536792Simp			    MIN(sizeof(peeraddr.sin_addr), hp->h_length));
4561590Srgrimes			peeraddr.sin_family = hp->h_addrtype;
4571590Srgrimes			connected = 1;
45836792Simp			strncpy(hostname, hp->h_name, sizeof(hostname));
45936792Simp			hostname[sizeof(hostname) - 1] = '\0';
4601590Srgrimes		}
4611590Srgrimes		if (argc < 4) {
4621590Srgrimes			cp = argc == 3 ? argv[2] : tail(src);
4631590Srgrimes			fd = creat(cp, 0644);
4641590Srgrimes			if (fd < 0) {
46528202Scharnier				warn("%s", cp);
4661590Srgrimes				return;
4671590Srgrimes			}
4681590Srgrimes			if (verbose)
4691590Srgrimes				printf("getting from %s:%s to %s [%s]\n",
4701590Srgrimes					hostname, src, cp, mode);
4711590Srgrimes			peeraddr.sin_port = port;
4721590Srgrimes			recvfile(fd, src, mode);
4731590Srgrimes			break;
4741590Srgrimes		}
4751590Srgrimes		cp = tail(src);         /* new .. jdg */
4761590Srgrimes		fd = creat(cp, 0644);
4771590Srgrimes		if (fd < 0) {
47828202Scharnier			warn("%s", cp);
4791590Srgrimes			continue;
4801590Srgrimes		}
4811590Srgrimes		if (verbose)
4821590Srgrimes			printf("getting from %s:%s to %s [%s]\n",
4831590Srgrimes				hostname, src, cp, mode);
4841590Srgrimes		peeraddr.sin_port = port;
4851590Srgrimes		recvfile(fd, src, mode);
4861590Srgrimes	}
4871590Srgrimes}
4881590Srgrimes
4891590Srgrimesstatic void
4901590Srgrimesgetusage(s)
4911590Srgrimes	char *s;
4921590Srgrimes{
4931590Srgrimes	printf("usage: %s host:file host:file ... file, or\n", s);
4941590Srgrimes	printf("       %s file file ... file if connected\n", s);
4951590Srgrimes}
4961590Srgrimes
4971590Srgrimesint	rexmtval = TIMEOUT;
4981590Srgrimes
4991590Srgrimesvoid
5001590Srgrimessetrexmt(argc, argv)
5011590Srgrimes	int argc;
5021590Srgrimes	char *argv[];
5031590Srgrimes{
5041590Srgrimes	int t;
5051590Srgrimes
5061590Srgrimes	if (argc < 2) {
5071590Srgrimes		strcpy(line, "Rexmt-timeout ");
5081590Srgrimes		printf("(value) ");
50913068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
5101590Srgrimes		makeargv();
5111590Srgrimes		argc = margc;
5121590Srgrimes		argv = margv;
5131590Srgrimes	}
5141590Srgrimes	if (argc != 2) {
5151590Srgrimes		printf("usage: %s value\n", argv[0]);
5161590Srgrimes		return;
5171590Srgrimes	}
5181590Srgrimes	t = atoi(argv[1]);
5191590Srgrimes	if (t < 0)
5201590Srgrimes		printf("%s: bad value\n", argv[1]);
5211590Srgrimes	else
5221590Srgrimes		rexmtval = t;
5231590Srgrimes}
5241590Srgrimes
5251590Srgrimesint	maxtimeout = 5 * TIMEOUT;
5261590Srgrimes
5271590Srgrimesvoid
5281590Srgrimessettimeout(argc, argv)
5291590Srgrimes	int argc;
5301590Srgrimes	char *argv[];
5311590Srgrimes{
5321590Srgrimes	int t;
5331590Srgrimes
5341590Srgrimes	if (argc < 2) {
5351590Srgrimes		strcpy(line, "Maximum-timeout ");
5361590Srgrimes		printf("(value) ");
53713068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
5381590Srgrimes		makeargv();
5391590Srgrimes		argc = margc;
5401590Srgrimes		argv = margv;
5411590Srgrimes	}
5421590Srgrimes	if (argc != 2) {
5431590Srgrimes		printf("usage: %s value\n", argv[0]);
5441590Srgrimes		return;
5451590Srgrimes	}
5461590Srgrimes	t = atoi(argv[1]);
5471590Srgrimes	if (t < 0)
5481590Srgrimes		printf("%s: bad value\n", argv[1]);
5491590Srgrimes	else
5501590Srgrimes		maxtimeout = t;
5511590Srgrimes}
5521590Srgrimes
5531590Srgrimesvoid
5541590Srgrimesstatus(argc, argv)
55587708Smarkm	int argc __unused;
55687708Smarkm	char *argv[] __unused;
5571590Srgrimes{
5581590Srgrimes	if (connected)
5591590Srgrimes		printf("Connected to %s.\n", hostname);
5601590Srgrimes	else
5611590Srgrimes		printf("Not connected.\n");
5621590Srgrimes	printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
5631590Srgrimes		verbose ? "on" : "off", trace ? "on" : "off");
5641590Srgrimes	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
5651590Srgrimes		rexmtval, maxtimeout);
5661590Srgrimes}
5671590Srgrimes
5681590Srgrimesvoid
56987708Smarkmintr(dummy)
57087708Smarkm	int dummy __unused;
5711590Srgrimes{
5721590Srgrimes
5731590Srgrimes	signal(SIGALRM, SIG_IGN);
5741590Srgrimes	alarm(0);
5751590Srgrimes	longjmp(toplevel, -1);
5761590Srgrimes}
5771590Srgrimes
5781590Srgrimeschar *
5791590Srgrimestail(filename)
5801590Srgrimes	char *filename;
5811590Srgrimes{
58287708Smarkm	char *s;
5838874Srgrimes
5841590Srgrimes	while (*filename) {
5851590Srgrimes		s = rindex(filename, '/');
5861590Srgrimes		if (s == NULL)
5871590Srgrimes			break;
5881590Srgrimes		if (s[1])
5891590Srgrimes			return (s + 1);
5901590Srgrimes		*s = '\0';
5911590Srgrimes	}
5921590Srgrimes	return (filename);
5931590Srgrimes}
5941590Srgrimes
59585120Smdoddstatic const char *
59685155Sbdecommand_prompt()
59785155Sbde{
59885155Sbde
59985120Smdodd	return ("tftp> ");
60085120Smdodd}
60185120Smdodd
6021590Srgrimes/*
6031590Srgrimes * Command parser.
6041590Srgrimes */
60518286Sbdestatic void
6061590Srgrimescommand()
6071590Srgrimes{
60885155Sbde	HistEvent he;
60987708Smarkm	struct cmd *c;
61085155Sbde	static EditLine *el;
61185155Sbde	static History *hist;
61285155Sbde	const char *bp;
61313068Sjoerg	char *cp;
61487708Smarkm	int len, num, vrbose;
6151590Srgrimes
61687708Smarkm	vrbose = isatty(0);
61787708Smarkm	if (vrbose) {
61885120Smdodd		el = el_init("tftp", stdin, stdout, stderr);
61985120Smdodd		hist = history_init();
62085120Smdodd		history(hist, &he, H_EVENT, 100);
62185120Smdodd		el_set(el, EL_HIST, history, hist);
62285120Smdodd		el_set(el, EL_EDITOR, "emacs");
62385120Smdodd		el_set(el, EL_PROMPT, command_prompt);
62485120Smdodd		el_set(el, EL_SIGNAL, 1);
62585120Smdodd		el_source(el, NULL);
62685120Smdodd	}
6271590Srgrimes	for (;;) {
62887708Smarkm		if (vrbose) {
62985120Smdodd                        if ((bp = el_gets(el, &num)) == NULL || num == 0)
63085120Smdodd                                exit(0);
63185120Smdodd                        len = (num > MAXLINE) ? MAXLINE : num;
63285120Smdodd                        memcpy(line, bp, len);
63385120Smdodd                        line[len] = '\0';
63485120Smdodd                        history(hist, &he, H_ENTER, bp);
63585120Smdodd		} else {
63685120Smdodd			if (fgets(line, sizeof line , stdin) == 0) {
63785120Smdodd				if (feof(stdin)) {
63885120Smdodd					exit(0);
63985120Smdodd				} else {
64085120Smdodd					continue;
64185120Smdodd				}
6421590Srgrimes			}
6431590Srgrimes		}
64413068Sjoerg		if ((cp = strchr(line, '\n')))
64513068Sjoerg			*cp = '\0';
6461590Srgrimes		if (line[0] == 0)
6471590Srgrimes			continue;
6481590Srgrimes		makeargv();
6491590Srgrimes		if (margc == 0)
6501590Srgrimes			continue;
6511590Srgrimes		c = getcmd(margv[0]);
6521590Srgrimes		if (c == (struct cmd *)-1) {
6531590Srgrimes			printf("?Ambiguous command\n");
6541590Srgrimes			continue;
6551590Srgrimes		}
6561590Srgrimes		if (c == 0) {
6571590Srgrimes			printf("?Invalid command\n");
6581590Srgrimes			continue;
6591590Srgrimes		}
6601590Srgrimes		(*c->handler)(margc, margv);
6611590Srgrimes	}
6621590Srgrimes}
6631590Srgrimes
6641590Srgrimesstruct cmd *
6651590Srgrimesgetcmd(name)
66687708Smarkm	char *name;
6671590Srgrimes{
66887708Smarkm	const char *p, *q;
66987708Smarkm	struct cmd *c, *found;
67087708Smarkm	int nmatches, longest;
6711590Srgrimes
6721590Srgrimes	longest = 0;
6731590Srgrimes	nmatches = 0;
6741590Srgrimes	found = 0;
6751590Srgrimes	for (c = cmdtab; (p = c->name) != NULL; c++) {
6761590Srgrimes		for (q = name; *q == *p++; q++)
6771590Srgrimes			if (*q == 0)		/* exact match? */
6781590Srgrimes				return (c);
6791590Srgrimes		if (!*q) {			/* the name was a prefix */
6801590Srgrimes			if (q - name > longest) {
6811590Srgrimes				longest = q - name;
6821590Srgrimes				nmatches = 1;
6831590Srgrimes				found = c;
6841590Srgrimes			} else if (q - name == longest)
6851590Srgrimes				nmatches++;
6861590Srgrimes		}
6871590Srgrimes	}
6881590Srgrimes	if (nmatches > 1)
6891590Srgrimes		return ((struct cmd *)-1);
6901590Srgrimes	return (found);
6911590Srgrimes}
6921590Srgrimes
6931590Srgrimes/*
6941590Srgrimes * Slice a string up into argc/argv.
6951590Srgrimes */
6961590Srgrimesstatic void
6971590Srgrimesmakeargv()
6981590Srgrimes{
69987708Smarkm	char *cp;
70087708Smarkm	char **argp = margv;
7011590Srgrimes
7021590Srgrimes	margc = 0;
70313068Sjoerg	if ((cp = strchr(line, '\n')))
70413068Sjoerg		*cp = '\0';
7051590Srgrimes	for (cp = line; *cp;) {
7061590Srgrimes		while (isspace(*cp))
7071590Srgrimes			cp++;
7081590Srgrimes		if (*cp == '\0')
7091590Srgrimes			break;
7101590Srgrimes		*argp++ = cp;
7111590Srgrimes		margc += 1;
7121590Srgrimes		while (*cp != '\0' && !isspace(*cp))
7131590Srgrimes			cp++;
7141590Srgrimes		if (*cp == '\0')
7151590Srgrimes			break;
7161590Srgrimes		*cp++ = '\0';
7171590Srgrimes	}
7181590Srgrimes	*argp++ = 0;
7191590Srgrimes}
7201590Srgrimes
7211590Srgrimesvoid
7221590Srgrimesquit(argc, argv)
72387708Smarkm	int argc __unused;
72487708Smarkm	char *argv[] __unused;
7251590Srgrimes{
7261590Srgrimes
7271590Srgrimes	exit(0);
7281590Srgrimes}
7291590Srgrimes
7301590Srgrimes/*
7311590Srgrimes * Help command.
7321590Srgrimes */
7331590Srgrimesvoid
7341590Srgrimeshelp(argc, argv)
7351590Srgrimes	int argc;
7361590Srgrimes	char *argv[];
7371590Srgrimes{
73887708Smarkm	struct cmd *c;
7391590Srgrimes
7401590Srgrimes	if (argc == 1) {
7411590Srgrimes		printf("Commands may be abbreviated.  Commands are:\n\n");
7421590Srgrimes		for (c = cmdtab; c->name; c++)
7431590Srgrimes			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
7441590Srgrimes		return;
7451590Srgrimes	}
7461590Srgrimes	while (--argc > 0) {
74787708Smarkm		char *arg;
7481590Srgrimes		arg = *++argv;
7491590Srgrimes		c = getcmd(arg);
7501590Srgrimes		if (c == (struct cmd *)-1)
7511590Srgrimes			printf("?Ambiguous help command %s\n", arg);
7521590Srgrimes		else if (c == (struct cmd *)0)
7531590Srgrimes			printf("?Invalid help command %s\n", arg);
7541590Srgrimes		else
7551590Srgrimes			printf("%s\n", c->help);
7561590Srgrimes	}
7571590Srgrimes}
7581590Srgrimes
7591590Srgrimesvoid
7601590Srgrimessettrace(argc, argv)
76187708Smarkm	int argc __unused;
76287708Smarkm	char **argv __unused;
7631590Srgrimes{
7641590Srgrimes	trace = !trace;
7651590Srgrimes	printf("Packet tracing %s.\n", trace ? "on" : "off");
7661590Srgrimes}
7671590Srgrimes
7681590Srgrimesvoid
7691590Srgrimessetverbose(argc, argv)
77087708Smarkm	int argc __unused;
77187708Smarkm	char **argv __unused;
7721590Srgrimes{
7731590Srgrimes	verbose = !verbose;
7741590Srgrimes	printf("Verbose mode %s.\n", verbose ? "on" : "off");
7751590Srgrimes}
776