1223328Sgavin/*	$NetBSD: cmds.c,v 1.17 2010/01/12 06:55:47 lukem Exp $	*/
2223328Sgavin/*	from	NetBSD: cmds.c,v 1.130 2009/07/13 19:05:41 roy Exp	*/
379971Sobrien
479971Sobrien/*-
5223328Sgavin * Copyright (c) 1996-2009 The NetBSD Foundation, Inc.
679971Sobrien * All rights reserved.
779971Sobrien *
879971Sobrien * This code is derived from software contributed to The NetBSD Foundation
979971Sobrien * by Luke Mewburn.
1079971Sobrien *
1179971Sobrien * This code is derived from software contributed to The NetBSD Foundation
1279971Sobrien * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
1379971Sobrien * NASA Ames Research Center.
1479971Sobrien *
1579971Sobrien * Redistribution and use in source and binary forms, with or without
1679971Sobrien * modification, are permitted provided that the following conditions
1779971Sobrien * are met:
1879971Sobrien * 1. Redistributions of source code must retain the above copyright
1979971Sobrien *    notice, this list of conditions and the following disclaimer.
2079971Sobrien * 2. Redistributions in binary form must reproduce the above copyright
2179971Sobrien *    notice, this list of conditions and the following disclaimer in the
2279971Sobrien *    documentation and/or other materials provided with the distribution.
2379971Sobrien *
2479971Sobrien * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2579971Sobrien * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2679971Sobrien * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2779971Sobrien * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2879971Sobrien * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2979971Sobrien * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3079971Sobrien * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3179971Sobrien * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3279971Sobrien * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3379971Sobrien * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3479971Sobrien * POSSIBILITY OF SUCH DAMAGE.
3579971Sobrien */
3679971Sobrien
3779971Sobrien/*
3879971Sobrien * Copyright (c) 1985, 1989, 1993, 1994
3979971Sobrien *	The Regents of the University of California.  All rights reserved.
4079971Sobrien *
4179971Sobrien * Redistribution and use in source and binary forms, with or without
4279971Sobrien * modification, are permitted provided that the following conditions
4379971Sobrien * are met:
4479971Sobrien * 1. Redistributions of source code must retain the above copyright
4579971Sobrien *    notice, this list of conditions and the following disclaimer.
4679971Sobrien * 2. Redistributions in binary form must reproduce the above copyright
4779971Sobrien *    notice, this list of conditions and the following disclaimer in the
4879971Sobrien *    documentation and/or other materials provided with the distribution.
49121966Smikeh * 3. Neither the name of the University nor the names of its contributors
5079971Sobrien *    may be used to endorse or promote products derived from this software
5179971Sobrien *    without specific prior written permission.
5279971Sobrien *
5379971Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5479971Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5579971Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5679971Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5779971Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5879971Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5979971Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6079971Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6179971Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6279971Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6379971Sobrien * SUCH DAMAGE.
6479971Sobrien */
6579971Sobrien
6679971Sobrien/*
6779971Sobrien * Copyright (C) 1997 and 1998 WIDE Project.
6879971Sobrien * All rights reserved.
69146309Smikeh *
7079971Sobrien * Redistribution and use in source and binary forms, with or without
7179971Sobrien * modification, are permitted provided that the following conditions
7279971Sobrien * are met:
7379971Sobrien * 1. Redistributions of source code must retain the above copyright
7479971Sobrien *    notice, this list of conditions and the following disclaimer.
7579971Sobrien * 2. Redistributions in binary form must reproduce the above copyright
7679971Sobrien *    notice, this list of conditions and the following disclaimer in the
7779971Sobrien *    documentation and/or other materials provided with the distribution.
7879971Sobrien * 3. Neither the name of the project nor the names of its contributors
7979971Sobrien *    may be used to endorse or promote products derived from this software
8079971Sobrien *    without specific prior written permission.
81146309Smikeh *
8279971Sobrien * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
8379971Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
8479971Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
8579971Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
8679971Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
8779971Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
8879971Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
8979971Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
9079971Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
9179971Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
9279971Sobrien * SUCH DAMAGE.
9379971Sobrien */
9479971Sobrien
95223328Sgavin#include "tnftp.h"
96223328Sgavin
97223328Sgavin#if 0	/* tnftp */
98223328Sgavin
99116424Smikeh#include <sys/cdefs.h>
100116424Smikeh#ifndef lint
101116424Smikeh#if 0
102116424Smikehstatic char sccsid[] = "@(#)cmds.c	8.6 (Berkeley) 10/9/94";
103116424Smikeh#else
104223328Sgavin__RCSID(" NetBSD: cmds.c,v 1.130 2009/07/13 19:05:41 roy Exp  ");
105116424Smikeh#endif
106116424Smikeh#endif /* not lint */
107116424Smikeh
10879971Sobrien/*
10979971Sobrien * FTP User Program -- Command Routines.
11079971Sobrien */
111116424Smikeh#include <sys/types.h>
112116424Smikeh#include <sys/socket.h>
113116424Smikeh#include <sys/stat.h>
114116424Smikeh#include <sys/wait.h>
115116424Smikeh#include <arpa/ftp.h>
11679971Sobrien
117116424Smikeh#include <ctype.h>
118116424Smikeh#include <err.h>
119116424Smikeh#include <glob.h>
120116424Smikeh#include <limits.h>
121116424Smikeh#include <netdb.h>
122116424Smikeh#include <paths.h>
123223328Sgavin#include <stddef.h>
124116424Smikeh#include <stdio.h>
125116424Smikeh#include <stdlib.h>
126116424Smikeh#include <string.h>
127116424Smikeh#include <time.h>
128116424Smikeh#include <unistd.h>
12979971Sobrien
130223328Sgavin#endif	/* tnftp */
131223328Sgavin
13279971Sobrien#include "ftp_var.h"
13379971Sobrien#include "version.h"
13479971Sobrien
135223328Sgavinstatic struct types {
136223328Sgavin	const char	*t_name;
137223328Sgavin	const char	*t_mode;
138223328Sgavin	int		t_type;
139223328Sgavin	const char	*t_arg;
14079971Sobrien} types[] = {
14179971Sobrien	{ "ascii",	"A",	TYPE_A,	0 },
14279971Sobrien	{ "binary",	"I",	TYPE_I,	0 },
14379971Sobrien	{ "image",	"I",	TYPE_I,	0 },
14479971Sobrien	{ "ebcdic",	"E",	TYPE_E,	0 },
14579971Sobrien	{ "tenex",	"L",	TYPE_L,	bytename },
146223328Sgavin	{ NULL,		NULL,	0, NULL }
14779971Sobrien};
14879971Sobrien
149223328Sgavinstatic sigjmp_buf	 jabort;
15079971Sobrien
15179971Sobrienstatic int	confirm(const char *, const char *);
152223328Sgavinstatic void	mintr(int);
153223328Sgavinstatic void	mabort(const char *);
154223328Sgavinstatic void	set_type(const char *);
15579971Sobrien
156142129Smikehstatic const char *doprocess(char *, size_t, const char *, int, int, int);
157142129Smikehstatic const char *domap(char *, size_t, const char *);
158142129Smikehstatic const char *docase(char *, size_t, const char *);
159142129Smikehstatic const char *dotrans(char *, size_t, const char *);
160142129Smikeh
161223328Sgavin/*
162223328Sgavin * Confirm if "cmd" is to be performed upon "file".
163223328Sgavin * If "file" is NULL, generate a "Continue with" prompt instead.
164223328Sgavin */
16579971Sobrienstatic int
16679971Sobrienconfirm(const char *cmd, const char *file)
16779971Sobrien{
168223328Sgavin	const char *errormsg;
169223328Sgavin	char cline[BUFSIZ];
170223328Sgavin	const char *promptleft, *promptright;
17179971Sobrien
17279971Sobrien	if (!interactive || confirmrest)
17379971Sobrien		return (1);
174223328Sgavin	if (file == NULL) {
175223328Sgavin		promptleft = "Continue with";
176223328Sgavin		promptright = cmd;
177223328Sgavin	} else {
178223328Sgavin		promptleft = cmd;
179223328Sgavin		promptright = file;
180223328Sgavin	}
18179971Sobrien	while (1) {
182223328Sgavin		fprintf(ttyout, "%s %s [anpqy?]? ", promptleft, promptright);
18379971Sobrien		(void)fflush(ttyout);
184223328Sgavin		if (get_line(stdin, cline, sizeof(cline), &errormsg) < 0) {
18579971Sobrien			mflag = 0;
186223328Sgavin			fprintf(ttyout, "%s; %s aborted\n", errormsg, cmd);
18779971Sobrien			return (0);
18879971Sobrien		}
189223328Sgavin		switch (tolower((unsigned char)*cline)) {
19079971Sobrien			case 'a':
19179971Sobrien				confirmrest = 1;
19279971Sobrien				fprintf(ttyout,
19379971Sobrien				    "Prompting off for duration of %s.\n", cmd);
19479971Sobrien				break;
19579971Sobrien			case 'p':
19679971Sobrien				interactive = 0;
19779971Sobrien				fputs("Interactive mode: off.\n", ttyout);
19879971Sobrien				break;
19979971Sobrien			case 'q':
20079971Sobrien				mflag = 0;
201223328Sgavin				fprintf(ttyout, "%s aborted.\n", cmd);
20279971Sobrien				/* FALLTHROUGH */
20379971Sobrien			case 'n':
20479971Sobrien				return (0);
20579971Sobrien			case '?':
20679971Sobrien				fprintf(ttyout,
20779971Sobrien				    "  confirmation options:\n"
20879971Sobrien				    "\ta  answer `yes' for the duration of %s\n"
20979971Sobrien				    "\tn  answer `no' for this file\n"
21079971Sobrien				    "\tp  turn off `prompt' mode\n"
21179971Sobrien				    "\tq  stop the current %s\n"
21279971Sobrien				    "\ty  answer `yes' for this file\n"
21379971Sobrien				    "\t?  this help list\n",
21479971Sobrien				    cmd, cmd);
21579971Sobrien				continue;	/* back to while(1) */
21679971Sobrien		}
21779971Sobrien		return (1);
21879971Sobrien	}
21979971Sobrien	/* NOTREACHED */
22079971Sobrien}
22179971Sobrien
22279971Sobrien/*
22379971Sobrien * Set transfer type.
22479971Sobrien */
22579971Sobrienvoid
22679971Sobriensettype(int argc, char *argv[])
22779971Sobrien{
22879971Sobrien	struct types *p;
22979971Sobrien
23079971Sobrien	if (argc == 0 || argc > 2) {
231223328Sgavin		const char *sep;
23279971Sobrien
233223328Sgavin		UPRINTF("usage: %s [", argv[0]);
23479971Sobrien		sep = " ";
23579971Sobrien		for (p = types; p->t_name; p++) {
23679971Sobrien			fprintf(ttyout, "%s%s", sep, p->t_name);
23779971Sobrien			sep = " | ";
23879971Sobrien		}
23979971Sobrien		fputs(" ]\n", ttyout);
24079971Sobrien		code = -1;
24179971Sobrien		return;
24279971Sobrien	}
24379971Sobrien	if (argc < 2) {
24479971Sobrien		fprintf(ttyout, "Using %s mode to transfer files.\n", typename);
24579971Sobrien		code = 0;
24679971Sobrien		return;
24779971Sobrien	}
248223328Sgavin	set_type(argv[1]);
249223328Sgavin}
250223328Sgavin
251223328Sgavinvoid
252223328Sgavinset_type(const char *ttype)
253223328Sgavin{
254223328Sgavin	struct types *p;
255223328Sgavin	int comret;
256223328Sgavin
25779971Sobrien	for (p = types; p->t_name; p++)
258223328Sgavin		if (strcmp(ttype, p->t_name) == 0)
25979971Sobrien			break;
26079971Sobrien	if (p->t_name == 0) {
261223328Sgavin		fprintf(ttyout, "%s: unknown mode.\n", ttype);
26279971Sobrien		code = -1;
26379971Sobrien		return;
26479971Sobrien	}
26579971Sobrien	if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
26679971Sobrien		comret = command("TYPE %s %s", p->t_mode, p->t_arg);
26779971Sobrien	else
26879971Sobrien		comret = command("TYPE %s", p->t_mode);
26979971Sobrien	if (comret == COMPLETE) {
27079971Sobrien		(void)strlcpy(typename, p->t_name, sizeof(typename));
27179971Sobrien		curtype = type = p->t_type;
27279971Sobrien	}
27379971Sobrien}
27479971Sobrien
27579971Sobrien/*
27679971Sobrien * Internal form of settype; changes current type in use with server
27779971Sobrien * without changing our notion of the type for data transfers.
27879971Sobrien * Used to change to and from ascii for listings.
27979971Sobrien */
28079971Sobrienvoid
28179971Sobrienchangetype(int newtype, int show)
28279971Sobrien{
28379971Sobrien	struct types *p;
28479971Sobrien	int comret, oldverbose = verbose;
28579971Sobrien
28679971Sobrien	if (newtype == 0)
28779971Sobrien		newtype = TYPE_I;
28879971Sobrien	if (newtype == curtype)
28979971Sobrien		return;
290223328Sgavin	if (ftp_debug == 0 && show == 0)
29179971Sobrien		verbose = 0;
29279971Sobrien	for (p = types; p->t_name; p++)
29379971Sobrien		if (newtype == p->t_type)
29479971Sobrien			break;
29579971Sobrien	if (p->t_name == 0) {
296223328Sgavin		errx(1, "changetype: unknown type %d", newtype);
29779971Sobrien	}
29879971Sobrien	if (newtype == TYPE_L && bytename[0] != '\0')
29979971Sobrien		comret = command("TYPE %s %s", p->t_mode, bytename);
30079971Sobrien	else
30179971Sobrien		comret = command("TYPE %s", p->t_mode);
30279971Sobrien	if (comret == COMPLETE)
30379971Sobrien		curtype = newtype;
30479971Sobrien	verbose = oldverbose;
30579971Sobrien}
30679971Sobrien
30779971Sobrien/*
30879971Sobrien * Set binary transfer type.
30979971Sobrien */
31079971Sobrien/*VARARGS*/
31179971Sobrienvoid
31279971Sobriensetbinary(int argc, char *argv[])
31379971Sobrien{
31479971Sobrien
31579971Sobrien	if (argc == 0) {
316223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
31779971Sobrien		code = -1;
31879971Sobrien		return;
31979971Sobrien	}
320223328Sgavin	set_type("binary");
32179971Sobrien}
32279971Sobrien
32379971Sobrien/*
32479971Sobrien * Set ascii transfer type.
32579971Sobrien */
32679971Sobrien/*VARARGS*/
32779971Sobrienvoid
32879971Sobriensetascii(int argc, char *argv[])
32979971Sobrien{
33079971Sobrien
33179971Sobrien	if (argc == 0) {
332223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
33379971Sobrien		code = -1;
33479971Sobrien		return;
33579971Sobrien	}
336223328Sgavin	set_type("ascii");
33779971Sobrien}
33879971Sobrien
33979971Sobrien/*
34079971Sobrien * Set tenex transfer type.
34179971Sobrien */
34279971Sobrien/*VARARGS*/
34379971Sobrienvoid
34479971Sobriensettenex(int argc, char *argv[])
34579971Sobrien{
34679971Sobrien
34779971Sobrien	if (argc == 0) {
348223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
34979971Sobrien		code = -1;
35079971Sobrien		return;
35179971Sobrien	}
352223328Sgavin	set_type("tenex");
35379971Sobrien}
35479971Sobrien
35579971Sobrien/*
35679971Sobrien * Set file transfer mode.
35779971Sobrien */
35879971Sobrien/*ARGSUSED*/
35979971Sobrienvoid
36079971Sobriensetftmode(int argc, char *argv[])
36179971Sobrien{
36279971Sobrien
36379971Sobrien	if (argc != 2) {
364223328Sgavin		UPRINTF("usage: %s mode-name\n", argv[0]);
36579971Sobrien		code = -1;
36679971Sobrien		return;
36779971Sobrien	}
36879971Sobrien	fprintf(ttyout, "We only support %s mode, sorry.\n", modename);
36979971Sobrien	code = -1;
37079971Sobrien}
37179971Sobrien
37279971Sobrien/*
37379971Sobrien * Set file transfer format.
37479971Sobrien */
37579971Sobrien/*ARGSUSED*/
37679971Sobrienvoid
37779971Sobriensetform(int argc, char *argv[])
37879971Sobrien{
37979971Sobrien
38079971Sobrien	if (argc != 2) {
381223328Sgavin		UPRINTF("usage: %s format\n", argv[0]);
38279971Sobrien		code = -1;
38379971Sobrien		return;
38479971Sobrien	}
38579971Sobrien	fprintf(ttyout, "We only support %s format, sorry.\n", formname);
38679971Sobrien	code = -1;
38779971Sobrien}
38879971Sobrien
38979971Sobrien/*
39079971Sobrien * Set file transfer structure.
39179971Sobrien */
39279971Sobrien/*ARGSUSED*/
39379971Sobrienvoid
39479971Sobriensetstruct(int argc, char *argv[])
39579971Sobrien{
39679971Sobrien
39779971Sobrien	if (argc != 2) {
398223328Sgavin		UPRINTF("usage: %s struct-mode\n", argv[0]);
39979971Sobrien		code = -1;
40079971Sobrien		return;
40179971Sobrien	}
40279971Sobrien	fprintf(ttyout, "We only support %s structure, sorry.\n", structname);
40379971Sobrien	code = -1;
40479971Sobrien}
40579971Sobrien
40679971Sobrien/*
40779971Sobrien * Send a single file.
40879971Sobrien */
40979971Sobrienvoid
41079971Sobrienput(int argc, char *argv[])
41179971Sobrien{
412142129Smikeh	char buf[MAXPATHLEN];
413223328Sgavin	const char *cmd;
41479971Sobrien	int loc = 0;
415142129Smikeh	char *locfile;
416142129Smikeh	const char *remfile;
41779971Sobrien
41879971Sobrien	if (argc == 2) {
41979971Sobrien		argc++;
42079971Sobrien		argv[2] = argv[1];
42179971Sobrien		loc++;
42279971Sobrien	}
42379971Sobrien	if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-file")))
42479971Sobrien		goto usage;
42579971Sobrien	if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
42679971Sobrien usage:
427223328Sgavin		UPRINTF("usage: %s local-file [remote-file]\n", argv[0]);
42879971Sobrien		code = -1;
42979971Sobrien		return;
43079971Sobrien	}
43179971Sobrien	if ((locfile = globulize(argv[1])) == NULL) {
43279971Sobrien		code = -1;
43379971Sobrien		return;
43479971Sobrien	}
43579971Sobrien	remfile = argv[2];
43679971Sobrien	if (loc)	/* If argv[2] is a copy of the old argv[1], update it */
43779971Sobrien		remfile = locfile;
43879971Sobrien	cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
439142129Smikeh	remfile = doprocess(buf, sizeof(buf), remfile,
440142129Smikeh		0, loc && ntflag, loc && mapflag);
44179971Sobrien	sendrequest(cmd, locfile, remfile,
44279971Sobrien	    locfile != argv[1] || remfile != argv[2]);
44379971Sobrien	free(locfile);
44479971Sobrien}
44579971Sobrien
446142129Smikehstatic const char *
447142129Smikehdoprocess(char *dst, size_t dlen, const char *src,
448142129Smikeh    int casef, int transf, int mapf)
449142129Smikeh{
450142129Smikeh	if (casef)
451142129Smikeh		src = docase(dst, dlen, src);
452142129Smikeh	if (transf)
453142129Smikeh		src = dotrans(dst, dlen, src);
454142129Smikeh	if (mapf)
455142129Smikeh		src = domap(dst, dlen, src);
456142129Smikeh	return src;
457142129Smikeh}
458142129Smikeh
45979971Sobrien/*
46079971Sobrien * Send multiple files.
46179971Sobrien */
46279971Sobrienvoid
46379971Sobrienmput(int argc, char *argv[])
46479971Sobrien{
46579971Sobrien	int i;
46679971Sobrien	sigfunc oldintr;
46779971Sobrien	int ointer;
468142129Smikeh	const char *tp;
46979971Sobrien
47079971Sobrien	if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-files"))) {
471223328Sgavin		UPRINTF("usage: %s local-files\n", argv[0]);
47279971Sobrien		code = -1;
47379971Sobrien		return;
47479971Sobrien	}
47579971Sobrien	mflag = 1;
47679971Sobrien	oldintr = xsignal(SIGINT, mintr);
47779971Sobrien	if (sigsetjmp(jabort, 1))
478223328Sgavin		mabort(argv[0]);
47979971Sobrien	if (proxy) {
48079971Sobrien		char *cp;
48179971Sobrien
48279971Sobrien		while ((cp = remglob(argv, 0, NULL)) != NULL) {
48379971Sobrien			if (*cp == '\0' || !connected) {
48479971Sobrien				mflag = 0;
48579971Sobrien				continue;
48679971Sobrien			}
48779971Sobrien			if (mflag && confirm(argv[0], cp)) {
488142129Smikeh				char buf[MAXPATHLEN];
489142129Smikeh				tp = doprocess(buf, sizeof(buf), cp,
490142129Smikeh				    mcase, ntflag, mapflag);
49179971Sobrien				sendrequest((sunique) ? "STOU" : "STOR",
49279971Sobrien				    cp, tp, cp != tp || !interactive);
49379971Sobrien				if (!mflag && fromatty) {
49479971Sobrien					ointer = interactive;
49579971Sobrien					interactive = 1;
496223328Sgavin					if (confirm(argv[0], NULL)) {
49779971Sobrien						mflag++;
49879971Sobrien					}
49979971Sobrien					interactive = ointer;
50079971Sobrien				}
50179971Sobrien			}
50279971Sobrien		}
50379971Sobrien		goto cleanupmput;
50479971Sobrien	}
50579971Sobrien	for (i = 1; i < argc && connected; i++) {
50679971Sobrien		char **cpp;
50779971Sobrien		glob_t gl;
50879971Sobrien		int flags;
50979971Sobrien
51079971Sobrien		if (!doglob) {
51179971Sobrien			if (mflag && confirm(argv[0], argv[i])) {
512142129Smikeh				char buf[MAXPATHLEN];
513142129Smikeh				tp = doprocess(buf, sizeof(buf), argv[i],
514142129Smikeh					0, ntflag, mapflag);
51579971Sobrien				sendrequest((sunique) ? "STOU" : "STOR",
51679971Sobrien				    argv[i], tp, tp != argv[i] || !interactive);
51779971Sobrien				if (!mflag && fromatty) {
51879971Sobrien					ointer = interactive;
51979971Sobrien					interactive = 1;
520223328Sgavin					if (confirm(argv[0], NULL)) {
52179971Sobrien						mflag++;
52279971Sobrien					}
52379971Sobrien					interactive = ointer;
52479971Sobrien				}
52579971Sobrien			}
52679971Sobrien			continue;
52779971Sobrien		}
52879971Sobrien
52979971Sobrien		memset(&gl, 0, sizeof(gl));
53079971Sobrien		flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
53179971Sobrien		if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
532223328Sgavin			warnx("Glob pattern `%s' not found", argv[i]);
53379971Sobrien			globfree(&gl);
53479971Sobrien			continue;
53579971Sobrien		}
53679971Sobrien		for (cpp = gl.gl_pathv; cpp && *cpp != NULL && connected;
53779971Sobrien		    cpp++) {
53879971Sobrien			if (mflag && confirm(argv[0], *cpp)) {
539142129Smikeh				char buf[MAXPATHLEN];
540142129Smikeh				tp = *cpp;
541142129Smikeh				tp = doprocess(buf, sizeof(buf), *cpp,
542142129Smikeh				    0, ntflag, mapflag);
54379971Sobrien				sendrequest((sunique) ? "STOU" : "STOR",
54479971Sobrien				    *cpp, tp, *cpp != tp || !interactive);
54579971Sobrien				if (!mflag && fromatty) {
54679971Sobrien					ointer = interactive;
54779971Sobrien					interactive = 1;
548223328Sgavin					if (confirm(argv[0], NULL)) {
54979971Sobrien						mflag++;
55079971Sobrien					}
55179971Sobrien					interactive = ointer;
55279971Sobrien				}
55379971Sobrien			}
55479971Sobrien		}
55579971Sobrien		globfree(&gl);
55679971Sobrien	}
55779971Sobrien cleanupmput:
55879971Sobrien	(void)xsignal(SIGINT, oldintr);
55979971Sobrien	mflag = 0;
56079971Sobrien}
56179971Sobrien
56279971Sobrienvoid
56379971Sobrienreget(int argc, char *argv[])
56479971Sobrien{
56579971Sobrien
56698247Smikeh	(void)getit(argc, argv, 1, "r+");
56779971Sobrien}
56879971Sobrien
56979971Sobrienvoid
57079971Sobrienget(int argc, char *argv[])
57179971Sobrien{
57279971Sobrien
57398247Smikeh	(void)getit(argc, argv, 0, restart_point ? "r+" : "w" );
57479971Sobrien}
57579971Sobrien
57679971Sobrien/*
57779971Sobrien * Receive one file.
57898247Smikeh * If restartit is  1, restart the xfer always.
57998247Smikeh * If restartit is -1, restart the xfer only if the remote file is newer.
58079971Sobrien */
58179971Sobrienint
582223328Sgavingetit(int argc, char *argv[], int restartit, const char *gmode)
58379971Sobrien{
584146309Smikeh	int	loc, rval;
585142129Smikeh	char	*remfile, *olocfile;
586142129Smikeh	const char *locfile;
587146309Smikeh	char	buf[MAXPATHLEN];
58879971Sobrien
58998247Smikeh	loc = rval = 0;
59079971Sobrien	if (argc == 2) {
59179971Sobrien		argc++;
59279971Sobrien		argv[2] = argv[1];
59379971Sobrien		loc++;
59479971Sobrien	}
59579971Sobrien	if (argc == 0 || (argc == 1 && !another(&argc, &argv, "remote-file")))
59679971Sobrien		goto usage;
59779971Sobrien	if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
59879971Sobrien usage:
599223328Sgavin		UPRINTF("usage: %s remote-file [local-file]\n", argv[0]);
60079971Sobrien		code = -1;
60179971Sobrien		return (0);
60279971Sobrien	}
60379971Sobrien	remfile = argv[1];
60479971Sobrien	if ((olocfile = globulize(argv[2])) == NULL) {
60579971Sobrien		code = -1;
60679971Sobrien		return (0);
60779971Sobrien	}
608142129Smikeh	locfile = doprocess(buf, sizeof(buf), olocfile,
609142129Smikeh		loc && mcase, loc && ntflag, loc && mapflag);
61079971Sobrien	if (restartit) {
61179971Sobrien		struct stat stbuf;
61279971Sobrien		int ret;
61379971Sobrien
61479971Sobrien		if (! features[FEAT_REST_STREAM]) {
61579971Sobrien			fprintf(ttyout,
61679971Sobrien			    "Restart is not supported by the remote server.\n");
61779971Sobrien			return (0);
61879971Sobrien		}
61979971Sobrien		ret = stat(locfile, &stbuf);
62079971Sobrien		if (restartit == 1) {
62179971Sobrien			if (ret < 0) {
622223328Sgavin				warn("Can't stat `%s'", locfile);
62379971Sobrien				goto freegetit;
62479971Sobrien			}
62579971Sobrien			restart_point = stbuf.st_size;
62679971Sobrien		} else {
62779971Sobrien			if (ret == 0) {
62879971Sobrien				time_t mtime;
62979971Sobrien
63079971Sobrien				mtime = remotemodtime(argv[1], 0);
63179971Sobrien				if (mtime == -1)
63279971Sobrien					goto freegetit;
63379971Sobrien				if (stbuf.st_mtime >= mtime) {
63479971Sobrien					rval = 1;
63579971Sobrien					goto freegetit;
63679971Sobrien				}
63779971Sobrien			}
63879971Sobrien		}
63979971Sobrien	}
64079971Sobrien
641223328Sgavin	recvrequest("RETR", locfile, remfile, gmode,
64279971Sobrien	    remfile != argv[1] || locfile != argv[2], loc);
64379971Sobrien	restart_point = 0;
64479971Sobrien freegetit:
64579971Sobrien	(void)free(olocfile);
64679971Sobrien	return (rval);
64779971Sobrien}
64879971Sobrien
64979971Sobrien/* ARGSUSED */
650223328Sgavinstatic void
65179971Sobrienmintr(int signo)
65279971Sobrien{
65379971Sobrien
65479971Sobrien	alarmtimer(0);
65579971Sobrien	if (fromatty)
65679971Sobrien		write(fileno(ttyout), "\n", 1);
65779971Sobrien	siglongjmp(jabort, 1);
65879971Sobrien}
65979971Sobrien
660223328Sgavinstatic void
661223328Sgavinmabort(const char *cmd)
66279971Sobrien{
66379971Sobrien	int ointer, oconf;
66479971Sobrien
66579971Sobrien	if (mflag && fromatty) {
66679971Sobrien		ointer = interactive;
66779971Sobrien		oconf = confirmrest;
66879971Sobrien		interactive = 1;
66979971Sobrien		confirmrest = 0;
670223328Sgavin		if (confirm(cmd, NULL)) {
67179971Sobrien			interactive = ointer;
67279971Sobrien			confirmrest = oconf;
67379971Sobrien			return;
67479971Sobrien		}
67579971Sobrien		interactive = ointer;
67679971Sobrien		confirmrest = oconf;
67779971Sobrien	}
67879971Sobrien	mflag = 0;
67979971Sobrien}
68079971Sobrien
68179971Sobrien/*
68279971Sobrien * Get multiple files.
68379971Sobrien */
68479971Sobrienvoid
68579971Sobrienmget(int argc, char *argv[])
68679971Sobrien{
68779971Sobrien	sigfunc oldintr;
68898247Smikeh	int ointer;
689142129Smikeh	char *cp;
690142129Smikeh	const char *tp;
691223328Sgavin	int volatile restartit;
69279971Sobrien
69379971Sobrien	if (argc == 0 ||
69479971Sobrien	    (argc == 1 && !another(&argc, &argv, "remote-files"))) {
695223328Sgavin		UPRINTF("usage: %s remote-files\n", argv[0]);
69679971Sobrien		code = -1;
69779971Sobrien		return;
69879971Sobrien	}
69979971Sobrien	mflag = 1;
70098247Smikeh	restart_point = 0;
70198247Smikeh	restartit = 0;
70298247Smikeh	if (strcmp(argv[0], "mreget") == 0) {
70398247Smikeh		if (! features[FEAT_REST_STREAM]) {
70498247Smikeh			fprintf(ttyout,
70598247Smikeh		    "Restart is not supported by the remote server.\n");
70698247Smikeh			return;
70798247Smikeh		}
70898247Smikeh		restartit = 1;
70998247Smikeh	}
71079971Sobrien	oldintr = xsignal(SIGINT, mintr);
71179971Sobrien	if (sigsetjmp(jabort, 1))
712223328Sgavin		mabort(argv[0]);
71379971Sobrien	while ((cp = remglob(argv, proxy, NULL)) != NULL) {
714142129Smikeh		char buf[MAXPATHLEN];
71579971Sobrien		if (*cp == '\0' || !connected) {
71679971Sobrien			mflag = 0;
71779971Sobrien			continue;
71879971Sobrien		}
719142129Smikeh		if (! mflag)
72098247Smikeh			continue;
721142129Smikeh		if (! fileindir(cp, localcwd)) {
722142129Smikeh			fprintf(ttyout, "Skipping non-relative filename `%s'\n",
723142129Smikeh			    cp);
724142129Smikeh			continue;
725142129Smikeh		}
726142129Smikeh		if (!confirm(argv[0], cp))
727142129Smikeh			continue;
728142129Smikeh		tp = doprocess(buf, sizeof(buf), cp, mcase, ntflag, mapflag);
72998247Smikeh		if (restartit) {
73098247Smikeh			struct stat stbuf;
73198247Smikeh
73298247Smikeh			if (stat(tp, &stbuf) == 0)
73398247Smikeh				restart_point = stbuf.st_size;
73498247Smikeh			else
735223328Sgavin				warn("Can't stat `%s'", tp);
73679971Sobrien		}
73798247Smikeh		recvrequest("RETR", tp, cp, restart_point ? "r+" : "w",
73898247Smikeh		    tp != cp || !interactive, 1);
73998247Smikeh		restart_point = 0;
74098247Smikeh		if (!mflag && fromatty) {
74198247Smikeh			ointer = interactive;
74298247Smikeh			interactive = 1;
743223328Sgavin			if (confirm(argv[0], NULL))
74498247Smikeh				mflag++;
74598247Smikeh			interactive = ointer;
74698247Smikeh		}
74779971Sobrien	}
74879971Sobrien	(void)xsignal(SIGINT, oldintr);
74979971Sobrien	mflag = 0;
75079971Sobrien}
75179971Sobrien
75279971Sobrien/*
75379971Sobrien * Read list of filenames from a local file and get those
75479971Sobrien */
75579971Sobrienvoid
75679971Sobrienfget(int argc, char *argv[])
75779971Sobrien{
758223328Sgavin	const char *gmode;
75979971Sobrien	FILE	*fp;
760223328Sgavin	char	buf[MAXPATHLEN], cmdbuf[MAX_C_NAME];
76179971Sobrien
76279971Sobrien	if (argc != 2) {
763223328Sgavin		UPRINTF("usage: %s localfile\n", argv[0]);
76479971Sobrien		code = -1;
76579971Sobrien		return;
76679971Sobrien	}
76779971Sobrien
76879971Sobrien	fp = fopen(argv[1], "r");
76979971Sobrien	if (fp == NULL) {
770223328Sgavin		fprintf(ttyout, "Can't open source file %s\n", argv[1]);
77179971Sobrien		code = -1;
77279971Sobrien		return;
77379971Sobrien	}
77479971Sobrien
775223328Sgavin	(void)strlcpy(cmdbuf, "get", sizeof(cmdbuf));
776223328Sgavin	argv[0] = cmdbuf;
777223328Sgavin	gmode = restart_point ? "r+" : "w";
77879971Sobrien
779223328Sgavin	while (get_line(fp, buf, sizeof(buf), NULL) >= 0) {
78079971Sobrien		if (buf[0] == '\0')
78179971Sobrien			continue;
78279971Sobrien		argv[1] = buf;
783223328Sgavin		(void)getit(argc, argv, 0, gmode);
78479971Sobrien	}
78579971Sobrien	fclose(fp);
78679971Sobrien}
78779971Sobrien
788223328Sgavinconst char *
789223328Sgavinonoff(int val)
79079971Sobrien{
79179971Sobrien
792223328Sgavin	return (val ? "on" : "off");
79379971Sobrien}
79479971Sobrien
79579971Sobrien/*
79679971Sobrien * Show status.
79779971Sobrien */
79879971Sobrien/*ARGSUSED*/
79979971Sobrienvoid
80079971Sobrienstatus(int argc, char *argv[])
80179971Sobrien{
80279971Sobrien
80379971Sobrien	if (argc == 0) {
804223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
80579971Sobrien		code = -1;
80679971Sobrien		return;
80779971Sobrien	}
808142129Smikeh#ifndef NO_STATUS
80979971Sobrien	if (connected)
81079971Sobrien		fprintf(ttyout, "Connected %sto %s.\n",
81179971Sobrien		    connected == -1 ? "and logged in" : "", hostname);
81279971Sobrien	else
81379971Sobrien		fputs("Not connected.\n", ttyout);
81479971Sobrien	if (!proxy) {
81579971Sobrien		pswitch(1);
81679971Sobrien		if (connected) {
81779971Sobrien			fprintf(ttyout, "Connected for proxy commands to %s.\n",
81879971Sobrien			    hostname);
81979971Sobrien		}
82079971Sobrien		else {
82179971Sobrien			fputs("No proxy connection.\n", ttyout);
82279971Sobrien		}
82379971Sobrien		pswitch(0);
82479971Sobrien	}
82579971Sobrien	fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
82679971Sobrien	    *gateserver ? gateserver : "(none)", gateport);
82779971Sobrien	fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
82879971Sobrien	    onoff(passivemode), onoff(activefallback));
82979971Sobrien	fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
83079971Sobrien	    modename, typename, formname, structname);
83179971Sobrien	fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
83279971Sobrien	    onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob));
83379971Sobrien	fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n",
83479971Sobrien	    onoff(sunique), onoff(runique));
83579971Sobrien	fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve));
83679971Sobrien	fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase),
83779971Sobrien	    onoff(crflag));
83879971Sobrien	if (ntflag) {
83979971Sobrien		fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout);
84079971Sobrien	}
84179971Sobrien	else {
84279971Sobrien		fputs("Ntrans: off.\n", ttyout);
84379971Sobrien	}
84479971Sobrien	if (mapflag) {
84579971Sobrien		fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout);
84679971Sobrien	}
84779971Sobrien	else {
84879971Sobrien		fputs("Nmap: off.\n", ttyout);
84979971Sobrien	}
85079971Sobrien	fprintf(ttyout,
85179971Sobrien	    "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
85279971Sobrien	    onoff(hash), mark, onoff(progress));
85379971Sobrien	fprintf(ttyout,
85479971Sobrien	    "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
85579971Sobrien	    onoff(rate_get), rate_get, rate_get_incr);
85679971Sobrien	fprintf(ttyout,
85779971Sobrien	    "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
85879971Sobrien	    onoff(rate_put), rate_put, rate_put_incr);
85979971Sobrien	fprintf(ttyout,
86079971Sobrien	    "Socket buffer sizes: send %d, receive %d.\n",
86179971Sobrien	    sndbuf_size, rcvbuf_size);
86279971Sobrien	fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport));
86379971Sobrien	fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4),
86479971Sobrien	    epsv4bad ? " (disabled for this connection)" : "");
865223328Sgavin	fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv6: %s%s.\n", onoff(epsv6),
866223328Sgavin	    epsv6bad ? " (disabled for this connection)" : "");
86779971Sobrien	fprintf(ttyout, "Command line editing: %s.\n",
86879971Sobrien#ifdef NO_EDITCOMPLETE
86979971Sobrien	    "support not compiled in"
87079971Sobrien#else	/* !def NO_EDITCOMPLETE */
87179971Sobrien	    onoff(editing)
87279971Sobrien#endif	/* !def NO_EDITCOMPLETE */
87379971Sobrien	    );
87479971Sobrien	if (macnum > 0) {
875142129Smikeh		int i;
876142129Smikeh
87779971Sobrien		fputs("Macros:\n", ttyout);
87879971Sobrien		for (i=0; i<macnum; i++) {
87979971Sobrien			fprintf(ttyout, "\t%s\n", macros[i].mac_name);
88079971Sobrien		}
88179971Sobrien	}
882142129Smikeh#endif /* !def NO_STATUS */
883142129Smikeh	fprintf(ttyout, "Version: %s %s\n", FTP_PRODUCT, FTP_VERSION);
88479971Sobrien	code = 0;
88579971Sobrien}
88679971Sobrien
88779971Sobrien/*
88879971Sobrien * Toggle a variable
88979971Sobrien */
89079971Sobrienint
89179971Sobrientogglevar(int argc, char *argv[], int *var, const char *mesg)
89279971Sobrien{
89379971Sobrien	if (argc == 1) {
89479971Sobrien		*var = !*var;
89579971Sobrien	} else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
89679971Sobrien		*var = 1;
89779971Sobrien	} else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
89879971Sobrien		*var = 0;
89979971Sobrien	} else {
900223328Sgavin		UPRINTF("usage: %s [ on | off ]\n", argv[0]);
90179971Sobrien		return (-1);
90279971Sobrien	}
90379971Sobrien	if (mesg)
90479971Sobrien		fprintf(ttyout, "%s %s.\n", mesg, onoff(*var));
90579971Sobrien	return (*var);
90679971Sobrien}
90779971Sobrien
90879971Sobrien/*
90979971Sobrien * Set beep on cmd completed mode.
91079971Sobrien */
91179971Sobrien/*VARARGS*/
91279971Sobrienvoid
91379971Sobriensetbell(int argc, char *argv[])
91479971Sobrien{
91579971Sobrien
91679971Sobrien	code = togglevar(argc, argv, &bell, "Bell mode");
91779971Sobrien}
91879971Sobrien
91979971Sobrien/*
92079971Sobrien * Set command line editing
92179971Sobrien */
92279971Sobrien/*VARARGS*/
92379971Sobrienvoid
92479971Sobriensetedit(int argc, char *argv[])
92579971Sobrien{
92679971Sobrien
92779971Sobrien#ifdef NO_EDITCOMPLETE
92879971Sobrien	if (argc == 0) {
929223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
93079971Sobrien		code = -1;
93179971Sobrien		return;
93279971Sobrien	}
93379971Sobrien	if (verbose)
93479971Sobrien		fputs("Editing support not compiled in; ignoring command.\n",
93579971Sobrien		    ttyout);
93679971Sobrien#else	/* !def NO_EDITCOMPLETE */
93779971Sobrien	code = togglevar(argc, argv, &editing, "Editing mode");
93879971Sobrien	controlediting();
93979971Sobrien#endif	/* !def NO_EDITCOMPLETE */
94079971Sobrien}
94179971Sobrien
94279971Sobrien/*
94379971Sobrien * Turn on packet tracing.
94479971Sobrien */
94579971Sobrien/*VARARGS*/
94679971Sobrienvoid
94779971Sobriensettrace(int argc, char *argv[])
94879971Sobrien{
94979971Sobrien
95079971Sobrien	code = togglevar(argc, argv, &trace, "Packet tracing");
95179971Sobrien}
95279971Sobrien
95379971Sobrien/*
95479971Sobrien * Toggle hash mark printing during transfers, or set hash mark bytecount.
95579971Sobrien */
95679971Sobrien/*VARARGS*/
95779971Sobrienvoid
95879971Sobriensethash(int argc, char *argv[])
95979971Sobrien{
96079971Sobrien	if (argc == 1)
96179971Sobrien		hash = !hash;
96279971Sobrien	else if (argc != 2) {
963223328Sgavin		UPRINTF("usage: %s [ on | off | bytecount ]\n",
96479971Sobrien		    argv[0]);
96579971Sobrien		code = -1;
96679971Sobrien		return;
96779971Sobrien	} else if (strcasecmp(argv[1], "on") == 0)
96879971Sobrien		hash = 1;
96979971Sobrien	else if (strcasecmp(argv[1], "off") == 0)
97079971Sobrien		hash = 0;
97179971Sobrien	else {
97279971Sobrien		int nmark;
97379971Sobrien
97479971Sobrien		nmark = strsuftoi(argv[1]);
97579971Sobrien		if (nmark < 1) {
97679971Sobrien			fprintf(ttyout, "mark: bad bytecount value `%s'.\n",
97779971Sobrien			    argv[1]);
97879971Sobrien			code = -1;
97979971Sobrien			return;
98079971Sobrien		}
98179971Sobrien		mark = nmark;
98279971Sobrien		hash = 1;
98379971Sobrien	}
98479971Sobrien	fprintf(ttyout, "Hash mark printing %s", onoff(hash));
98579971Sobrien	if (hash)
98679971Sobrien		fprintf(ttyout, " (%d bytes/hash mark)", mark);
98779971Sobrien	fputs(".\n", ttyout);
98879971Sobrien	if (hash)
98979971Sobrien		progress = 0;
99079971Sobrien	code = hash;
99179971Sobrien}
99279971Sobrien
99379971Sobrien/*
99479971Sobrien * Turn on printing of server echo's.
99579971Sobrien */
99679971Sobrien/*VARARGS*/
99779971Sobrienvoid
99879971Sobriensetverbose(int argc, char *argv[])
99979971Sobrien{
100079971Sobrien
100179971Sobrien	code = togglevar(argc, argv, &verbose, "Verbose mode");
100279971Sobrien}
100379971Sobrien
100479971Sobrien/*
100579971Sobrien * Toggle PORT/LPRT cmd use before each data connection.
100679971Sobrien */
100779971Sobrien/*VARARGS*/
100879971Sobrienvoid
100979971Sobriensetport(int argc, char *argv[])
101079971Sobrien{
101179971Sobrien
101279971Sobrien	code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds");
101379971Sobrien}
101479971Sobrien
101579971Sobrien/*
101679971Sobrien * Toggle transfer progress bar.
101779971Sobrien */
101879971Sobrien/*VARARGS*/
101979971Sobrienvoid
102079971Sobriensetprogress(int argc, char *argv[])
102179971Sobrien{
102279971Sobrien
102379971Sobrien	code = togglevar(argc, argv, &progress, "Progress bar");
102479971Sobrien	if (progress)
102579971Sobrien		hash = 0;
102679971Sobrien}
102779971Sobrien
102879971Sobrien/*
102979971Sobrien * Turn on interactive prompting during mget, mput, and mdelete.
103079971Sobrien */
103179971Sobrien/*VARARGS*/
103279971Sobrienvoid
103379971Sobriensetprompt(int argc, char *argv[])
103479971Sobrien{
103579971Sobrien
103679971Sobrien	code = togglevar(argc, argv, &interactive, "Interactive mode");
103779971Sobrien}
103879971Sobrien
103979971Sobrien/*
104079971Sobrien * Toggle gate-ftp mode, or set gate-ftp server
104179971Sobrien */
104279971Sobrien/*VARARGS*/
104379971Sobrienvoid
104479971Sobriensetgate(int argc, char *argv[])
104579971Sobrien{
104679971Sobrien	static char gsbuf[MAXHOSTNAMELEN];
104779971Sobrien
104879971Sobrien	if (argc == 0 || argc > 3) {
1049223328Sgavin		UPRINTF(
105079971Sobrien		    "usage: %s [ on | off | gateserver [port] ]\n", argv[0]);
105179971Sobrien		code = -1;
105279971Sobrien		return;
105379971Sobrien	} else if (argc < 2) {
105479971Sobrien		gatemode = !gatemode;
105579971Sobrien	} else {
105679971Sobrien		if (argc == 2 && strcasecmp(argv[1], "on") == 0)
105779971Sobrien			gatemode = 1;
105879971Sobrien		else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
105979971Sobrien			gatemode = 0;
106079971Sobrien		else {
106179971Sobrien			if (argc == 3)
1062223328Sgavin				gateport = ftp_strdup(argv[2]);
106379971Sobrien			(void)strlcpy(gsbuf, argv[1], sizeof(gsbuf));
106479971Sobrien			gateserver = gsbuf;
106579971Sobrien			gatemode = 1;
106679971Sobrien		}
106779971Sobrien	}
106879971Sobrien	if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
106979971Sobrien		fprintf(ttyout,
107079971Sobrien		    "Disabling gate-ftp mode - no gate-ftp server defined.\n");
107179971Sobrien		gatemode = 0;
107279971Sobrien	} else {
107379971Sobrien		fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n",
107479971Sobrien		    onoff(gatemode), *gateserver ? gateserver : "(none)",
107579971Sobrien		    gateport);
107679971Sobrien	}
107779971Sobrien	code = gatemode;
107879971Sobrien}
107979971Sobrien
108079971Sobrien/*
108179971Sobrien * Toggle metacharacter interpretation on local file names.
108279971Sobrien */
108379971Sobrien/*VARARGS*/
108479971Sobrienvoid
108579971Sobriensetglob(int argc, char *argv[])
108679971Sobrien{
108779971Sobrien
108879971Sobrien	code = togglevar(argc, argv, &doglob, "Globbing");
108979971Sobrien}
109079971Sobrien
109179971Sobrien/*
109279971Sobrien * Toggle preserving modification times on retrieved files.
109379971Sobrien */
109479971Sobrien/*VARARGS*/
109579971Sobrienvoid
109679971Sobriensetpreserve(int argc, char *argv[])
109779971Sobrien{
109879971Sobrien
109979971Sobrien	code = togglevar(argc, argv, &preserve, "Preserve modification times");
110079971Sobrien}
110179971Sobrien
110279971Sobrien/*
110379971Sobrien * Set debugging mode on/off and/or set level of debugging.
110479971Sobrien */
110579971Sobrien/*VARARGS*/
110679971Sobrienvoid
110779971Sobriensetdebug(int argc, char *argv[])
110879971Sobrien{
110979971Sobrien	if (argc == 0 || argc > 2) {
1110223328Sgavin		UPRINTF("usage: %s [ on | off | debuglevel ]\n", argv[0]);
111179971Sobrien		code = -1;
111279971Sobrien		return;
111379971Sobrien	} else if (argc == 2) {
111479971Sobrien		if (strcasecmp(argv[1], "on") == 0)
1115223328Sgavin			ftp_debug = 1;
111679971Sobrien		else if (strcasecmp(argv[1], "off") == 0)
1117223328Sgavin			ftp_debug = 0;
111879971Sobrien		else {
111979971Sobrien			int val;
112079971Sobrien
112179971Sobrien			val = strsuftoi(argv[1]);
112279971Sobrien			if (val < 0) {
112379971Sobrien				fprintf(ttyout, "%s: bad debugging value.\n",
112479971Sobrien				    argv[1]);
112579971Sobrien				code = -1;
112679971Sobrien				return;
112779971Sobrien			}
1128223328Sgavin			ftp_debug = val;
112979971Sobrien		}
113079971Sobrien	} else
1131223328Sgavin		ftp_debug = !ftp_debug;
1132223328Sgavin	if (ftp_debug)
113379971Sobrien		options |= SO_DEBUG;
113479971Sobrien	else
113579971Sobrien		options &= ~SO_DEBUG;
1136223328Sgavin	fprintf(ttyout, "Debugging %s (ftp_debug=%d).\n", onoff(ftp_debug), ftp_debug);
1137223328Sgavin	code = ftp_debug > 0;
113879971Sobrien}
113979971Sobrien
114079971Sobrien/*
114179971Sobrien * Set current working directory on remote machine.
114279971Sobrien */
114379971Sobrienvoid
114479971Sobriencd(int argc, char *argv[])
114579971Sobrien{
114679971Sobrien	int r;
114779971Sobrien
114879971Sobrien	if (argc == 0 || argc > 2 ||
114979971Sobrien	    (argc == 1 && !another(&argc, &argv, "remote-directory"))) {
1150223328Sgavin		UPRINTF("usage: %s remote-directory\n", argv[0]);
115179971Sobrien		code = -1;
115279971Sobrien		return;
115379971Sobrien	}
115479971Sobrien	r = command("CWD %s", argv[1]);
115579971Sobrien	if (r == ERROR && code == 500) {
115679971Sobrien		if (verbose)
115779971Sobrien			fputs("CWD command not recognized, trying XCWD.\n",
115879971Sobrien			    ttyout);
115979971Sobrien		r = command("XCWD %s", argv[1]);
116079971Sobrien	}
116179971Sobrien	if (r == COMPLETE) {
116279971Sobrien		dirchange = 1;
1163142129Smikeh		updateremotecwd();
116479971Sobrien	}
116579971Sobrien}
116679971Sobrien
116779971Sobrien/*
116879971Sobrien * Set current working directory on local machine.
116979971Sobrien */
117079971Sobrienvoid
117179971Sobrienlcd(int argc, char *argv[])
117279971Sobrien{
117379971Sobrien	char *locdir;
117479971Sobrien
117579971Sobrien	code = -1;
117679971Sobrien	if (argc == 1) {
117779971Sobrien		argc++;
117898247Smikeh		argv[1] = localhome;
117979971Sobrien	}
118079971Sobrien	if (argc != 2) {
1181223328Sgavin		UPRINTF("usage: %s [local-directory]\n", argv[0]);
118279971Sobrien		return;
118379971Sobrien	}
118479971Sobrien	if ((locdir = globulize(argv[1])) == NULL)
118579971Sobrien		return;
1186142129Smikeh	if (chdir(locdir) == -1)
1187223328Sgavin		warn("Can't chdir `%s'", locdir);
118879971Sobrien	else {
1189142129Smikeh		updatelocalcwd();
1190142129Smikeh		if (localcwd[0]) {
1191142129Smikeh			fprintf(ttyout, "Local directory now: %s\n", localcwd);
119279971Sobrien			code = 0;
1193142129Smikeh		} else {
1194142129Smikeh			fprintf(ttyout, "Unable to determine local directory\n");
1195142129Smikeh		}
119679971Sobrien	}
119779971Sobrien	(void)free(locdir);
119879971Sobrien}
119979971Sobrien
120079971Sobrien/*
120179971Sobrien * Delete a single file.
120279971Sobrien */
120379971Sobrienvoid
120479971Sobriendelete(int argc, char *argv[])
120579971Sobrien{
120679971Sobrien
120779971Sobrien	if (argc == 0 || argc > 2 ||
120879971Sobrien	    (argc == 1 && !another(&argc, &argv, "remote-file"))) {
1209223328Sgavin		UPRINTF("usage: %s remote-file\n", argv[0]);
121079971Sobrien		code = -1;
121179971Sobrien		return;
121279971Sobrien	}
1213121966Smikeh	if (command("DELE %s", argv[1]) == COMPLETE)
1214121966Smikeh		dirchange = 1;
121579971Sobrien}
121679971Sobrien
121779971Sobrien/*
121879971Sobrien * Delete multiple files.
121979971Sobrien */
122079971Sobrienvoid
122179971Sobrienmdelete(int argc, char *argv[])
122279971Sobrien{
122379971Sobrien	sigfunc oldintr;
122479971Sobrien	int ointer;
122579971Sobrien	char *cp;
122679971Sobrien
122779971Sobrien	if (argc == 0 ||
122879971Sobrien	    (argc == 1 && !another(&argc, &argv, "remote-files"))) {
1229223328Sgavin		UPRINTF("usage: %s [remote-files]\n", argv[0]);
123079971Sobrien		code = -1;
123179971Sobrien		return;
123279971Sobrien	}
123379971Sobrien	mflag = 1;
123479971Sobrien	oldintr = xsignal(SIGINT, mintr);
123579971Sobrien	if (sigsetjmp(jabort, 1))
1236223328Sgavin		mabort(argv[0]);
123779971Sobrien	while ((cp = remglob(argv, 0, NULL)) != NULL) {
123879971Sobrien		if (*cp == '\0') {
123979971Sobrien			mflag = 0;
124079971Sobrien			continue;
124179971Sobrien		}
124279971Sobrien		if (mflag && confirm(argv[0], cp)) {
1243121966Smikeh			if (command("DELE %s", cp) == COMPLETE)
1244121966Smikeh				dirchange = 1;
124579971Sobrien			if (!mflag && fromatty) {
124679971Sobrien				ointer = interactive;
124779971Sobrien				interactive = 1;
1248223328Sgavin				if (confirm(argv[0], NULL)) {
124979971Sobrien					mflag++;
125079971Sobrien				}
125179971Sobrien				interactive = ointer;
125279971Sobrien			}
125379971Sobrien		}
125479971Sobrien	}
125579971Sobrien	(void)xsignal(SIGINT, oldintr);
125679971Sobrien	mflag = 0;
125779971Sobrien}
125879971Sobrien
125979971Sobrien/*
126079971Sobrien * Rename a remote file.
126179971Sobrien */
126279971Sobrienvoid
126379971Sobrienrenamefile(int argc, char *argv[])
126479971Sobrien{
126579971Sobrien
126679971Sobrien	if (argc == 0 || (argc == 1 && !another(&argc, &argv, "from-name")))
126779971Sobrien		goto usage;
126879971Sobrien	if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
126979971Sobrien usage:
1270223328Sgavin		UPRINTF("usage: %s from-name to-name\n", argv[0]);
127179971Sobrien		code = -1;
127279971Sobrien		return;
127379971Sobrien	}
1274121966Smikeh	if (command("RNFR %s", argv[1]) == CONTINUE &&
1275121966Smikeh	    command("RNTO %s", argv[2]) == COMPLETE)
1276121966Smikeh		dirchange = 1;
127779971Sobrien}
127879971Sobrien
127979971Sobrien/*
128079971Sobrien * Get a directory listing of remote files.
128179971Sobrien * Supports being invoked as:
128279971Sobrien *	cmd		runs
128379971Sobrien *	---		----
128479971Sobrien *	dir, ls		LIST
128579971Sobrien *	mlsd		MLSD
128679971Sobrien *	nlist		NLST
128779971Sobrien *	pdir, pls	LIST |$PAGER
1288223328Sgavin *	pmlsd		MLSD |$PAGER
128979971Sobrien */
129079971Sobrienvoid
129179971Sobrienls(int argc, char *argv[])
129279971Sobrien{
129379971Sobrien	const char *cmd;
1294223328Sgavin	char *remdir, *locbuf;
1295223328Sgavin	const char *locfile;
1296223328Sgavin	int pagecmd, mlsdcmd;
129779971Sobrien
129879971Sobrien	remdir = NULL;
1299223328Sgavin	locbuf = NULL;
130079971Sobrien	locfile = "-";
1301223328Sgavin	pagecmd = mlsdcmd = 0;
130279971Sobrien			/*
130379971Sobrien			 * the only commands that start with `p' are
130479971Sobrien			 * the `pager' versions.
130579971Sobrien			 */
130679971Sobrien	if (argv[0][0] == 'p')
130779971Sobrien		pagecmd = 1;
130879971Sobrien	if (strcmp(argv[0] + pagecmd , "mlsd") == 0) {
130979971Sobrien		if (! features[FEAT_MLST]) {
131079971Sobrien			fprintf(ttyout,
131179971Sobrien			   "MLSD is not supported by the remote server.\n");
131279971Sobrien			return;
131379971Sobrien		}
131479971Sobrien		mlsdcmd = 1;
131579971Sobrien	}
131679971Sobrien	if (argc == 0)
131779971Sobrien		goto usage;
131879971Sobrien
131979971Sobrien	if (mlsdcmd)
132079971Sobrien		cmd = "MLSD";
132179971Sobrien	else if (strcmp(argv[0] + pagecmd, "nlist") == 0)
132279971Sobrien		cmd = "NLST";
132379971Sobrien	else
132479971Sobrien		cmd = "LIST";
132579971Sobrien
132679971Sobrien	if (argc > 1)
132779971Sobrien		remdir = argv[1];
132879971Sobrien	if (argc > 2)
132979971Sobrien		locfile = argv[2];
133079971Sobrien	if (argc > 3 || ((pagecmd | mlsdcmd) && argc > 2)) {
133179971Sobrien usage:
133279971Sobrien		if (pagecmd || mlsdcmd)
1333223328Sgavin			UPRINTF("usage: %s [remote-path]\n", argv[0]);
133479971Sobrien		else
1335223328Sgavin			UPRINTF("usage: %s [remote-path [local-file]]\n",
133679971Sobrien			    argv[0]);
133779971Sobrien		code = -1;
133879971Sobrien		goto freels;
133979971Sobrien	}
134079971Sobrien
134179971Sobrien	if (pagecmd) {
1342223328Sgavin		const char *p;
1343223328Sgavin		size_t len;
134479971Sobrien
134579971Sobrien		p = getoptionvalue("pager");
134679971Sobrien		if (EMPTYSTRING(p))
134779971Sobrien			p = DEFAULTPAGER;
134879971Sobrien		len = strlen(p) + 2;
1349223328Sgavin		locbuf = ftp_malloc(len);
1350223328Sgavin		locbuf[0] = '|';
1351223328Sgavin		(void)strlcpy(locbuf + 1, p, len - 1);
1352223328Sgavin		locfile = locbuf;
135379971Sobrien	} else if ((strcmp(locfile, "-") != 0) && *locfile != '|') {
1354223328Sgavin		if ((locbuf = globulize(locfile)) == NULL ||
1355223328Sgavin		    !confirm("output to local-file:", locbuf)) {
135679971Sobrien			code = -1;
135779971Sobrien			goto freels;
135879971Sobrien		}
1359223328Sgavin		locfile = locbuf;
136079971Sobrien	}
136179971Sobrien	recvrequest(cmd, locfile, remdir, "w", 0, 0);
136279971Sobrien freels:
1363223328Sgavin	if (locbuf)
1364223328Sgavin		(void)free(locbuf);
136579971Sobrien}
136679971Sobrien
136779971Sobrien/*
136879971Sobrien * Get a directory listing of multiple remote files.
136979971Sobrien */
137079971Sobrienvoid
137179971Sobrienmls(int argc, char *argv[])
137279971Sobrien{
137379971Sobrien	sigfunc oldintr;
137479971Sobrien	int ointer, i;
1375223328Sgavin	int volatile dolist;
1376223328Sgavin	char * volatile dest, *odest;
1377223328Sgavin	const char *lmode;
137879971Sobrien
137979971Sobrien	if (argc == 0)
138079971Sobrien		goto usage;
138179971Sobrien	if (argc < 2 && !another(&argc, &argv, "remote-files"))
138279971Sobrien		goto usage;
138379971Sobrien	if (argc < 3 && !another(&argc, &argv, "local-file")) {
138479971Sobrien usage:
1385223328Sgavin		UPRINTF("usage: %s remote-files local-file\n", argv[0]);
138679971Sobrien		code = -1;
138779971Sobrien		return;
138879971Sobrien	}
138979971Sobrien	odest = dest = argv[argc - 1];
139079971Sobrien	argv[argc - 1] = NULL;
139179971Sobrien	if (strcmp(dest, "-") && *dest != '|')
139279971Sobrien		if (((dest = globulize(dest)) == NULL) ||
139379971Sobrien		    !confirm("output to local-file:", dest)) {
139479971Sobrien			code = -1;
139579971Sobrien			return;
139679971Sobrien	}
139779971Sobrien	dolist = strcmp(argv[0], "mls");
139879971Sobrien	mflag = 1;
139979971Sobrien	oldintr = xsignal(SIGINT, mintr);
140079971Sobrien	if (sigsetjmp(jabort, 1))
1401223328Sgavin		mabort(argv[0]);
140279971Sobrien	for (i = 1; mflag && i < argc-1 && connected; i++) {
1403223328Sgavin		lmode = (i == 1) ? "w" : "a";
1404223328Sgavin		recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], lmode,
140579971Sobrien		    0, 0);
140679971Sobrien		if (!mflag && fromatty) {
140779971Sobrien			ointer = interactive;
140879971Sobrien			interactive = 1;
1409223328Sgavin			if (confirm(argv[0], NULL)) {
1410142129Smikeh				mflag++;
141179971Sobrien			}
141279971Sobrien			interactive = ointer;
141379971Sobrien		}
141479971Sobrien	}
141579971Sobrien	(void)xsignal(SIGINT, oldintr);
141679971Sobrien	mflag = 0;
141779971Sobrien	if (dest != odest)			/* free up after globulize() */
141879971Sobrien		free(dest);
141979971Sobrien}
142079971Sobrien
142179971Sobrien/*
142279971Sobrien * Do a shell escape
142379971Sobrien */
142479971Sobrien/*ARGSUSED*/
142579971Sobrienvoid
142679971Sobrienshell(int argc, char *argv[])
142779971Sobrien{
142879971Sobrien	pid_t pid;
1429142129Smikeh	sigfunc oldintr;
1430223328Sgavin	char shellnam[MAXPATHLEN];
1431223328Sgavin	const char *shellp, *namep;
143279971Sobrien	int wait_status;
143379971Sobrien
143479971Sobrien	if (argc == 0) {
1435223328Sgavin		UPRINTF("usage: %s [command [args]]\n", argv[0]);
143679971Sobrien		code = -1;
143779971Sobrien		return;
143879971Sobrien	}
1439142129Smikeh	oldintr = xsignal(SIGINT, SIG_IGN);
144079971Sobrien	if ((pid = fork()) == 0) {
144179971Sobrien		for (pid = 3; pid < 20; pid++)
144279971Sobrien			(void)close(pid);
144379971Sobrien		(void)xsignal(SIGINT, SIG_DFL);
1444223328Sgavin		shellp = getenv("SHELL");
1445223328Sgavin		if (shellp == NULL)
1446223328Sgavin			shellp = _PATH_BSHELL;
1447223328Sgavin		namep = strrchr(shellp, '/');
144879971Sobrien		if (namep == NULL)
1449223328Sgavin			namep = shellp;
145079971Sobrien		else
145179971Sobrien			namep++;
145279971Sobrien		(void)strlcpy(shellnam, namep, sizeof(shellnam));
1453223328Sgavin		if (ftp_debug) {
1454223328Sgavin			fputs(shellp, ttyout);
145579971Sobrien			putc('\n', ttyout);
145679971Sobrien		}
145779971Sobrien		if (argc > 1) {
1458223328Sgavin			execl(shellp, shellnam, "-c", altarg, (char *)0);
145979971Sobrien		}
146079971Sobrien		else {
1461223328Sgavin			execl(shellp, shellnam, (char *)0);
146279971Sobrien		}
1463223328Sgavin		warn("Can't execute `%s'", shellp);
146479971Sobrien		code = -1;
146579971Sobrien		exit(1);
146679971Sobrien	}
146779971Sobrien	if (pid > 0)
146879971Sobrien		while (wait(&wait_status) != pid)
146979971Sobrien			;
1470142129Smikeh	(void)xsignal(SIGINT, oldintr);
147179971Sobrien	if (pid == -1) {
1472223328Sgavin		warn("Can't fork a subshell; try again later");
147379971Sobrien		code = -1;
147479971Sobrien	} else
147579971Sobrien		code = 0;
147679971Sobrien}
147779971Sobrien
147879971Sobrien/*
147979971Sobrien * Send new user information (re-login)
148079971Sobrien */
148179971Sobrienvoid
148279971Sobrienuser(int argc, char *argv[])
148379971Sobrien{
1484223328Sgavin	char *password;
1485223328Sgavin	char emptypass[] = "";
148679971Sobrien	int n, aflag = 0;
148779971Sobrien
148879971Sobrien	if (argc == 0)
148979971Sobrien		goto usage;
149079971Sobrien	if (argc < 2)
149179971Sobrien		(void)another(&argc, &argv, "username");
149279971Sobrien	if (argc < 2 || argc > 4) {
149379971Sobrien usage:
1494223328Sgavin		UPRINTF("usage: %s username [password [account]]\n",
149579971Sobrien		    argv[0]);
149679971Sobrien		code = -1;
149779971Sobrien		return;
149879971Sobrien	}
149979971Sobrien	n = command("USER %s", argv[1]);
150079971Sobrien	if (n == CONTINUE) {
150179971Sobrien		if (argc < 3) {
1502223328Sgavin			password = getpass("Password: ");
1503223328Sgavin			if (password == NULL)
1504223328Sgavin				password = emptypass;
1505223328Sgavin		} else {
1506223328Sgavin			password = argv[2];
150779971Sobrien		}
1508223328Sgavin		n = command("PASS %s", password);
1509223328Sgavin		memset(password, 0, strlen(password));
151079971Sobrien	}
151179971Sobrien	if (n == CONTINUE) {
1512223328Sgavin		aflag++;
151379971Sobrien		if (argc < 4) {
1514223328Sgavin			password = getpass("Account: ");
1515223328Sgavin			if (password == NULL)
1516223328Sgavin				password = emptypass;
1517223328Sgavin		} else {
1518223328Sgavin			password = argv[3];
151979971Sobrien		}
1520223328Sgavin		n = command("ACCT %s", password);
1521223328Sgavin		memset(password, 0, strlen(password));
152279971Sobrien	}
152379971Sobrien	if (n != COMPLETE) {
152479971Sobrien		fputs("Login failed.\n", ttyout);
152579971Sobrien		return;
152679971Sobrien	}
152779971Sobrien	if (!aflag && argc == 4) {
1528223328Sgavin		password = argv[3];
1529223328Sgavin		(void)command("ACCT %s", password);
1530223328Sgavin		memset(password, 0, strlen(password));
153179971Sobrien	}
153279971Sobrien	connected = -1;
153379971Sobrien	getremoteinfo();
153479971Sobrien}
153579971Sobrien
153679971Sobrien/*
153779971Sobrien * Print working directory on remote machine.
153879971Sobrien */
153979971Sobrien/*VARARGS*/
154079971Sobrienvoid
154179971Sobrienpwd(int argc, char *argv[])
154279971Sobrien{
154379971Sobrien
1544142129Smikeh	code = -1;
1545142129Smikeh	if (argc != 1) {
1546223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
154779971Sobrien		return;
154879971Sobrien	}
1549142129Smikeh	if (! remotecwd[0])
1550142129Smikeh		updateremotecwd();
1551142129Smikeh	if (! remotecwd[0])
1552142129Smikeh		fprintf(ttyout, "Unable to determine remote directory\n");
1553142129Smikeh	else {
1554142129Smikeh		fprintf(ttyout, "Remote directory: %s\n", remotecwd);
1555142129Smikeh		code = 0;
155679971Sobrien	}
155779971Sobrien}
155879971Sobrien
155979971Sobrien/*
156079971Sobrien * Print working directory on local machine.
156179971Sobrien */
156279971Sobrienvoid
156379971Sobrienlpwd(int argc, char *argv[])
156479971Sobrien{
156579971Sobrien
1566142129Smikeh	code = -1;
1567142129Smikeh	if (argc != 1) {
1568223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
156979971Sobrien		return;
157079971Sobrien	}
1571142129Smikeh	if (! localcwd[0])
1572142129Smikeh		updatelocalcwd();
1573142129Smikeh	if (! localcwd[0])
1574142129Smikeh		fprintf(ttyout, "Unable to determine local directory\n");
1575142129Smikeh	else {
1576142129Smikeh		fprintf(ttyout, "Local directory: %s\n", localcwd);
157779971Sobrien		code = 0;
157879971Sobrien	}
157979971Sobrien}
158079971Sobrien
158179971Sobrien/*
158279971Sobrien * Make a directory.
158379971Sobrien */
158479971Sobrienvoid
158579971Sobrienmakedir(int argc, char *argv[])
158679971Sobrien{
1587121966Smikeh	int r;
158879971Sobrien
158979971Sobrien	if (argc == 0 || argc > 2 ||
159079971Sobrien	    (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1591223328Sgavin		UPRINTF("usage: %s directory-name\n", argv[0]);
159279971Sobrien		code = -1;
159379971Sobrien		return;
159479971Sobrien	}
1595121966Smikeh	r = command("MKD %s", argv[1]);
1596121966Smikeh	if (r == ERROR && code == 500) {
159779971Sobrien		if (verbose)
159879971Sobrien			fputs("MKD command not recognized, trying XMKD.\n",
159979971Sobrien			    ttyout);
1600121966Smikeh		r = command("XMKD %s", argv[1]);
160179971Sobrien	}
1602121966Smikeh	if (r == COMPLETE)
1603121966Smikeh		dirchange = 1;
160479971Sobrien}
160579971Sobrien
160679971Sobrien/*
160779971Sobrien * Remove a directory.
160879971Sobrien */
160979971Sobrienvoid
161079971Sobrienremovedir(int argc, char *argv[])
161179971Sobrien{
1612121966Smikeh	int r;
161379971Sobrien
161479971Sobrien	if (argc == 0 || argc > 2 ||
161579971Sobrien	    (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1616223328Sgavin		UPRINTF("usage: %s directory-name\n", argv[0]);
161779971Sobrien		code = -1;
161879971Sobrien		return;
161979971Sobrien	}
1620121966Smikeh	r = command("RMD %s", argv[1]);
1621121966Smikeh	if (r == ERROR && code == 500) {
162279971Sobrien		if (verbose)
162379971Sobrien			fputs("RMD command not recognized, trying XRMD.\n",
162479971Sobrien			    ttyout);
1625121966Smikeh		r = command("XRMD %s", argv[1]);
162679971Sobrien	}
1627121966Smikeh	if (r == COMPLETE)
1628121966Smikeh		dirchange = 1;
162979971Sobrien}
163079971Sobrien
163179971Sobrien/*
163279971Sobrien * Send a line, verbatim, to the remote machine.
163379971Sobrien */
163479971Sobrienvoid
163579971Sobrienquote(int argc, char *argv[])
163679971Sobrien{
163779971Sobrien
163879971Sobrien	if (argc == 0 ||
163979971Sobrien	    (argc == 1 && !another(&argc, &argv, "command line to send"))) {
1640223328Sgavin		UPRINTF("usage: %s line-to-send\n", argv[0]);
164179971Sobrien		code = -1;
164279971Sobrien		return;
164379971Sobrien	}
164479971Sobrien	quote1("", argc, argv);
164579971Sobrien}
164679971Sobrien
164779971Sobrien/*
164879971Sobrien * Send a SITE command to the remote machine.  The line
164979971Sobrien * is sent verbatim to the remote machine, except that the
165079971Sobrien * word "SITE" is added at the front.
165179971Sobrien */
165279971Sobrienvoid
165379971Sobriensite(int argc, char *argv[])
165479971Sobrien{
165579971Sobrien
165679971Sobrien	if (argc == 0 ||
165779971Sobrien	    (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){
1658223328Sgavin		UPRINTF("usage: %s line-to-send\n", argv[0]);
165979971Sobrien		code = -1;
166079971Sobrien		return;
166179971Sobrien	}
166279971Sobrien	quote1("SITE ", argc, argv);
166379971Sobrien}
166479971Sobrien
166579971Sobrien/*
166679971Sobrien * Turn argv[1..argc) into a space-separated string, then prepend initial text.
166779971Sobrien * Send the result as a one-line command and get response.
166879971Sobrien */
166979971Sobrienvoid
167079971Sobrienquote1(const char *initial, int argc, char *argv[])
167179971Sobrien{
167279971Sobrien	int i;
167379971Sobrien	char buf[BUFSIZ];		/* must be >= sizeof(line) */
167479971Sobrien
167579971Sobrien	(void)strlcpy(buf, initial, sizeof(buf));
167679971Sobrien	for (i = 1; i < argc; i++) {
167779971Sobrien		(void)strlcat(buf, argv[i], sizeof(buf));
167879971Sobrien		if (i < (argc - 1))
167979971Sobrien			(void)strlcat(buf, " ", sizeof(buf));
168079971Sobrien	}
168179971Sobrien	if (command("%s", buf) == PRELIM) {
168279971Sobrien		while (getreply(0) == PRELIM)
168379971Sobrien			continue;
168479971Sobrien	}
1685121966Smikeh	dirchange = 1;
168679971Sobrien}
168779971Sobrien
168879971Sobrienvoid
168979971Sobriendo_chmod(int argc, char *argv[])
169079971Sobrien{
169179971Sobrien
169279971Sobrien	if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode")))
169379971Sobrien		goto usage;
169479971Sobrien	if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
169579971Sobrien usage:
1696223328Sgavin		UPRINTF("usage: %s mode remote-file\n", argv[0]);
169779971Sobrien		code = -1;
169879971Sobrien		return;
169979971Sobrien	}
170079971Sobrien	(void)command("SITE CHMOD %s %s", argv[1], argv[2]);
170179971Sobrien}
170279971Sobrien
1703146309Smikeh#define COMMAND_1ARG(argc, argv, cmd)			\
170498247Smikeh	if (argc == 1)					\
170598247Smikeh		command(cmd);				\
170698247Smikeh	else						\
170798247Smikeh		command(cmd " %s", argv[1])
170898247Smikeh
170979971Sobrienvoid
171079971Sobriendo_umask(int argc, char *argv[])
171179971Sobrien{
171279971Sobrien	int oldverbose = verbose;
171379971Sobrien
171479971Sobrien	if (argc == 0) {
1715223328Sgavin		UPRINTF("usage: %s [umask]\n", argv[0]);
171679971Sobrien		code = -1;
171779971Sobrien		return;
171879971Sobrien	}
171979971Sobrien	verbose = 1;
172098247Smikeh	COMMAND_1ARG(argc, argv, "SITE UMASK");
172179971Sobrien	verbose = oldverbose;
172279971Sobrien}
172379971Sobrien
172479971Sobrienvoid
172579971Sobrienidlecmd(int argc, char *argv[])
172679971Sobrien{
172779971Sobrien	int oldverbose = verbose;
172879971Sobrien
172979971Sobrien	if (argc < 1 || argc > 2) {
1730223328Sgavin		UPRINTF("usage: %s [seconds]\n", argv[0]);
173179971Sobrien		code = -1;
173279971Sobrien		return;
173379971Sobrien	}
173479971Sobrien	verbose = 1;
173598247Smikeh	COMMAND_1ARG(argc, argv, "SITE IDLE");
173679971Sobrien	verbose = oldverbose;
173779971Sobrien}
173879971Sobrien
173979971Sobrien/*
174079971Sobrien * Ask the other side for help.
174179971Sobrien */
174279971Sobrienvoid
174379971Sobrienrmthelp(int argc, char *argv[])
174479971Sobrien{
174579971Sobrien	int oldverbose = verbose;
174679971Sobrien
174779971Sobrien	if (argc == 0) {
1748223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
174979971Sobrien		code = -1;
175079971Sobrien		return;
175179971Sobrien	}
175279971Sobrien	verbose = 1;
175398247Smikeh	COMMAND_1ARG(argc, argv, "HELP");
175479971Sobrien	verbose = oldverbose;
175579971Sobrien}
175679971Sobrien
175779971Sobrien/*
175879971Sobrien * Terminate session and exit.
175979971Sobrien * May be called with 0, NULL.
176079971Sobrien */
176179971Sobrien/*VARARGS*/
176279971Sobrienvoid
176379971Sobrienquit(int argc, char *argv[])
176479971Sobrien{
176579971Sobrien
176679971Sobrien			/* this may be called with argc == 0, argv == NULL */
176779971Sobrien	if (argc == 0 && argv != NULL) {
1768223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
176979971Sobrien		code = -1;
177079971Sobrien		return;
177179971Sobrien	}
177279971Sobrien	if (connected)
177379971Sobrien		disconnect(0, NULL);
177479971Sobrien	pswitch(1);
177579971Sobrien	if (connected)
177679971Sobrien		disconnect(0, NULL);
177779971Sobrien	exit(0);
177879971Sobrien}
177979971Sobrien
178079971Sobrien/*
178179971Sobrien * Terminate session, but don't exit.
178279971Sobrien * May be called with 0, NULL.
178379971Sobrien */
178479971Sobrienvoid
178579971Sobriendisconnect(int argc, char *argv[])
178679971Sobrien{
178779971Sobrien
178879971Sobrien			/* this may be called with argc == 0, argv == NULL */
178979971Sobrien	if (argc == 0 && argv != NULL) {
1790223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
179179971Sobrien		code = -1;
179279971Sobrien		return;
179379971Sobrien	}
179479971Sobrien	if (!connected)
179579971Sobrien		return;
179679971Sobrien	(void)command("QUIT");
179779971Sobrien	cleanuppeer();
179879971Sobrien}
179979971Sobrien
180079971Sobrienvoid
180179971Sobrienaccount(int argc, char *argv[])
180279971Sobrien{
180379971Sobrien	char *ap;
1804223328Sgavin	char emptypass[] = "";
180579971Sobrien
180679971Sobrien	if (argc == 0 || argc > 2) {
1807223328Sgavin		UPRINTF("usage: %s [password]\n", argv[0]);
180879971Sobrien		code = -1;
180979971Sobrien		return;
181079971Sobrien	}
181179971Sobrien	else if (argc == 2)
181279971Sobrien		ap = argv[1];
1813223328Sgavin	else {
181479971Sobrien		ap = getpass("Account:");
1815223328Sgavin		if (ap == NULL)
1816223328Sgavin			ap = emptypass;
1817223328Sgavin	}
181879971Sobrien	(void)command("ACCT %s", ap);
1819223328Sgavin	memset(ap, 0, strlen(ap));
182079971Sobrien}
182179971Sobrien
182279971Sobriensigjmp_buf abortprox;
182379971Sobrien
182479971Sobrienvoid
182579971Sobrienproxabort(int notused)
182679971Sobrien{
182779971Sobrien
1828142129Smikeh	sigint_raised = 1;
182979971Sobrien	alarmtimer(0);
183079971Sobrien	if (!proxy) {
183179971Sobrien		pswitch(1);
183279971Sobrien	}
183379971Sobrien	if (connected) {
183479971Sobrien		proxflag = 1;
183579971Sobrien	}
183679971Sobrien	else {
183779971Sobrien		proxflag = 0;
183879971Sobrien	}
183979971Sobrien	pswitch(0);
184079971Sobrien	siglongjmp(abortprox, 1);
184179971Sobrien}
184279971Sobrien
184379971Sobrienvoid
184479971Sobriendoproxy(int argc, char *argv[])
184579971Sobrien{
184679971Sobrien	struct cmd *c;
184779971Sobrien	int cmdpos;
184879971Sobrien	sigfunc oldintr;
1849223328Sgavin	char cmdbuf[MAX_C_NAME];
185079971Sobrien
185179971Sobrien	if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) {
1852223328Sgavin		UPRINTF("usage: %s command\n", argv[0]);
185379971Sobrien		code = -1;
185479971Sobrien		return;
185579971Sobrien	}
185679971Sobrien	c = getcmd(argv[1]);
185779971Sobrien	if (c == (struct cmd *) -1) {
185879971Sobrien		fputs("?Ambiguous command.\n", ttyout);
185979971Sobrien		code = -1;
186079971Sobrien		return;
186179971Sobrien	}
186279971Sobrien	if (c == 0) {
186379971Sobrien		fputs("?Invalid command.\n", ttyout);
186479971Sobrien		code = -1;
186579971Sobrien		return;
186679971Sobrien	}
186779971Sobrien	if (!c->c_proxy) {
186879971Sobrien		fputs("?Invalid proxy command.\n", ttyout);
186979971Sobrien		code = -1;
187079971Sobrien		return;
187179971Sobrien	}
187279971Sobrien	if (sigsetjmp(abortprox, 1)) {
187379971Sobrien		code = -1;
187479971Sobrien		return;
187579971Sobrien	}
187679971Sobrien	oldintr = xsignal(SIGINT, proxabort);
187779971Sobrien	pswitch(1);
187879971Sobrien	if (c->c_conn && !connected) {
187979971Sobrien		fputs("Not connected.\n", ttyout);
188079971Sobrien		pswitch(0);
188179971Sobrien		(void)xsignal(SIGINT, oldintr);
188279971Sobrien		code = -1;
188379971Sobrien		return;
188479971Sobrien	}
188579971Sobrien	cmdpos = strcspn(line, " \t");
188679971Sobrien	if (cmdpos > 0)		/* remove leading "proxy " from input buffer */
188779971Sobrien		memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1888223328Sgavin	(void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf));
1889223328Sgavin	argv[1] = cmdbuf;
189079971Sobrien	(*c->c_handler)(argc-1, argv+1);
189179971Sobrien	if (connected) {
189279971Sobrien		proxflag = 1;
189379971Sobrien	}
189479971Sobrien	else {
189579971Sobrien		proxflag = 0;
189679971Sobrien	}
189779971Sobrien	pswitch(0);
189879971Sobrien	(void)xsignal(SIGINT, oldintr);
189979971Sobrien}
190079971Sobrien
190179971Sobrienvoid
190279971Sobriensetcase(int argc, char *argv[])
190379971Sobrien{
190479971Sobrien
190579971Sobrien	code = togglevar(argc, argv, &mcase, "Case mapping");
190679971Sobrien}
190779971Sobrien
190879971Sobrien/*
190979971Sobrien * convert the given name to lower case if it's all upper case, into
191079971Sobrien * a static buffer which is returned to the caller
191179971Sobrien */
1912142129Smikehstatic const char *
1913142129Smikehdocase(char *dst, size_t dlen, const char *src)
191479971Sobrien{
1915142129Smikeh	size_t i;
1916142129Smikeh	int dochange = 1;
191779971Sobrien
1918142129Smikeh	for (i = 0; src[i] != '\0' && i < dlen - 1; i++) {
1919142129Smikeh		dst[i] = src[i];
1920142129Smikeh		if (islower((unsigned char)dst[i]))
192179971Sobrien			dochange = 0;
192279971Sobrien	}
1923142129Smikeh	dst[i] = '\0';
192479971Sobrien
192579971Sobrien	if (dochange) {
1926142129Smikeh		for (i = 0; dst[i] != '\0'; i++)
1927142129Smikeh			if (isupper((unsigned char)dst[i]))
1928142129Smikeh				dst[i] = tolower((unsigned char)dst[i]);
192979971Sobrien	}
1930142129Smikeh	return dst;
193179971Sobrien}
193279971Sobrien
193379971Sobrienvoid
193479971Sobriensetcr(int argc, char *argv[])
193579971Sobrien{
193679971Sobrien
193779971Sobrien	code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
193879971Sobrien}
193979971Sobrien
194079971Sobrienvoid
194179971Sobriensetntrans(int argc, char *argv[])
194279971Sobrien{
194379971Sobrien
194479971Sobrien	if (argc == 0 || argc > 3) {
1945223328Sgavin		UPRINTF("usage: %s [inchars [outchars]]\n", argv[0]);
194679971Sobrien		code = -1;
194779971Sobrien		return;
194879971Sobrien	}
194979971Sobrien	if (argc == 1) {
195079971Sobrien		ntflag = 0;
195179971Sobrien		fputs("Ntrans off.\n", ttyout);
195279971Sobrien		code = ntflag;
195379971Sobrien		return;
195479971Sobrien	}
195579971Sobrien	ntflag++;
195679971Sobrien	code = ntflag;
195779971Sobrien	(void)strlcpy(ntin, argv[1], sizeof(ntin));
195879971Sobrien	if (argc == 2) {
195979971Sobrien		ntout[0] = '\0';
196079971Sobrien		return;
196179971Sobrien	}
196279971Sobrien	(void)strlcpy(ntout, argv[2], sizeof(ntout));
196379971Sobrien}
196479971Sobrien
1965142129Smikehstatic const char *
1966142129Smikehdotrans(char *dst, size_t dlen, const char *src)
196779971Sobrien{
1968142129Smikeh	const char *cp1;
1969142129Smikeh	char *cp2 = dst;
1970142129Smikeh	size_t i, ostop;
197179971Sobrien
197279971Sobrien	for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
197379971Sobrien		continue;
1974142129Smikeh	for (cp1 = src; *cp1; cp1++) {
1975142129Smikeh		int found = 0;
197679971Sobrien		for (i = 0; *(ntin + i) && i < 16; i++) {
197779971Sobrien			if (*cp1 == *(ntin + i)) {
197879971Sobrien				found++;
197979971Sobrien				if (i < ostop) {
198079971Sobrien					*cp2++ = *(ntout + i);
1981223328Sgavin					if (cp2 - dst >= (ptrdiff_t)(dlen - 1))
1982142129Smikeh						goto out;
198379971Sobrien				}
198479971Sobrien				break;
198579971Sobrien			}
198679971Sobrien		}
198779971Sobrien		if (!found) {
198879971Sobrien			*cp2++ = *cp1;
198979971Sobrien		}
199079971Sobrien	}
1991142129Smikehout:
199279971Sobrien	*cp2 = '\0';
1993142129Smikeh	return dst;
199479971Sobrien}
199579971Sobrien
199679971Sobrienvoid
199779971Sobriensetnmap(int argc, char *argv[])
199879971Sobrien{
199979971Sobrien	char *cp;
200079971Sobrien
200179971Sobrien	if (argc == 1) {
200279971Sobrien		mapflag = 0;
200379971Sobrien		fputs("Nmap off.\n", ttyout);
200479971Sobrien		code = mapflag;
200579971Sobrien		return;
200679971Sobrien	}
200779971Sobrien	if (argc == 0 ||
200879971Sobrien	    (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
2009223328Sgavin		UPRINTF("usage: %s [mapin mapout]\n", argv[0]);
201079971Sobrien		code = -1;
201179971Sobrien		return;
201279971Sobrien	}
201379971Sobrien	mapflag = 1;
201479971Sobrien	code = 1;
201579971Sobrien	cp = strchr(altarg, ' ');
201679971Sobrien	if (proxy) {
201779971Sobrien		while(*++cp == ' ')
201879971Sobrien			continue;
201979971Sobrien		altarg = cp;
202079971Sobrien		cp = strchr(altarg, ' ');
202179971Sobrien	}
202279971Sobrien	*cp = '\0';
202379971Sobrien	(void)strlcpy(mapin, altarg, MAXPATHLEN);
202479971Sobrien	while (*++cp == ' ')
202579971Sobrien		continue;
202679971Sobrien	(void)strlcpy(mapout, cp, MAXPATHLEN);
202779971Sobrien}
202879971Sobrien
2029142129Smikehstatic const char *
2030142129Smikehdomap(char *dst, size_t dlen, const char *src)
203179971Sobrien{
2032142129Smikeh	const char *cp1 = src;
2033142129Smikeh	char *cp2 = mapin;
2034142129Smikeh	const char *tp[9], *te[9];
203579971Sobrien	int i, toks[9], toknum = 0, match = 1;
203679971Sobrien
203779971Sobrien	for (i=0; i < 9; ++i) {
203879971Sobrien		toks[i] = 0;
203979971Sobrien	}
204079971Sobrien	while (match && *cp1 && *cp2) {
204179971Sobrien		switch (*cp2) {
204279971Sobrien			case '\\':
204379971Sobrien				if (*++cp2 != *cp1) {
204479971Sobrien					match = 0;
204579971Sobrien				}
204679971Sobrien				break;
204779971Sobrien			case '$':
204879971Sobrien				if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
204979971Sobrien					if (*cp1 != *(++cp2+1)) {
205079971Sobrien						toks[toknum = *cp2 - '1']++;
205179971Sobrien						tp[toknum] = cp1;
205279971Sobrien						while (*++cp1 && *(cp2+1)
205379971Sobrien							!= *cp1);
205479971Sobrien						te[toknum] = cp1;
205579971Sobrien					}
205679971Sobrien					cp2++;
205779971Sobrien					break;
205879971Sobrien				}
205979971Sobrien				/* FALLTHROUGH */
206079971Sobrien			default:
206179971Sobrien				if (*cp2 != *cp1) {
206279971Sobrien					match = 0;
206379971Sobrien				}
206479971Sobrien				break;
206579971Sobrien		}
206679971Sobrien		if (match && *cp1) {
206779971Sobrien			cp1++;
206879971Sobrien		}
206979971Sobrien		if (match && *cp2) {
207079971Sobrien			cp2++;
207179971Sobrien		}
207279971Sobrien	}
207379971Sobrien	if (!match && *cp1) /* last token mismatch */
207479971Sobrien	{
207579971Sobrien		toks[toknum] = 0;
207679971Sobrien	}
2077142129Smikeh	cp2 = dst;
2078142129Smikeh	*cp2 = '\0';
2079142129Smikeh	cp1 = mapout;
2080142129Smikeh	while (*cp1) {
208179971Sobrien		match = 0;
2082142129Smikeh		switch (*cp1) {
208379971Sobrien			case '\\':
2084142129Smikeh				if (*(cp1 + 1)) {
2085142129Smikeh					*cp2++ = *++cp1;
208679971Sobrien				}
208779971Sobrien				break;
208879971Sobrien			case '[':
208979971SobrienLOOP:
2090142129Smikeh				if (*++cp1 == '$' &&
2091142129Smikeh				    isdigit((unsigned char)*(cp1+1))) {
2092142129Smikeh					if (*++cp1 == '0') {
2093142129Smikeh						const char *cp3 = src;
209479971Sobrien
209579971Sobrien						while (*cp3) {
2096142129Smikeh							*cp2++ = *cp3++;
209779971Sobrien						}
209879971Sobrien						match = 1;
209979971Sobrien					}
2100142129Smikeh					else if (toks[toknum = *cp1 - '1']) {
2101142129Smikeh						const char *cp3 = tp[toknum];
210279971Sobrien
210379971Sobrien						while (cp3 != te[toknum]) {
2104142129Smikeh							*cp2++ = *cp3++;
210579971Sobrien						}
210679971Sobrien						match = 1;
210779971Sobrien					}
210879971Sobrien				}
210979971Sobrien				else {
2110142129Smikeh					while (*cp1 && *cp1 != ',' &&
2111142129Smikeh					    *cp1 != ']') {
2112142129Smikeh						if (*cp1 == '\\') {
2113142129Smikeh							cp1++;
211479971Sobrien						}
2115142129Smikeh						else if (*cp1 == '$' &&
2116142129Smikeh						    isdigit((unsigned char)*(cp1+1))) {
2117142129Smikeh							if (*++cp1 == '0') {
2118142129Smikeh							   const char *cp3 = src;
211979971Sobrien
212079971Sobrien							   while (*cp3) {
2121142129Smikeh								*cp2++ = *cp3++;
212279971Sobrien							   }
212379971Sobrien							}
212479971Sobrien							else if (toks[toknum =
2125142129Smikeh							    *cp1 - '1']) {
2126142129Smikeh							   const char *cp3=tp[toknum];
212779971Sobrien
212879971Sobrien							   while (cp3 !=
212979971Sobrien								  te[toknum]) {
2130142129Smikeh								*cp2++ = *cp3++;
213179971Sobrien							   }
213279971Sobrien							}
213379971Sobrien						}
2134142129Smikeh						else if (*cp1) {
2135142129Smikeh							*cp2++ = *cp1++;
213679971Sobrien						}
213779971Sobrien					}
2138142129Smikeh					if (!*cp1) {
213979971Sobrien						fputs(
214079971Sobrien						"nmap: unbalanced brackets.\n",
214179971Sobrien						    ttyout);
2142142129Smikeh						return (src);
214379971Sobrien					}
214479971Sobrien					match = 1;
2145142129Smikeh					cp1--;
214679971Sobrien				}
214779971Sobrien				if (match) {
2148142129Smikeh					while (*++cp1 && *cp1 != ']') {
2149142129Smikeh					      if (*cp1 == '\\' && *(cp1 + 1)) {
2150142129Smikeh							cp1++;
215179971Sobrien					      }
215279971Sobrien					}
2153142129Smikeh					if (!*cp1) {
215479971Sobrien						fputs(
215579971Sobrien						"nmap: unbalanced brackets.\n",
215679971Sobrien						    ttyout);
2157142129Smikeh						return (src);
215879971Sobrien					}
215979971Sobrien					break;
216079971Sobrien				}
2161142129Smikeh				switch (*++cp1) {
216279971Sobrien					case ',':
216379971Sobrien						goto LOOP;
216479971Sobrien					case ']':
216579971Sobrien						break;
216679971Sobrien					default:
2167142129Smikeh						cp1--;
216879971Sobrien						goto LOOP;
216979971Sobrien				}
217079971Sobrien				break;
217179971Sobrien			case '$':
2172142129Smikeh				if (isdigit((unsigned char)*(cp1 + 1))) {
2173142129Smikeh					if (*++cp1 == '0') {
2174142129Smikeh						const char *cp3 = src;
217579971Sobrien
217679971Sobrien						while (*cp3) {
2177142129Smikeh							*cp2++ = *cp3++;
217879971Sobrien						}
217979971Sobrien					}
2180142129Smikeh					else if (toks[toknum = *cp1 - '1']) {
2181142129Smikeh						const char *cp3 = tp[toknum];
218279971Sobrien
218379971Sobrien						while (cp3 != te[toknum]) {
2184142129Smikeh							*cp2++ = *cp3++;
218579971Sobrien						}
218679971Sobrien					}
218779971Sobrien					break;
218879971Sobrien				}
218979971Sobrien				/* intentional drop through */
219079971Sobrien			default:
2191142129Smikeh				*cp2++ = *cp1;
219279971Sobrien				break;
219379971Sobrien		}
2194142129Smikeh		cp1++;
219579971Sobrien	}
2196142129Smikeh	*cp2 = '\0';
2197142129Smikeh	return *dst ? dst : src;
219879971Sobrien}
219979971Sobrien
220079971Sobrienvoid
220179971Sobriensetpassive(int argc, char *argv[])
220279971Sobrien{
220379971Sobrien
220479971Sobrien	if (argc == 1) {
220579971Sobrien		passivemode = !passivemode;
220679971Sobrien		activefallback = passivemode;
220779971Sobrien	} else if (argc != 2) {
220879971Sobrien passiveusage:
2209223328Sgavin		UPRINTF("usage: %s [ on | off | auto ]\n", argv[0]);
221079971Sobrien		code = -1;
221179971Sobrien		return;
221279971Sobrien	} else if (strcasecmp(argv[1], "on") == 0) {
221379971Sobrien		passivemode = 1;
221479971Sobrien		activefallback = 0;
221579971Sobrien	} else if (strcasecmp(argv[1], "off") == 0) {
221679971Sobrien		passivemode = 0;
221779971Sobrien		activefallback = 0;
221879971Sobrien	} else if (strcasecmp(argv[1], "auto") == 0) {
221979971Sobrien		passivemode = 1;
222079971Sobrien		activefallback = 1;
222179971Sobrien	} else
222279971Sobrien		goto passiveusage;
222379971Sobrien	fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
222479971Sobrien	    onoff(passivemode), onoff(activefallback));
222579971Sobrien	code = passivemode;
222679971Sobrien}
222779971Sobrien
2228223328Sgavin
222979971Sobrienvoid
223079971Sobriensetepsv4(int argc, char *argv[])
223179971Sobrien{
223279971Sobrien	code = togglevar(argc, argv, &epsv4,
223379971Sobrien	    verbose ? "EPSV/EPRT on IPv4" : NULL);
223479971Sobrien	epsv4bad = 0;
223579971Sobrien}
223679971Sobrien
223779971Sobrienvoid
2238223328Sgavinsetepsv6(int argc, char *argv[])
2239223328Sgavin{
2240223328Sgavin	code = togglevar(argc, argv, &epsv6,
2241223328Sgavin	    verbose ? "EPSV/EPRT on IPv6" : NULL);
2242223328Sgavin	epsv6bad = 0;
2243223328Sgavin}
2244223328Sgavin
2245223328Sgavinvoid
2246223328Sgavinsetepsv(int argc, char*argv[])
2247223328Sgavin{
2248223328Sgavin	setepsv4(argc,argv);
2249223328Sgavin	setepsv6(argc,argv);
2250223328Sgavin}
2251223328Sgavin
2252223328Sgavinvoid
225379971Sobriensetsunique(int argc, char *argv[])
225479971Sobrien{
225579971Sobrien
225679971Sobrien	code = togglevar(argc, argv, &sunique, "Store unique");
225779971Sobrien}
225879971Sobrien
225979971Sobrienvoid
226079971Sobriensetrunique(int argc, char *argv[])
226179971Sobrien{
226279971Sobrien
226379971Sobrien	code = togglevar(argc, argv, &runique, "Receive unique");
226479971Sobrien}
226579971Sobrien
226679971Sobrienint
226779971Sobrienparserate(int argc, char *argv[], int cmdlineopt)
226879971Sobrien{
226979971Sobrien	int dir, max, incr, showonly;
227079971Sobrien	sigfunc oldusr1, oldusr2;
227179971Sobrien
227279971Sobrien	if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) {
227379971Sobrien usage:
227479971Sobrien		if (cmdlineopt)
2275223328Sgavin			UPRINTF(
227679971Sobrien	"usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n",
227779971Sobrien			    argv[0]);
227879971Sobrien		else
2279223328Sgavin			UPRINTF(
228079971Sobrien	"usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n",
228179971Sobrien			    argv[0]);
228279971Sobrien		return -1;
228379971Sobrien	}
228479971Sobrien	dir = max = incr = showonly = 0;
228579971Sobrien#define	RATE_GET	1
228679971Sobrien#define	RATE_PUT	2
228779971Sobrien#define	RATE_ALL	(RATE_GET | RATE_PUT)
228879971Sobrien
228979971Sobrien	if (strcasecmp(argv[1], "all") == 0)
229079971Sobrien		dir = RATE_ALL;
229179971Sobrien	else if (strcasecmp(argv[1], "get") == 0)
229279971Sobrien		dir = RATE_GET;
229379971Sobrien	else if (strcasecmp(argv[1], "put") == 0)
229479971Sobrien		dir = RATE_PUT;
229579971Sobrien	else
229679971Sobrien		goto usage;
229779971Sobrien
229879971Sobrien	if (argc >= 3) {
229979971Sobrien		if ((max = strsuftoi(argv[2])) < 0)
230079971Sobrien			goto usage;
230179971Sobrien	} else
230279971Sobrien		showonly = 1;
230379971Sobrien
230479971Sobrien	if (argc == 4) {
230579971Sobrien		if ((incr = strsuftoi(argv[3])) <= 0)
230679971Sobrien			goto usage;
230779971Sobrien	} else
230879971Sobrien		incr = DEFAULTINCR;
230979971Sobrien
231079971Sobrien	oldusr1 = xsignal(SIGUSR1, SIG_IGN);
231179971Sobrien	oldusr2 = xsignal(SIGUSR2, SIG_IGN);
231279971Sobrien	if (dir & RATE_GET) {
231379971Sobrien		if (!showonly) {
231479971Sobrien			rate_get = max;
231579971Sobrien			rate_get_incr = incr;
231679971Sobrien		}
231779971Sobrien		if (!cmdlineopt || verbose)
231879971Sobrien			fprintf(ttyout,
231979971Sobrien		"Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
232079971Sobrien			    onoff(rate_get), rate_get, rate_get_incr);
232179971Sobrien	}
232279971Sobrien	if (dir & RATE_PUT) {
232379971Sobrien		if (!showonly) {
232479971Sobrien			rate_put = max;
232579971Sobrien			rate_put_incr = incr;
232679971Sobrien		}
232779971Sobrien		if (!cmdlineopt || verbose)
232879971Sobrien			fprintf(ttyout,
232979971Sobrien		"Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
233079971Sobrien			    onoff(rate_put), rate_put, rate_put_incr);
233179971Sobrien	}
233279971Sobrien	(void)xsignal(SIGUSR1, oldusr1);
233379971Sobrien	(void)xsignal(SIGUSR2, oldusr2);
233479971Sobrien	return 0;
233579971Sobrien}
233679971Sobrien
233779971Sobrienvoid
233879971Sobriensetrate(int argc, char *argv[])
233979971Sobrien{
234079971Sobrien
234179971Sobrien	code = parserate(argc, argv, 0);
234279971Sobrien}
234379971Sobrien
234479971Sobrien/* change directory to parent directory */
234579971Sobrienvoid
234679971Sobriencdup(int argc, char *argv[])
234779971Sobrien{
234879971Sobrien	int r;
234979971Sobrien
235079971Sobrien	if (argc == 0) {
2351223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
235279971Sobrien		code = -1;
235379971Sobrien		return;
235479971Sobrien	}
235579971Sobrien	r = command("CDUP");
235679971Sobrien	if (r == ERROR && code == 500) {
235779971Sobrien		if (verbose)
235879971Sobrien			fputs("CDUP command not recognized, trying XCUP.\n",
235979971Sobrien			    ttyout);
236079971Sobrien		r = command("XCUP");
236179971Sobrien	}
236279971Sobrien	if (r == COMPLETE) {
236379971Sobrien		dirchange = 1;
2364142129Smikeh		updateremotecwd();
236579971Sobrien	}
236679971Sobrien}
236779971Sobrien
236879971Sobrien/*
236979971Sobrien * Restart transfer at specific point
237079971Sobrien */
237179971Sobrienvoid
237279971Sobrienrestart(int argc, char *argv[])
237379971Sobrien{
237479971Sobrien
237579971Sobrien	if (argc == 0 || argc > 2) {
2376223328Sgavin		UPRINTF("usage: %s [restart-point]\n", argv[0]);
237779971Sobrien		code = -1;
237879971Sobrien		return;
237979971Sobrien	}
238079971Sobrien	if (! features[FEAT_REST_STREAM]) {
238179971Sobrien		fprintf(ttyout,
238279971Sobrien		    "Restart is not supported by the remote server.\n");
238379971Sobrien		return;
238479971Sobrien	}
238579971Sobrien	if (argc == 2) {
238679971Sobrien		off_t rp;
238779971Sobrien		char *ep;
238879971Sobrien
238979971Sobrien		rp = STRTOLL(argv[1], &ep, 10);
239079971Sobrien		if (rp < 0 || *ep != '\0')
239179971Sobrien			fprintf(ttyout, "restart: Invalid offset `%s'\n",
239279971Sobrien			    argv[1]);
239379971Sobrien		else
239479971Sobrien			restart_point = rp;
239579971Sobrien	}
239679971Sobrien	if (restart_point == 0)
239779971Sobrien		fputs("No restart point defined.\n", ttyout);
239879971Sobrien	else
239979971Sobrien		fprintf(ttyout,
240079971Sobrien		    "Restarting at " LLF " for next get, put or append\n",
240179971Sobrien		    (LLT)restart_point);
240279971Sobrien}
240379971Sobrien
240479971Sobrien/*
240579971Sobrien * Show remote system type
240679971Sobrien */
240779971Sobrienvoid
240879971Sobriensyst(int argc, char *argv[])
240979971Sobrien{
241079971Sobrien	int oldverbose = verbose;
241179971Sobrien
241279971Sobrien	if (argc == 0) {
2413223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
241479971Sobrien		code = -1;
241579971Sobrien		return;
241679971Sobrien	}
241779971Sobrien	verbose = 1;	/* If we aren't verbose, this doesn't do anything! */
241879971Sobrien	(void)command("SYST");
241979971Sobrien	verbose = oldverbose;
242079971Sobrien}
242179971Sobrien
242279971Sobrienvoid
242379971Sobrienmacdef(int argc, char *argv[])
242479971Sobrien{
242579971Sobrien	char *tmp;
242679971Sobrien	int c;
242779971Sobrien
242879971Sobrien	if (argc == 0)
242979971Sobrien		goto usage;
243079971Sobrien	if (macnum == 16) {
243179971Sobrien		fputs("Limit of 16 macros have already been defined.\n",
243279971Sobrien		    ttyout);
243379971Sobrien		code = -1;
243479971Sobrien		return;
243579971Sobrien	}
243679971Sobrien	if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
243779971Sobrien usage:
2438223328Sgavin		UPRINTF("usage: %s macro_name\n", argv[0]);
243979971Sobrien		code = -1;
244079971Sobrien		return;
244179971Sobrien	}
244279971Sobrien	if (interactive)
244379971Sobrien		fputs(
244479971Sobrien		"Enter macro line by line, terminating it with a null line.\n",
244579971Sobrien		    ttyout);
244679971Sobrien	(void)strlcpy(macros[macnum].mac_name, argv[1],
244779971Sobrien	    sizeof(macros[macnum].mac_name));
244879971Sobrien	if (macnum == 0)
244979971Sobrien		macros[macnum].mac_start = macbuf;
245079971Sobrien	else
245179971Sobrien		macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
245279971Sobrien	tmp = macros[macnum].mac_start;
245379971Sobrien	while (tmp != macbuf+4096) {
245479971Sobrien		if ((c = getchar()) == EOF) {
245579971Sobrien			fputs("macdef: end of file encountered.\n", ttyout);
245679971Sobrien			code = -1;
245779971Sobrien			return;
245879971Sobrien		}
245979971Sobrien		if ((*tmp = c) == '\n') {
246079971Sobrien			if (tmp == macros[macnum].mac_start) {
246179971Sobrien				macros[macnum++].mac_end = tmp;
246279971Sobrien				code = 0;
246379971Sobrien				return;
246479971Sobrien			}
246579971Sobrien			if (*(tmp-1) == '\0') {
246679971Sobrien				macros[macnum++].mac_end = tmp - 1;
246779971Sobrien				code = 0;
246879971Sobrien				return;
246979971Sobrien			}
247079971Sobrien			*tmp = '\0';
247179971Sobrien		}
247279971Sobrien		tmp++;
247379971Sobrien	}
247479971Sobrien	while (1) {
247579971Sobrien		while ((c = getchar()) != '\n' && c != EOF)
247679971Sobrien			/* LOOP */;
247779971Sobrien		if (c == EOF || getchar() == '\n') {
247879971Sobrien			fputs("Macro not defined - 4K buffer exceeded.\n",
247979971Sobrien			    ttyout);
248079971Sobrien			code = -1;
248179971Sobrien			return;
248279971Sobrien		}
248379971Sobrien	}
248479971Sobrien}
248579971Sobrien
248679971Sobrien/*
248779971Sobrien * Get size of file on remote machine
248879971Sobrien */
248979971Sobrienvoid
249079971Sobriensizecmd(int argc, char *argv[])
249179971Sobrien{
249279971Sobrien	off_t size;
249379971Sobrien
249479971Sobrien	if (argc == 0 || argc > 2 ||
249579971Sobrien	    (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2496223328Sgavin		UPRINTF("usage: %s remote-file\n", argv[0]);
249779971Sobrien		code = -1;
249879971Sobrien		return;
249979971Sobrien	}
250079971Sobrien	size = remotesize(argv[1], 1);
250179971Sobrien	if (size != -1)
250279971Sobrien		fprintf(ttyout,
250379971Sobrien		    "%s\t" LLF "\n", argv[1], (LLT)size);
250479971Sobrien	code = (size > 0);
250579971Sobrien}
250679971Sobrien
250779971Sobrien/*
250879971Sobrien * Get last modification time of file on remote machine
250979971Sobrien */
251079971Sobrienvoid
251179971Sobrienmodtime(int argc, char *argv[])
251279971Sobrien{
251379971Sobrien	time_t mtime;
251479971Sobrien
251579971Sobrien	if (argc == 0 || argc > 2 ||
251679971Sobrien	    (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2517223328Sgavin		UPRINTF("usage: %s remote-file\n", argv[0]);
251879971Sobrien		code = -1;
251979971Sobrien		return;
252079971Sobrien	}
252179971Sobrien	mtime = remotemodtime(argv[1], 1);
252279971Sobrien	if (mtime != -1)
2523223328Sgavin		fprintf(ttyout, "%s\t%s", argv[1],
2524223328Sgavin		    rfc2822time(localtime(&mtime)));
252579971Sobrien	code = (mtime > 0);
252679971Sobrien}
252779971Sobrien
252879971Sobrien/*
252979971Sobrien * Show status on remote machine
253079971Sobrien */
253179971Sobrienvoid
253279971Sobrienrmtstatus(int argc, char *argv[])
253379971Sobrien{
253479971Sobrien
253579971Sobrien	if (argc == 0) {
2536223328Sgavin		UPRINTF("usage: %s [remote-file]\n", argv[0]);
253779971Sobrien		code = -1;
253879971Sobrien		return;
253979971Sobrien	}
254098247Smikeh	COMMAND_1ARG(argc, argv, "STAT");
254179971Sobrien}
254279971Sobrien
254379971Sobrien/*
254479971Sobrien * Get file if modtime is more recent than current file
254579971Sobrien */
254679971Sobrienvoid
254779971Sobriennewer(int argc, char *argv[])
254879971Sobrien{
254979971Sobrien
255079971Sobrien	if (getit(argc, argv, -1, "w"))
255179971Sobrien		fprintf(ttyout,
255279971Sobrien		    "Local file \"%s\" is newer than remote file \"%s\".\n",
255379971Sobrien		    argv[2], argv[1]);
255479971Sobrien}
255579971Sobrien
255679971Sobrien/*
255779971Sobrien * Display one local file through $PAGER.
255879971Sobrien */
255979971Sobrienvoid
256079971Sobrienlpage(int argc, char *argv[])
256179971Sobrien{
2562223328Sgavin	size_t len;
2563223328Sgavin	const char *p;
2564223328Sgavin	char *pager, *locfile;
256579971Sobrien
256679971Sobrien	if (argc == 0 || argc > 2 ||
256779971Sobrien	    (argc == 1 && !another(&argc, &argv, "local-file"))) {
2568223328Sgavin		UPRINTF("usage: %s local-file\n", argv[0]);
256979971Sobrien		code = -1;
257079971Sobrien		return;
257179971Sobrien	}
257279971Sobrien	if ((locfile = globulize(argv[1])) == NULL) {
257379971Sobrien		code = -1;
257479971Sobrien		return;
257579971Sobrien	}
257679971Sobrien	p = getoptionvalue("pager");
257779971Sobrien	if (EMPTYSTRING(p))
257879971Sobrien		p = DEFAULTPAGER;
257979971Sobrien	len = strlen(p) + strlen(locfile) + 2;
2580223328Sgavin	pager = ftp_malloc(len);
258179971Sobrien	(void)strlcpy(pager, p,		len);
258279971Sobrien	(void)strlcat(pager, " ",	len);
258379971Sobrien	(void)strlcat(pager, locfile,	len);
258479971Sobrien	system(pager);
258579971Sobrien	code = 0;
258679971Sobrien	(void)free(pager);
258779971Sobrien	(void)free(locfile);
258879971Sobrien}
258979971Sobrien
259079971Sobrien/*
259179971Sobrien * Display one remote file through $PAGER.
259279971Sobrien */
259379971Sobrienvoid
259479971Sobrienpage(int argc, char *argv[])
259579971Sobrien{
2596223328Sgavin	int ohash, orestart_point, overbose;
2597223328Sgavin	size_t len;
2598223328Sgavin	const char *p;
2599223328Sgavin	char *pager;
260079971Sobrien
260179971Sobrien	if (argc == 0 || argc > 2 ||
260279971Sobrien	    (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2603223328Sgavin		UPRINTF("usage: %s remote-file\n", argv[0]);
260479971Sobrien		code = -1;
260579971Sobrien		return;
260679971Sobrien	}
260779971Sobrien	p = getoptionvalue("pager");
260879971Sobrien	if (EMPTYSTRING(p))
260979971Sobrien		p = DEFAULTPAGER;
261079971Sobrien	len = strlen(p) + 2;
2611223328Sgavin	pager = ftp_malloc(len);
261279971Sobrien	pager[0] = '|';
261379971Sobrien	(void)strlcpy(pager + 1, p, len - 1);
261479971Sobrien
261579971Sobrien	ohash = hash;
261679971Sobrien	orestart_point = restart_point;
261779971Sobrien	overbose = verbose;
261879971Sobrien	hash = restart_point = verbose = 0;
261998247Smikeh	recvrequest("RETR", pager, argv[1], "r+", 1, 0);
262079971Sobrien	hash = ohash;
262179971Sobrien	restart_point = orestart_point;
262279971Sobrien	verbose = overbose;
262379971Sobrien	(void)free(pager);
262479971Sobrien}
262579971Sobrien
262679971Sobrien/*
262779971Sobrien * Set the socket send or receive buffer size.
262879971Sobrien */
262979971Sobrienvoid
263079971Sobriensetxferbuf(int argc, char *argv[])
263179971Sobrien{
263279971Sobrien	int size, dir;
263379971Sobrien
263479971Sobrien	if (argc != 2) {
263579971Sobrien usage:
2636223328Sgavin		UPRINTF("usage: %s size\n", argv[0]);
263779971Sobrien		code = -1;
263879971Sobrien		return;
263979971Sobrien	}
264079971Sobrien	if (strcasecmp(argv[0], "sndbuf") == 0)
264179971Sobrien		dir = RATE_PUT;
264279971Sobrien	else if (strcasecmp(argv[0], "rcvbuf") == 0)
264379971Sobrien		dir = RATE_GET;
264479971Sobrien	else if (strcasecmp(argv[0], "xferbuf") == 0)
264579971Sobrien		dir = RATE_ALL;
264679971Sobrien	else
264779971Sobrien		goto usage;
264879971Sobrien
264979971Sobrien	if ((size = strsuftoi(argv[1])) == -1)
265079971Sobrien		goto usage;
265179971Sobrien
265279971Sobrien	if (size == 0) {
265379971Sobrien		fprintf(ttyout, "%s: size must be positive.\n", argv[0]);
265479971Sobrien		goto usage;
265579971Sobrien	}
265679971Sobrien
265779971Sobrien	if (dir & RATE_PUT)
265879971Sobrien		sndbuf_size = size;
265979971Sobrien	if (dir & RATE_GET)
266079971Sobrien		rcvbuf_size = size;
266179971Sobrien	fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n",
266279971Sobrien	    sndbuf_size, rcvbuf_size);
266379971Sobrien	code = 0;
266479971Sobrien}
266579971Sobrien
266679971Sobrien/*
266779971Sobrien * Set or display options (defaults are provided by various env vars)
266879971Sobrien */
266979971Sobrienvoid
267079971Sobriensetoption(int argc, char *argv[])
267179971Sobrien{
267279971Sobrien	struct option *o;
267379971Sobrien
267479971Sobrien	code = -1;
267579971Sobrien	if (argc == 0 || (argc != 1 && argc != 3)) {
2676223328Sgavin		UPRINTF("usage: %s [option value]\n", argv[0]);
267779971Sobrien		return;
267879971Sobrien	}
267979971Sobrien
268079971Sobrien#define	OPTIONINDENT ((int) sizeof("http_proxy"))
268179971Sobrien	if (argc == 1) {
268279971Sobrien		for (o = optiontab; o->name != NULL; o++) {
268379971Sobrien			fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT,
268479971Sobrien			    o->name, o->value ? o->value : "");
268579971Sobrien		}
268679971Sobrien	} else {
2687223328Sgavin		set_option(argv[1], argv[2], 1);
268879971Sobrien	}
268979971Sobrien	code = 0;
269079971Sobrien}
269179971Sobrien
2692223328Sgavinvoid
2693223328Sgavinset_option(const char * option, const char * value, int doverbose)
2694223328Sgavin{
2695223328Sgavin	struct option *o;
2696223328Sgavin
2697223328Sgavin	o = getoption(option);
2698223328Sgavin	if (o == NULL) {
2699223328Sgavin		fprintf(ttyout, "No such option `%s'.\n", option);
2700223328Sgavin		return;
2701223328Sgavin	}
2702223328Sgavin	FREEPTR(o->value);
2703223328Sgavin	o->value = ftp_strdup(value);
2704223328Sgavin	if (verbose && doverbose)
2705223328Sgavin		fprintf(ttyout, "Setting `%s' to `%s'.\n",
2706223328Sgavin		    o->name, o->value);
2707223328Sgavin}
2708223328Sgavin
270979971Sobrien/*
271079971Sobrien * Unset an option
271179971Sobrien */
271279971Sobrienvoid
271379971Sobrienunsetoption(int argc, char *argv[])
271479971Sobrien{
271579971Sobrien	struct option *o;
271679971Sobrien
271779971Sobrien	code = -1;
271879971Sobrien	if (argc == 0 || argc != 2) {
2719223328Sgavin		UPRINTF("usage: %s option\n", argv[0]);
272079971Sobrien		return;
272179971Sobrien	}
272279971Sobrien
272379971Sobrien	o = getoption(argv[1]);
272479971Sobrien	if (o == NULL) {
272579971Sobrien		fprintf(ttyout, "No such option `%s'.\n", argv[1]);
272679971Sobrien		return;
272779971Sobrien	}
272879971Sobrien	FREEPTR(o->value);
272979971Sobrien	fprintf(ttyout, "Unsetting `%s'.\n", o->name);
273079971Sobrien	code = 0;
273179971Sobrien}
273279971Sobrien
273379971Sobrien/*
273479971Sobrien * Display features supported by the remote host.
273579971Sobrien */
273679971Sobrienvoid
273779971Sobrienfeat(int argc, char *argv[])
273879971Sobrien{
273979971Sobrien	int oldverbose = verbose;
274079971Sobrien
274179971Sobrien	if (argc == 0) {
2742223328Sgavin		UPRINTF("usage: %s\n", argv[0]);
274379971Sobrien		code = -1;
274479971Sobrien		return;
274579971Sobrien	}
274679971Sobrien	if (! features[FEAT_FEAT]) {
274779971Sobrien		fprintf(ttyout,
274879971Sobrien		    "FEAT is not supported by the remote server.\n");
274979971Sobrien		return;
275079971Sobrien	}
275179971Sobrien	verbose = 1;	/* If we aren't verbose, this doesn't do anything! */
275279971Sobrien	(void)command("FEAT");
275379971Sobrien	verbose = oldverbose;
275479971Sobrien}
275579971Sobrien
275679971Sobrienvoid
275779971Sobrienmlst(int argc, char *argv[])
275879971Sobrien{
275979971Sobrien	int oldverbose = verbose;
276079971Sobrien
276179971Sobrien	if (argc < 1 || argc > 2) {
2762223328Sgavin		UPRINTF("usage: %s [remote-path]\n", argv[0]);
276379971Sobrien		code = -1;
276479971Sobrien		return;
276579971Sobrien	}
276679971Sobrien	if (! features[FEAT_MLST]) {
276779971Sobrien		fprintf(ttyout,
276879971Sobrien		    "MLST is not supported by the remote server.\n");
276979971Sobrien		return;
277079971Sobrien	}
277179971Sobrien	verbose = 1;	/* If we aren't verbose, this doesn't do anything! */
277298247Smikeh	COMMAND_1ARG(argc, argv, "MLST");
277379971Sobrien	verbose = oldverbose;
277479971Sobrien}
277579971Sobrien
277679971Sobrienvoid
277779971Sobrienopts(int argc, char *argv[])
277879971Sobrien{
277979971Sobrien	int oldverbose = verbose;
278079971Sobrien
278179971Sobrien	if (argc < 2 || argc > 3) {
2782223328Sgavin		UPRINTF("usage: %s command [options]\n", argv[0]);
278379971Sobrien		code = -1;
278479971Sobrien		return;
278579971Sobrien	}
278679971Sobrien	if (! features[FEAT_FEAT]) {
278779971Sobrien		fprintf(ttyout,
278879971Sobrien		    "OPTS is not supported by the remote server.\n");
278979971Sobrien		return;
279079971Sobrien	}
279179971Sobrien	verbose = 1;	/* If we aren't verbose, this doesn't do anything! */
279298247Smikeh	if (argc == 2)
279398247Smikeh		command("OPTS %s", argv[1]);
279498247Smikeh	else
279598247Smikeh		command("OPTS %s %s", argv[1], argv[2]);
279679971Sobrien	verbose = oldverbose;
279779971Sobrien}
2798