1*a2cf252bShe /* $NetBSD: main.c,v 1.32 2012/07/16 09:20:26 he Exp $ */
21bbda10eSjtc
361f28255Scgd /*
41bbda10eSjtc * Copyright (c) 1983, 1993
51bbda10eSjtc * The Regents of the University of California. All rights reserved.
661f28255Scgd *
761f28255Scgd * Redistribution and use in source and binary forms, with or without
861f28255Scgd * modification, are permitted provided that the following conditions
961f28255Scgd * are met:
1061f28255Scgd * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd * notice, this list of conditions and the following disclaimer.
1261f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd * notice, this list of conditions and the following disclaimer in the
1461f28255Scgd * documentation and/or other materials provided with the distribution.
1589aaa1bbSagc * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd * may be used to endorse or promote products derived from this software
1761f28255Scgd * without specific prior written permission.
1861f28255Scgd *
1961f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd * SUCH DAMAGE.
3061f28255Scgd */
3161f28255Scgd
3211eb9f8eSmrg #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3498e5374cSlukem __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
3598e5374cSlukem The Regents of the University of California. All rights reserved.");
361bbda10eSjtc #if 0
371bbda10eSjtc static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
3811eb9f8eSmrg #else
39*a2cf252bShe __RCSID("$NetBSD: main.c,v 1.32 2012/07/16 09:20:26 he Exp $");
401bbda10eSjtc #endif
4161f28255Scgd #endif /* not lint */
4261f28255Scgd
4361f28255Scgd /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
4461f28255Scgd
4561f28255Scgd /*
4661f28255Scgd * TFTP User Program -- Command Interface.
4761f28255Scgd */
4861f28255Scgd #include <sys/types.h>
4945afe098Schristos #include <sys/param.h>
5061f28255Scgd #include <sys/socket.h>
5161f28255Scgd
5261f28255Scgd #include <netinet/in.h>
5361f28255Scgd
541bbda10eSjtc #include <arpa/inet.h>
5544411286Sbriggs #include <arpa/tftp.h>
561bbda10eSjtc
571bbda10eSjtc #include <ctype.h>
58e42e202cSchristos #include <fcntl.h>
590471adfdSlukem #include <err.h>
601bbda10eSjtc #include <errno.h>
611bbda10eSjtc #include <netdb.h>
621bbda10eSjtc #include <setjmp.h>
6361f28255Scgd #include <signal.h>
6461f28255Scgd #include <stdio.h>
651bbda10eSjtc #include <stdlib.h>
661bbda10eSjtc #include <string.h>
671bbda10eSjtc #include <unistd.h>
681bbda10eSjtc
691bbda10eSjtc #include "extern.h"
7061f28255Scgd
7161f28255Scgd #define TIMEOUT 5 /* secs between rexmt's */
7265b04c1fScgd #define LBUFLEN 200 /* size of input buffer */
7361f28255Scgd
74a40e2f28Sitojun struct sockaddr_storage peeraddr;
7561f28255Scgd int f;
7622c701ecSjmcneill int mf;
7761f28255Scgd int trace;
7861f28255Scgd int verbose;
7944411286Sbriggs int tsize=0;
8044411286Sbriggs int tout=0;
81643cfdb6Schristos size_t def_blksize = SEGSIZE;
82643cfdb6Schristos size_t blksize = SEGSIZE;
8322c701ecSjmcneill in_addr_t mcaddr = INADDR_NONE;
8422c701ecSjmcneill uint16_t mcport;
8545afe098Schristos u_int def_rexmtval = TIMEOUT;
8645afe098Schristos u_int rexmtval = TIMEOUT;
8722c701ecSjmcneill ushort mcmasterslave;
8845afe098Schristos int maxtimeout = 5 * TIMEOUT;
8961f28255Scgd
90*a2cf252bShe jmp_buf toplevel;
91*a2cf252bShe
9245afe098Schristos static int connected;
9345afe098Schristos static char mode[32];
9445afe098Schristos static char line[LBUFLEN];
9545afe098Schristos static int margc;
9645afe098Schristos static char *margv[20];
9745afe098Schristos static const char *prompt = "tftp";
9845afe098Schristos static char hostname[MAXHOSTNAMELEN];
991bbda10eSjtc
10045afe098Schristos static void get(int, char **);
10145afe098Schristos static void help(int, char **);
10245afe098Schristos static void modecmd(int, char **);
10345afe098Schristos static void put(int, char **);
10445afe098Schristos static __dead void quit(int, char **);
10545afe098Schristos static void setascii(int, char **);
10645afe098Schristos static void setbinary(int, char **);
10745afe098Schristos static void setpeer0(const char *, const char *);
10845afe098Schristos static void setpeer(int, char **);
10945afe098Schristos static void setrexmt(int, char **);
11045afe098Schristos static void settimeout(int, char **);
11145afe098Schristos static void settrace(int, char **);
11245afe098Schristos static void setverbose(int, char **);
11345afe098Schristos static void setblksize(int, char **);
11445afe098Schristos static void settsize(int, char **);
11545afe098Schristos static void settimeoutopt(int, char **);
11645afe098Schristos static void status(int, char **);
11745afe098Schristos static char *tail(char *);
11845afe098Schristos static __dead void intr(int);
11945afe098Schristos static const struct cmd *getcmd(const char *);
1201bbda10eSjtc
12145afe098Schristos static __dead void command(void);
12261f28255Scgd
12345afe098Schristos static void getUsage(char *);
12445afe098Schristos static void makeargv(void);
12545afe098Schristos static void putUsage(const char *);
12645afe098Schristos static void settftpmode(const char *);
12745afe098Schristos
12845afe098Schristos #define HELPINDENT sizeof("connect")
12961f28255Scgd
13061f28255Scgd struct cmd {
131b4995580Sross const char *name;
132b4995580Sross const char *help;
13345afe098Schristos void (*handler)(int, char **);
13461f28255Scgd };
13561f28255Scgd
13645afe098Schristos static const char vhelp[] = "toggle verbose mode";
13745afe098Schristos static const char thelp[] = "toggle packet tracing";
13845afe098Schristos static const char tshelp[] = "toggle extended tsize option";
13945afe098Schristos static const char tohelp[] = "toggle extended timeout option";
14045afe098Schristos static const char blhelp[] = "set an alternative blocksize (def. 512)";
14145afe098Schristos static const char chelp[] = "connect to remote tftp";
14245afe098Schristos static const char qhelp[] = "exit tftp";
14345afe098Schristos static const char hhelp[] = "print help information";
14445afe098Schristos static const char shelp[] = "send file";
14545afe098Schristos static const char rhelp[] = "receive file";
14645afe098Schristos static const char mhelp[] = "set file transfer mode";
14745afe098Schristos static const char sthelp[] = "show current status";
14845afe098Schristos static const char xhelp[] = "set per-packet retransmission timeout";
14945afe098Schristos static const char ihelp[] = "set total retransmission timeout";
15045afe098Schristos static const char ashelp[] = "set mode to netascii";
15145afe098Schristos static const char bnhelp[] = "set mode to octet";
15261f28255Scgd
15345afe098Schristos static const struct cmd cmdtab[] = {
15461f28255Scgd { "connect", chelp, setpeer },
15561f28255Scgd { "mode", mhelp, modecmd },
15661f28255Scgd { "put", shelp, put },
15761f28255Scgd { "get", rhelp, get },
15861f28255Scgd { "quit", qhelp, quit },
15961f28255Scgd { "verbose", vhelp, setverbose },
16044411286Sbriggs { "blksize", blhelp, setblksize },
16144411286Sbriggs { "tsize", tshelp, settsize },
16261f28255Scgd { "trace", thelp, settrace },
16361f28255Scgd { "status", sthelp, status },
16461f28255Scgd { "binary", bnhelp, setbinary },
16561f28255Scgd { "ascii", ashelp, setascii },
16661f28255Scgd { "rexmt", xhelp, setrexmt },
16761f28255Scgd { "timeout", ihelp, settimeout },
16844411286Sbriggs { "tout", tohelp, settimeoutopt },
16961f28255Scgd { "?", hhelp, help },
17088bb03c9Schristos { .name = NULL }
17161f28255Scgd };
17261f28255Scgd
17345afe098Schristos static struct modes {
17445afe098Schristos const char *m_name;
17545afe098Schristos const char *m_mode;
17645afe098Schristos } modes[] = {
17745afe098Schristos { "ascii", "netascii" },
17845afe098Schristos { "netascii", "netascii" },
17945afe098Schristos { "binary", "octet" },
18045afe098Schristos { "image", "octet" },
18145afe098Schristos { "octet", "octet" },
18245afe098Schristos /* { "mail", "mail" }, */
18345afe098Schristos { 0, 0 }
18445afe098Schristos };
18545afe098Schristos
1861bbda10eSjtc int
main(int argc,char * argv[])18745afe098Schristos main(int argc, char *argv[])
18861f28255Scgd {
18944411286Sbriggs int c;
19044411286Sbriggs
19122c701ecSjmcneill f = mf = -1;
1929d020319Schristos (void)strlcpy(mode, "netascii", sizeof(mode));
1939d020319Schristos (void)signal(SIGINT, intr);
19444411286Sbriggs
19544411286Sbriggs setprogname(argv[0]);
19644411286Sbriggs while ((c = getopt(argc, argv, "e")) != -1) {
19744411286Sbriggs switch (c) {
19844411286Sbriggs case 'e':
19944411286Sbriggs blksize = MAXSEGSIZE;
2009d020319Schristos (void)strlcpy(mode, "octet", sizeof(mode));
20144411286Sbriggs tsize = 1;
20244411286Sbriggs tout = 1;
20344411286Sbriggs break;
20444411286Sbriggs default:
20545afe098Schristos (void)fprintf(stderr,
20645afe098Schristos "Usage: %s [-e] host-name [port]\n", getprogname());
20744411286Sbriggs exit(1);
20844411286Sbriggs }
20944411286Sbriggs }
21044411286Sbriggs argc -= optind;
21144411286Sbriggs argv += optind;
21244411286Sbriggs
21344411286Sbriggs if (argc >= 1) {
21461f28255Scgd if (setjmp(toplevel) != 0)
21561f28255Scgd exit(0);
2168af68875Sitojun argc++;
2178af68875Sitojun argv--;
21861f28255Scgd setpeer(argc, argv);
21961f28255Scgd }
2201bbda10eSjtc if (setjmp(toplevel) != 0)
2211bbda10eSjtc (void)putchar('\n');
2221bbda10eSjtc command();
22345afe098Schristos return 0;
22461f28255Scgd }
22561f28255Scgd
22645afe098Schristos static void
getmore(const char * cmd,const char * prm)22745afe098Schristos getmore(const char *cmd, const char *prm)
22845afe098Schristos {
22945afe098Schristos (void)strlcpy(line, cmd, sizeof(line));
23045afe098Schristos (void)printf("%s", prm);
23145afe098Schristos (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
23245afe098Schristos makeargv();
23345afe098Schristos }
23461f28255Scgd
23545afe098Schristos static void
setpeer0(const char * host,const char * port)23645afe098Schristos setpeer0(const char *host, const char *port)
237a40e2f28Sitojun {
238a40e2f28Sitojun struct addrinfo hints, *res0, *res;
23944411286Sbriggs int error, soopt;
240a40e2f28Sitojun struct sockaddr_storage ss;
241b4995580Sross const char *cause = "unknown";
242a40e2f28Sitojun
243a40e2f28Sitojun if (connected) {
244643cfdb6Schristos (void)close(f);
245a40e2f28Sitojun f = -1;
246a40e2f28Sitojun }
2479b9f995dSitojun connected = 0;
248a40e2f28Sitojun
2499d020319Schristos (void)memset(&hints, 0, sizeof(hints));
250a40e2f28Sitojun hints.ai_family = PF_UNSPEC;
251a40e2f28Sitojun hints.ai_socktype = SOCK_DGRAM;
252a40e2f28Sitojun hints.ai_protocol = IPPROTO_UDP;
253a40e2f28Sitojun hints.ai_flags = AI_CANONNAME;
254a40e2f28Sitojun if (!port)
255a40e2f28Sitojun port = "tftp";
256a40e2f28Sitojun error = getaddrinfo(host, port, &hints, &res0);
257a40e2f28Sitojun if (error) {
258a40e2f28Sitojun warnx("%s", gai_strerror(error));
259a40e2f28Sitojun return;
260a40e2f28Sitojun }
261a40e2f28Sitojun
262a40e2f28Sitojun for (res = res0; res; res = res->ai_next) {
26344442755Sitojun if (res->ai_addrlen > sizeof(peeraddr))
26444442755Sitojun continue;
265a40e2f28Sitojun f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
26645afe098Schristos if (f == -1) {
267a40e2f28Sitojun cause = "socket";
268a40e2f28Sitojun continue;
269a40e2f28Sitojun }
270a40e2f28Sitojun
2719d020319Schristos (void)memset(&ss, 0, sizeof(ss));
272a40e2f28Sitojun ss.ss_family = res->ai_family;
273a40e2f28Sitojun ss.ss_len = res->ai_addrlen;
274643cfdb6Schristos if (bind(f, (struct sockaddr *)(void *)&ss,
27545afe098Schristos (socklen_t)ss.ss_len) == -1) {
276a40e2f28Sitojun cause = "bind";
2779d020319Schristos (void)close(f);
278a40e2f28Sitojun f = -1;
279a40e2f28Sitojun continue;
280a40e2f28Sitojun }
281a40e2f28Sitojun
282a40e2f28Sitojun break;
283a40e2f28Sitojun }
284a40e2f28Sitojun
28544411286Sbriggs if (f >= 0) {
28644411286Sbriggs soopt = 65536;
28744411286Sbriggs if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &soopt, sizeof(soopt))
28845afe098Schristos == -1) {
2899d020319Schristos (void)close(f);
29044411286Sbriggs f = -1;
29144411286Sbriggs cause = "setsockopt SNDBUF";
29244411286Sbriggs }
29345afe098Schristos else if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt,
29445afe098Schristos sizeof(soopt)) == -1) {
2959d020319Schristos (void)close(f);
29644411286Sbriggs f = -1;
29744411286Sbriggs cause = "setsockopt RCVBUF";
29844411286Sbriggs }
29944411286Sbriggs }
30044411286Sbriggs
30145afe098Schristos if (f == -1 || res == NULL)
302a40e2f28Sitojun warn("%s", cause);
303a40e2f28Sitojun else {
30444442755Sitojun /* res->ai_addr <= sizeof(peeraddr) is guaranteed */
3059d020319Schristos (void)memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
306a40e2f28Sitojun if (res->ai_canonname) {
307417386ecSitojun (void)strlcpy(hostname, res->ai_canonname,
308a40e2f28Sitojun sizeof(hostname));
309a40e2f28Sitojun } else
310417386ecSitojun (void)strlcpy(hostname, host, sizeof(hostname));
311a40e2f28Sitojun connected = 1;
312a40e2f28Sitojun }
3139b9f995dSitojun
3149b9f995dSitojun freeaddrinfo(res0);
315a40e2f28Sitojun }
316a40e2f28Sitojun
31745afe098Schristos static void
setpeer(int argc,char * argv[])31845afe098Schristos setpeer(int argc, char *argv[])
31961f28255Scgd {
32061f28255Scgd
3218cf5cbaeSitojun if (argc < 2) {
32245afe098Schristos getmore("Connect ", "(to) ");
32361f28255Scgd argc = margc;
32461f28255Scgd argv = margv;
32561f28255Scgd }
32645afe098Schristos if (argc < 2 || argc > 3) {
32745afe098Schristos (void)printf("Usage: %s [-e] host-name [port]\n",
32845afe098Schristos getprogname());
32961f28255Scgd return;
33061f28255Scgd }
3318cf5cbaeSitojun if (argc == 2)
3328cf5cbaeSitojun setpeer0(argv[1], NULL);
333a40e2f28Sitojun else
3348cf5cbaeSitojun setpeer0(argv[1], argv[2]);
33561f28255Scgd }
33661f28255Scgd
33745afe098Schristos static void
modecmd(int argc,char * argv[])33845afe098Schristos modecmd(int argc, char *argv[])
33961f28255Scgd {
3400471adfdSlukem struct modes *p;
341b4995580Sross const char *sep;
34261f28255Scgd
34361f28255Scgd if (argc < 2) {
3449d020319Schristos (void)printf("Using %s mode to transfer files.\n", mode);
34561f28255Scgd return;
34661f28255Scgd }
34761f28255Scgd if (argc == 2) {
34861f28255Scgd for (p = modes; p->m_name; p++)
34961f28255Scgd if (strcmp(argv[1], p->m_name) == 0)
35061f28255Scgd break;
35161f28255Scgd if (p->m_name) {
3521bbda10eSjtc settftpmode(p->m_mode);
35361f28255Scgd return;
35461f28255Scgd }
3559d020319Schristos (void)printf("%s: unknown mode\n", argv[1]);
35645afe098Schristos /* drop through and print Usage message */
35761f28255Scgd }
35861f28255Scgd
35945afe098Schristos (void)printf("Usage: %s [", argv[0]);
36061f28255Scgd sep = " ";
36161f28255Scgd for (p = modes; p->m_name; p++) {
3629d020319Schristos (void)printf("%s%s", sep, p->m_name);
36361f28255Scgd if (*sep == ' ')
36461f28255Scgd sep = " | ";
36561f28255Scgd }
3669d020319Schristos (void)printf(" ]\n");
36761f28255Scgd return;
36861f28255Scgd }
36961f28255Scgd
37045afe098Schristos static void
371643cfdb6Schristos /*ARGSUSED*/
setbinary(int argc,char * argv[])37245afe098Schristos setbinary(int argc, char *argv[])
3731bbda10eSjtc {
3741bbda10eSjtc
3751bbda10eSjtc settftpmode("octet");
37661f28255Scgd }
37761f28255Scgd
37845afe098Schristos static void
379643cfdb6Schristos /*ARGSUSED*/
setascii(int argc,char * argv[])38045afe098Schristos setascii(int argc, char *argv[])
3811bbda10eSjtc {
3821bbda10eSjtc
3831bbda10eSjtc settftpmode("netascii");
38461f28255Scgd }
38561f28255Scgd
3861bbda10eSjtc static void
settftpmode(const char * newmode)38745afe098Schristos settftpmode(const char *newmode)
38861f28255Scgd {
3899d020319Schristos (void)strlcpy(mode, newmode, sizeof(mode));
39061f28255Scgd if (verbose)
3919d020319Schristos (void)printf("mode set to %s\n", mode);
39261f28255Scgd }
39361f28255Scgd
39461f28255Scgd
39561f28255Scgd /*
39661f28255Scgd * Send file(s).
39761f28255Scgd */
39845afe098Schristos static void
put(int argc,char * argv[])39945afe098Schristos put(int argc, char *argv[])
40061f28255Scgd {
40161f28255Scgd int fd;
4020471adfdSlukem int n;
403b4995580Sross char *targ, *p;
40461f28255Scgd
40561f28255Scgd if (argc < 2) {
40645afe098Schristos getmore("send ", "(file) ");
40761f28255Scgd argc = margc;
40861f28255Scgd argv = margv;
40961f28255Scgd }
41061f28255Scgd if (argc < 2) {
41145afe098Schristos putUsage(argv[0]);
41261f28255Scgd return;
41361f28255Scgd }
41461f28255Scgd targ = argv[argc - 1];
415a40e2f28Sitojun if (strrchr(argv[argc - 1], ':')) {
41661f28255Scgd char *cp;
41761f28255Scgd
41861f28255Scgd for (n = 1; n < argc - 1; n++)
4190471adfdSlukem if (strchr(argv[n], ':')) {
42045afe098Schristos putUsage(argv[0]);
42161f28255Scgd return;
42261f28255Scgd }
42361f28255Scgd cp = argv[argc - 1];
424a40e2f28Sitojun targ = strrchr(cp, ':');
42561f28255Scgd *targ++ = 0;
426a40e2f28Sitojun if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
427a40e2f28Sitojun cp[strlen(cp) - 1] = '\0';
428a40e2f28Sitojun cp++;
42961f28255Scgd }
430a40e2f28Sitojun setpeer0(cp, NULL);
43161f28255Scgd }
43261f28255Scgd if (!connected) {
4339d020319Schristos (void)printf("No target machine specified.\n");
43461f28255Scgd return;
43561f28255Scgd }
43661f28255Scgd if (argc < 4) {
437b4995580Sross char *cp = argc == 2 ? tail(targ) : argv[1];
43861f28255Scgd fd = open(cp, O_RDONLY);
43945afe098Schristos if (fd == -1) {
4400471adfdSlukem warn("%s", cp);
44161f28255Scgd return;
44261f28255Scgd }
44361f28255Scgd if (verbose)
4449d020319Schristos (void)printf("putting %s to %s:%s [%s]\n",
44561f28255Scgd cp, hostname, targ, mode);
44661f28255Scgd sendfile(fd, targ, mode);
44761f28255Scgd return;
44861f28255Scgd }
44961f28255Scgd /* this assumes the target is a directory */
45061f28255Scgd /* on a remote unix system. hmmmm. */
451b4995580Sross p = strchr(targ, '\0');
452b4995580Sross *p++ = '/';
45361f28255Scgd for (n = 1; n < argc - 1; n++) {
4549d020319Schristos (void)strcpy(p, tail(argv[n]));
45561f28255Scgd fd = open(argv[n], O_RDONLY);
45645afe098Schristos if (fd == -1) {
4570471adfdSlukem warn("%s", argv[n]);
45861f28255Scgd continue;
45961f28255Scgd }
46061f28255Scgd if (verbose)
4619d020319Schristos (void)printf("putting %s to %s:%s [%s]\n",
46261f28255Scgd argv[n], hostname, targ, mode);
46361f28255Scgd sendfile(fd, targ, mode);
46461f28255Scgd }
46561f28255Scgd }
46661f28255Scgd
4671bbda10eSjtc static void
putUsage(const char * s)46845afe098Schristos putUsage(const char *s)
46961f28255Scgd {
47045afe098Schristos (void)printf("Usage: %s file ... host:target, or\n", s);
4719d020319Schristos (void)printf(" %s file ... target (when already connected)\n", s);
47261f28255Scgd }
47361f28255Scgd
47461f28255Scgd /*
47561f28255Scgd * Receive file(s).
47661f28255Scgd */
47745afe098Schristos static void
get(int argc,char * argv[])47845afe098Schristos get(int argc, char *argv[])
47961f28255Scgd {
48061f28255Scgd int fd;
4810471adfdSlukem int n;
482b4995580Sross char *p;
48361f28255Scgd char *src;
48461f28255Scgd
48561f28255Scgd if (argc < 2) {
48645afe098Schristos getmore("get ", "(files) ");
48761f28255Scgd argc = margc;
48861f28255Scgd argv = margv;
48961f28255Scgd }
49061f28255Scgd if (argc < 2) {
49145afe098Schristos getUsage(argv[0]);
49261f28255Scgd return;
49361f28255Scgd }
49461f28255Scgd if (!connected) {
49561f28255Scgd for (n = 1; n < argc ; n++)
496a40e2f28Sitojun if (strrchr(argv[n], ':') == 0) {
49745afe098Schristos getUsage(argv[0]);
49861f28255Scgd return;
49961f28255Scgd }
50061f28255Scgd }
50161f28255Scgd for (n = 1; n < argc ; n++) {
502a40e2f28Sitojun src = strrchr(argv[n], ':');
50361f28255Scgd if (src == NULL)
50461f28255Scgd src = argv[n];
50561f28255Scgd else {
506a40e2f28Sitojun char *cp;
50761f28255Scgd *src++ = 0;
508a40e2f28Sitojun cp = argv[n];
509a40e2f28Sitojun if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
510a40e2f28Sitojun cp[strlen(cp) - 1] = '\0';
511a40e2f28Sitojun cp++;
51261f28255Scgd }
513a40e2f28Sitojun setpeer0(cp, NULL);
514a40e2f28Sitojun if (!connected)
515a40e2f28Sitojun continue;
51661f28255Scgd }
51761f28255Scgd if (argc < 4) {
518b4995580Sross char *cp = argc == 3 ? argv[2] : tail(src);
51961f28255Scgd fd = creat(cp, 0644);
52045afe098Schristos if (fd == -1) {
5210471adfdSlukem warn("%s", cp);
52261f28255Scgd return;
52361f28255Scgd }
52461f28255Scgd if (verbose)
5259d020319Schristos (void)printf("getting from %s:%s to %s [%s]\n",
52661f28255Scgd hostname, src, cp, mode);
52761f28255Scgd recvfile(fd, src, mode);
52861f28255Scgd break;
52961f28255Scgd }
530b4995580Sross p = tail(src); /* new .. jdg */
531b4995580Sross fd = creat(p, 0644);
53245afe098Schristos if (fd == -1) {
533b4995580Sross warn("%s", p);
53461f28255Scgd continue;
53561f28255Scgd }
53661f28255Scgd if (verbose)
5379d020319Schristos (void)printf("getting from %s:%s to %s [%s]\n",
538b4995580Sross hostname, src, p, mode);
53961f28255Scgd recvfile(fd, src, mode);
54061f28255Scgd }
54161f28255Scgd }
54261f28255Scgd
5431bbda10eSjtc static void
getUsage(char * s)544d34c2845Smatt getUsage(char *s)
54561f28255Scgd {
54645afe098Schristos (void)printf("Usage: %s host:file host:file ... file, or\n", s);
5479d020319Schristos (void)printf(" %s file file ... file if connected\n", s);
54861f28255Scgd }
54961f28255Scgd
55044411286Sbriggs void
setblksize(int argc,char * argv[])551d34c2845Smatt setblksize(int argc, char *argv[])
55244411286Sbriggs {
55344411286Sbriggs int t;
55444411286Sbriggs
55544411286Sbriggs if (argc < 2) {
55645afe098Schristos getmore("blksize ", "(blksize) ");
55744411286Sbriggs argc = margc;
55844411286Sbriggs argv = margv;
55944411286Sbriggs }
56044411286Sbriggs if (argc != 2) {
56145afe098Schristos (void)printf("Usage: %s value\n", argv[0]);
56244411286Sbriggs return;
56344411286Sbriggs }
56444411286Sbriggs t = atoi(argv[1]);
56544411286Sbriggs if (t < 8 || t > 65464)
5669d020319Schristos (void)printf("%s: bad value\n", argv[1]);
56744411286Sbriggs else
56844411286Sbriggs blksize = t;
56944411286Sbriggs }
57044411286Sbriggs
57145afe098Schristos static void
setrexmt(int argc,char * argv[])57245afe098Schristos setrexmt(int argc, char *argv[])
57361f28255Scgd {
57461f28255Scgd int t;
57561f28255Scgd
57661f28255Scgd if (argc < 2) {
57745afe098Schristos getmore("Rexmt-timeout ", "(value) ");
57861f28255Scgd argc = margc;
57961f28255Scgd argv = margv;
58061f28255Scgd }
58161f28255Scgd if (argc != 2) {
58245afe098Schristos (void)printf("Usage: %s value\n", argv[0]);
58361f28255Scgd return;
58461f28255Scgd }
58561f28255Scgd t = atoi(argv[1]);
58661f28255Scgd if (t < 0)
5879d020319Schristos (void)printf("%s: bad value\n", argv[1]);
58861f28255Scgd else
58961f28255Scgd rexmtval = t;
59061f28255Scgd }
59161f28255Scgd
59245afe098Schristos static void
settimeout(int argc,char * argv[])59345afe098Schristos settimeout(int argc, char *argv[])
59461f28255Scgd {
59561f28255Scgd int t;
59661f28255Scgd
59761f28255Scgd if (argc < 2) {
59845afe098Schristos getmore("Maximum-timeout ", "(value) ");
59961f28255Scgd argc = margc;
60061f28255Scgd argv = margv;
60161f28255Scgd }
60261f28255Scgd if (argc != 2) {
60345afe098Schristos (void)printf("Usage: %s value\n", argv[0]);
60461f28255Scgd return;
60561f28255Scgd }
60661f28255Scgd t = atoi(argv[1]);
60761f28255Scgd if (t < 0)
6089d020319Schristos (void)printf("%s: bad value\n", argv[1]);
60961f28255Scgd else
61061f28255Scgd maxtimeout = t;
61161f28255Scgd }
61261f28255Scgd
61345afe098Schristos static void
614643cfdb6Schristos /*ARGSUSED*/
status(int argc,char * argv[])61545afe098Schristos status(int argc, char *argv[])
61661f28255Scgd {
61761f28255Scgd if (connected)
6189d020319Schristos (void)printf("Connected to %s.\n", hostname);
61961f28255Scgd else
6209d020319Schristos (void)printf("Not connected.\n");
6219d020319Schristos (void)printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
62261f28255Scgd verbose ? "on" : "off", trace ? "on" : "off");
6239d020319Schristos (void)printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
62461f28255Scgd rexmtval, maxtimeout);
62561f28255Scgd }
62661f28255Scgd
62745afe098Schristos static void
628643cfdb6Schristos /*ARGSUSED*/
intr(int dummy)62945afe098Schristos intr(int dummy)
63061f28255Scgd {
6311bbda10eSjtc
6329d020319Schristos (void)signal(SIGALRM, SIG_IGN);
6339d020319Schristos (void)alarm(0);
63461f28255Scgd longjmp(toplevel, -1);
63561f28255Scgd }
63661f28255Scgd
63745afe098Schristos static char *
tail(char * filename)63845afe098Schristos tail(char *filename)
63961f28255Scgd {
6400471adfdSlukem char *s;
64161f28255Scgd
64261f28255Scgd while (*filename) {
6430471adfdSlukem s = strrchr(filename, '/');
64461f28255Scgd if (s == NULL)
64561f28255Scgd break;
64661f28255Scgd if (s[1])
64745afe098Schristos return s + 1;
64861f28255Scgd *s = '\0';
64961f28255Scgd }
65045afe098Schristos return filename;
65161f28255Scgd }
65261f28255Scgd
65361f28255Scgd /*
65461f28255Scgd * Command parser.
65561f28255Scgd */
6561bbda10eSjtc static __dead void
command(void)65745afe098Schristos command(void)
65861f28255Scgd {
659b4995580Sross const struct cmd *c;
66061f28255Scgd
66161f28255Scgd for (;;) {
6629d020319Schristos (void)printf("%s> ", prompt);
6637f5fd4a5Schristos if (fgets(line, LBUFLEN, stdin) == NULL) {
66461f28255Scgd if (feof(stdin)) {
6651bbda10eSjtc exit(0);
66661f28255Scgd } else {
66761f28255Scgd continue;
66861f28255Scgd }
66961f28255Scgd }
67045afe098Schristos if (line[0] == '\0' || line[0] == '\n')
67161f28255Scgd continue;
67261f28255Scgd makeargv();
6731bbda10eSjtc if (margc == 0)
6741bbda10eSjtc continue;
67561f28255Scgd c = getcmd(margv[0]);
67661f28255Scgd if (c == (struct cmd *)-1) {
6779d020319Schristos (void)printf("?Ambiguous command\n");
67861f28255Scgd continue;
67961f28255Scgd }
68061f28255Scgd if (c == 0) {
6819d020319Schristos (void)printf("?Invalid command\n");
68261f28255Scgd continue;
68361f28255Scgd }
68461f28255Scgd (*c->handler)(margc, margv);
68561f28255Scgd }
68661f28255Scgd }
68761f28255Scgd
68845afe098Schristos static const struct cmd *
getcmd(const char * name)68945afe098Schristos getcmd(const char *name)
69061f28255Scgd {
691b4995580Sross const char *p, *q;
692b4995580Sross const struct cmd *c, *found;
6930471adfdSlukem int nmatches, longest;
69461f28255Scgd
69561f28255Scgd longest = 0;
69661f28255Scgd nmatches = 0;
69761f28255Scgd found = 0;
6981bbda10eSjtc for (c = cmdtab; (p = c->name) != NULL; c++) {
69961f28255Scgd for (q = name; *q == *p++; q++)
70061f28255Scgd if (*q == 0) /* exact match? */
70145afe098Schristos return c;
70261f28255Scgd if (!*q) { /* the name was a prefix */
70361f28255Scgd if (q - name > longest) {
70461f28255Scgd longest = q - name;
70561f28255Scgd nmatches = 1;
70661f28255Scgd found = c;
70761f28255Scgd } else if (q - name == longest)
70861f28255Scgd nmatches++;
70961f28255Scgd }
71061f28255Scgd }
71161f28255Scgd if (nmatches > 1)
71245afe098Schristos return (struct cmd *)-1;
71345afe098Schristos return found;
71461f28255Scgd }
71561f28255Scgd
71661f28255Scgd /*
71761f28255Scgd * Slice a string up into argc/argv.
71861f28255Scgd */
7191bbda10eSjtc static void
makeargv(void)72045afe098Schristos makeargv(void)
72161f28255Scgd {
7220471adfdSlukem char *cp;
7230471adfdSlukem char **argp = margv;
72461f28255Scgd
72561f28255Scgd margc = 0;
72661f28255Scgd for (cp = line; *cp;) {
727e42e202cSchristos while (isspace((unsigned char)*cp))
72861f28255Scgd cp++;
72961f28255Scgd if (*cp == '\0')
73061f28255Scgd break;
73161f28255Scgd *argp++ = cp;
73261f28255Scgd margc += 1;
733e42e202cSchristos while (*cp != '\0' && !isspace((unsigned char)*cp))
73461f28255Scgd cp++;
73561f28255Scgd if (*cp == '\0')
73661f28255Scgd break;
73761f28255Scgd *cp++ = '\0';
73861f28255Scgd }
73945afe098Schristos *argp++ = NULL;
74061f28255Scgd }
74161f28255Scgd
74245afe098Schristos static void
743643cfdb6Schristos /*ARGSUSED*/
quit(int argc,char * argv[])74445afe098Schristos quit(int argc, char *argv[])
74561f28255Scgd {
7461bbda10eSjtc
74761f28255Scgd exit(0);
74861f28255Scgd }
74961f28255Scgd
75061f28255Scgd /*
75161f28255Scgd * Help command.
75261f28255Scgd */
75345afe098Schristos static void
help(int argc,char * argv[])75445afe098Schristos help(int argc, char *argv[])
75561f28255Scgd {
756b4995580Sross const struct cmd *c;
75761f28255Scgd
75861f28255Scgd if (argc == 1) {
7599d020319Schristos (void)printf("Commands may be abbreviated. Commands are:\n\n");
76061f28255Scgd for (c = cmdtab; c->name; c++)
76145afe098Schristos (void)printf("%-*s\t%s\n", (int)HELPINDENT, c->name,
76245afe098Schristos c->help);
76361f28255Scgd return;
76461f28255Scgd }
76561f28255Scgd while (--argc > 0) {
7660471adfdSlukem char *arg;
76761f28255Scgd arg = *++argv;
76861f28255Scgd c = getcmd(arg);
76961f28255Scgd if (c == (struct cmd *)-1)
7709d020319Schristos (void)printf("?Ambiguous help command %s\n", arg);
77145afe098Schristos else if (c == NULL)
7729d020319Schristos (void)printf("?Invalid help command %s\n", arg);
77361f28255Scgd else
7749d020319Schristos (void)printf("%s\n", c->help);
77561f28255Scgd }
77661f28255Scgd }
77761f28255Scgd
77845afe098Schristos static void
779643cfdb6Schristos /*ARGSUSED*/
settrace(int argc,char ** argv)78045afe098Schristos settrace(int argc, char **argv)
78161f28255Scgd {
78261f28255Scgd trace = !trace;
7839d020319Schristos (void)printf("Packet tracing %s.\n", trace ? "on" : "off");
78461f28255Scgd }
78561f28255Scgd
78645afe098Schristos static void
787643cfdb6Schristos /*ARGSUSED*/
setverbose(int argc,char ** argv)78845afe098Schristos setverbose(int argc, char **argv)
78961f28255Scgd {
79061f28255Scgd verbose = !verbose;
7919d020319Schristos (void)printf("Verbose mode %s.\n", verbose ? "on" : "off");
79261f28255Scgd }
79344411286Sbriggs
79445afe098Schristos static void
795643cfdb6Schristos /*ARGSUSED*/
settsize(int argc,char ** argv)79645afe098Schristos settsize(int argc, char **argv)
79744411286Sbriggs {
79844411286Sbriggs tsize = !tsize;
7999d020319Schristos (void)printf("Tsize mode %s.\n", tsize ? "on" : "off");
80044411286Sbriggs }
80144411286Sbriggs
80245afe098Schristos static void
803643cfdb6Schristos /*ARGSUSED*/
settimeoutopt(int argc,char ** argv)80445afe098Schristos settimeoutopt(int argc, char **argv)
80544411286Sbriggs {
80644411286Sbriggs tout = !tout;
8079d020319Schristos (void)printf("Timeout option %s.\n", tout ? "on" : "off");
80844411286Sbriggs }
809