sftp.c revision 248619
11573Srgrimes/* $OpenBSD: sftp.c,v 1.142 2013/02/08 00:41:12 djm Exp $ */
21573Srgrimes/* $FreeBSD: head/crypto/openssh/sftp.c 248619 2013-03-22 17:55:38Z des $ */
31573Srgrimes/*
41573Srgrimes * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
51573Srgrimes *
61573Srgrimes * Permission to use, copy, modify, and distribute this software for any
71573Srgrimes * purpose with or without fee is hereby granted, provided that the above
81573Srgrimes * copyright notice and this permission notice appear in all copies.
91573Srgrimes *
101573Srgrimes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111573Srgrimes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
121573Srgrimes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
131573Srgrimes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
141573Srgrimes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151573Srgrimes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161573Srgrimes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171573Srgrimes */
181573Srgrimes
191573Srgrimes#include "includes.h"
201573Srgrimes
211573Srgrimes#include <sys/types.h>
221573Srgrimes#include <sys/ioctl.h>
231573Srgrimes#ifdef HAVE_SYS_STAT_H
241573Srgrimes# include <sys/stat.h>
251573Srgrimes#endif
261573Srgrimes#include <sys/param.h>
271573Srgrimes#include <sys/socket.h>
281573Srgrimes#include <sys/wait.h>
291573Srgrimes#ifdef HAVE_SYS_STATVFS_H
301573Srgrimes#include <sys/statvfs.h>
311573Srgrimes#endif
321573Srgrimes
3390045Sobrien#include <ctype.h>
3490045Sobrien#include <errno.h>
351573Srgrimes
3671579Sdeischen#ifdef HAVE_PATHS_H
371573Srgrimes# include <paths.h>
381573Srgrimes#endif
391573Srgrimes#ifdef HAVE_LIBGEN_H
401573Srgrimes#include <libgen.h>
41111618Snectar#endif
421573Srgrimes#ifdef USE_LIBEDIT
431573Srgrimes#include <histedit.h>
441573Srgrimes#else
451573Srgrimestypedef void EditLine;
461573Srgrimes#endif
471573Srgrimes#include <signal.h>
4871579Sdeischen#include <stdlib.h>
491573Srgrimes#include <stdio.h>
50292623Semaste#include <string.h>
51292623Semaste#include <unistd.h>
5228947Speter#include <stdarg.h>
53280219Sandrew
5436919Speter#ifdef HAVE_UTIL_H
5517141Sjkh# include <util.h>
5628947Speter#endif
5776224Sobrien
5876224Sobrien#include "xmalloc.h"
5928947Speter#include "log.h"
6028947Speter#include "pathnames.h"
6190045Sobrien#include "misc.h"
6290045Sobrien
6390045Sobrien#include "sftp.h"
64288028Srodrigc#include "buffer.h"
6528947Speter#include "sftp-common.h"
661573Srgrimes#include "sftp-client.h"
67287793Srodrigc
681573Srgrimes#define DEFAULT_COPY_BUFLEN	32768	/* Size of buffer for up/download */
691573Srgrimes#define DEFAULT_NUM_REQUESTS	256	/* # concurrent outstanding requests */
701573Srgrimes
71241046Sjilles/* File to read commands from */
721573SrgrimesFILE* infile;
731573Srgrimes
741573Srgrimes/* Are we in batchfile mode? */
7556698Sjasoneint batchmode = 0;
761573Srgrimes
771573Srgrimes/* PID of ssh transport process */
781573Srgrimesstatic pid_t sshpid = -1;
7928947Speter
8090045Sobrien/* This is set to 0 if the progressmeter is not desired. */
8128947Speterint showprogress = 1;
8228947Speter
8328947Speter/* When this option is set, we always recursively download/upload directories */
8428947Speterint global_rflag = 0;
8528947Speter
8628947Speter/* When this option is set, the file transfers will always preserve times */
8728947Speterint global_pflag = 0;
8828947Speter
891573Srgrimes/* SIGINT received during command processing */
901573Srgrimesvolatile sig_atomic_t interrupted = 0;
91287793Srodrigc
921573Srgrimes/* I wish qsort() took a separate ctx for the comparison function...*/
93287793Srodrigcint sort_flag;
94287793Srodrigc
9528947Speter/* Context used for commandline completion */
9628947Speterstruct complete_ctx {
9728947Speter	struct sftp_conn *conn;
9828947Speter	char **remote_pathp;
9928947Speter};
10028947Speter
10128947Speterint remote_glob(struct sftp_conn *, const char *, int,
10228947Speter    int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
10328947Speter
10428947Speterextern char *__progname;
10528947Speter
10628947Speter/* Separators for interactive commands */
10728947Speter#define WHITESPACE " \t\r\n"
108287793Srodrigc
10928947Speter/* ls flags */
11090045Sobrien#define LS_LONG_VIEW	0x0001	/* Full view ala ls -l */
11190045Sobrien#define LS_SHORT_VIEW	0x0002	/* Single row view ala ls -1 */
11290045Sobrien#define LS_NUMERIC_VIEW	0x0004	/* Long view with numeric uid/gid */
11390045Sobrien#define LS_NAME_SORT	0x0008	/* Sort by name (default) */
11490045Sobrien#define LS_TIME_SORT	0x0010	/* Sort by mtime */
1151794Scsgr#define LS_SIZE_SORT	0x0020	/* Sort by file size */
1161573Srgrimes#define LS_REVERSE_SORT	0x0040	/* Reverse sort order */
1171573Srgrimes#define LS_SHOW_ALL	0x0080	/* Don't skip filenames starting with '.' */
1181794Scsgr#define LS_SI_UNITS	0x0100	/* Display sizes as K, M, G, etc. */
11971579Sdeischen
1201573Srgrimes#define VIEW_FLAGS	(LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
1211573Srgrimes#define SORT_FLAGS	(LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
1221573Srgrimes
1231794Scsgr/* Commands for interactive mode */
1241573Srgrimes#define I_CHDIR		1
1251573Srgrimes#define I_CHGRP		2
1261573Srgrimes#define I_CHMOD		3
1271794Scsgr#define I_CHOWN		4
1281573Srgrimes#define I_DF		24
1291794Scsgr#define I_GET		5
1301794Scsgr#define I_HELP		6
1311794Scsgr#define I_LCHDIR	7
1321794Scsgr#define I_LINK		25
1331794Scsgr#define I_LLS		8
1341794Scsgr#define I_LMKDIR	9
1358870Srgrimes#define I_LPWD		10
1368870Srgrimes#define I_LS		11
1371794Scsgr#define I_LUMASK	12
1381573Srgrimes#define I_MKDIR		13
13921786Salex#define I_PUT		14
14021786Salex#define I_PWD		15
1411573Srgrimes#define I_QUIT		16
1421794Scsgr#define I_RENAME	17
1431794Scsgr#define I_RM		18
1441794Scsgr#define I_RMDIR		19
1451794Scsgr#define I_SHELL		20
1461794Scsgr#define I_SYMLINK	21
1471794Scsgr#define I_VERSION	22
1481794Scsgr#define I_PROGRESS	23
1491794Scsgr
1501794Scsgrstruct CMD {
1511794Scsgr	const char *c;
1521794Scsgr	const int n;
1531794Scsgr	const int t;
1541794Scsgr};
1551794Scsgr
1561794Scsgr/* Type of completion */
1571573Srgrimes#define NOARGS	0
1581573Srgrimes#define REMOTE	1
1591573Srgrimes#define LOCAL	2
1601573Srgrimes
1611573Srgrimesstatic const struct CMD cmds[] = {
1621573Srgrimes	{ "bye",	I_QUIT,		NOARGS	},
1631573Srgrimes	{ "cd",		I_CHDIR,	REMOTE	},
1641573Srgrimes	{ "chdir",	I_CHDIR,	REMOTE	},
1651573Srgrimes	{ "chgrp",	I_CHGRP,	REMOTE	},
1661573Srgrimes	{ "chmod",	I_CHMOD,	REMOTE	},
1671573Srgrimes	{ "chown",	I_CHOWN,	REMOTE	},
1681573Srgrimes	{ "df",		I_DF,		REMOTE	},
1691573Srgrimes	{ "dir",	I_LS,		REMOTE	},
1701573Srgrimes	{ "exit",	I_QUIT,		NOARGS	},
1711573Srgrimes	{ "get",	I_GET,		REMOTE	},
1721573Srgrimes	{ "help",	I_HELP,		NOARGS	},
1731573Srgrimes	{ "lcd",	I_LCHDIR,	LOCAL	},
1741573Srgrimes	{ "lchdir",	I_LCHDIR,	LOCAL	},
1751573Srgrimes	{ "lls",	I_LLS,		LOCAL	},
1761573Srgrimes	{ "lmkdir",	I_LMKDIR,	LOCAL	},
17790045Sobrien	{ "ln",		I_LINK,		REMOTE	},
1781573Srgrimes	{ "lpwd",	I_LPWD,		LOCAL	},
1791794Scsgr	{ "ls",		I_LS,		REMOTE	},
1801794Scsgr	{ "lumask",	I_LUMASK,	NOARGS	},
1811794Scsgr	{ "mkdir",	I_MKDIR,	REMOTE	},
1821794Scsgr	{ "mget",	I_GET,		REMOTE	},
1831794Scsgr	{ "mput",	I_PUT,		LOCAL	},
1841573Srgrimes	{ "progress",	I_PROGRESS,	NOARGS	},
1851573Srgrimes	{ "put",	I_PUT,		LOCAL	},
1861794Scsgr	{ "pwd",	I_PWD,		REMOTE	},
1871794Scsgr	{ "quit",	I_QUIT,		NOARGS	},
1881794Scsgr	{ "rename",	I_RENAME,	REMOTE	},
1891794Scsgr	{ "rm",		I_RM,		REMOTE	},
1901573Srgrimes	{ "rmdir",	I_RMDIR,	REMOTE	},
1911573Srgrimes	{ "symlink",	I_SYMLINK,	REMOTE	},
1921573Srgrimes	{ "version",	I_VERSION,	NOARGS	},
1931794Scsgr	{ "!",		I_SHELL,	NOARGS	},
1941573Srgrimes	{ "?",		I_HELP,		NOARGS	},
1951794Scsgr	{ NULL,		-1,		-1	}
1961573Srgrimes};
1971573Srgrimes
19828947Speterint interactive_loop(struct sftp_conn *, char *file1, char *file2);
19928947Speter
20028947Speter/* ARGSUSED */
20190045Sobrienstatic void
20238391Sdfrkillchild(int signo)
20328947Speter{
20428947Speter	if (sshpid > 1) {
20528947Speter		kill(sshpid, SIGTERM);
20628947Speter		waitpid(sshpid, NULL, 0);
207108533Sschweikh	}
208108533Sschweikh
20928947Speter	_exit(1);
21028947Speter}
211200150Sed
21228947Speter/* ARGSUSED */
21390045Sobrienstatic void
21428947Spetercmd_interrupt(int signo)
21528947Speter{
21628947Speter	const char msg[] = "\rInterrupt  \n";
21740688Sjdp	int olderrno = errno;
21828947Speter
21928947Speter	write(STDERR_FILENO, msg, sizeof(msg) - 1);
22028947Speter	interrupted = 1;
22128947Speter	errno = olderrno;
22228947Speter}
22328947Speter
22428947Speterstatic void
22528947Speterhelp(void)
22628947Speter{
22728947Speter	printf("Available commands:\n"
22828947Speter	    "bye                                Quit sftp\n"
22928947Speter	    "cd path                            Change remote directory to 'path'\n"
23028947Speter	    "chgrp grp path                     Change group of file 'path' to 'grp'\n"
23128947Speter	    "chmod mode path                    Change permissions of file 'path' to 'mode'\n"
23228947Speter	    "chown own path                     Change owner of file 'path' to 'own'\n"
23328947Speter	    "df [-hi] [path]                    Display statistics for current directory or\n"
234287793Srodrigc	    "                                   filesystem containing 'path'\n"
23528947Speter	    "exit                               Quit sftp\n"
23690045Sobrien	    "get [-Ppr] remote [local]          Download file\n"
23790045Sobrien	    "help                               Display this help text\n"
238153504Smarcel	    "lcd path                           Change local directory to 'path'\n"
239153504Smarcel	    "lls [ls-options [path]]            Display local directory listing\n"
24040688Sjdp	    "lmkdir path                        Create local directory\n"
24140688Sjdp	    "ln [-s] oldpath newpath            Link remote file (-s for symlink)\n"
24238391Sdfr	    "lpwd                               Print local working directory\n"
24338391Sdfr	    "ls [-1afhlnrSt] [path]             Display remote directory listing\n"
24438391Sdfr	    "lumask umask                       Set local umask to 'umask'\n"
24540688Sjdp	    "mkdir path                         Create remote directory\n"
24638391Sdfr	    "progress                           Toggle display of progress meter\n"
247153504Smarcel	    "put [-Ppr] local [remote]          Upload file\n"
24840688Sjdp	    "pwd                                Display remote working directory\n"
24928947Speter	    "quit                               Quit sftp\n"
25028947Speter	    "rename oldpath newpath             Rename remote file\n"
25128947Speter	    "rm path                            Delete remote file\n"
25228947Speter	    "rmdir path                         Remove remote directory\n"
25356698Sjasone	    "symlink oldpath newpath            Symlink remote file\n"
25428947Speter	    "version                            Show SFTP version\n"
25571579Sdeischen	    "!command                           Execute 'command' in local shell\n"
25628947Speter	    "!                                  Escape to local shell\n"
25728947Speter	    "?                                  Synonym for help\n");
25828947Speter}
25928947Speter
26028947Speterstatic void
26128947Speterlocal_do_shell(const char *args)
26228947Speter{
26328947Speter	int status;
26428947Speter	char *shell;
26528947Speter	pid_t pid;
26628947Speter
26728947Speter	if (!*args)
268271723Sbdrewery		args = NULL;
26940688Sjdp
27040688Sjdp	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
27128947Speter		shell = _PATH_BSHELL;
27240688Sjdp
27328947Speter	if ((pid = fork()) == -1)
27428947Speter		fatal("Couldn't fork: %s", strerror(errno));
27528947Speter
27628947Speter	if (pid == 0) {
27728947Speter		/* XXX: child has pipe fds to ssh subproc open - issue? */
27828947Speter		if (args) {
27928947Speter			debug3("Executing %s -c \"%s\"", shell, args);
28028947Speter			execl(shell, shell, "-c", args, (char *)NULL);
28128947Speter		} else {
28228947Speter			debug3("Executing %s", shell);
28328947Speter			execl(shell, shell, (char *)NULL);
28428947Speter		}
28528947Speter		fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
28628947Speter		    strerror(errno));
28728947Speter		_exit(1);
28828947Speter	}
28928947Speter	while (waitpid(pid, &status, 0) == -1)
29028947Speter		if (errno != EINTR)
29128947Speter			fatal("Couldn't wait for child: %s", strerror(errno));
29228947Speter	if (!WIFEXITED(status))
29340688Sjdp		error("Shell exited abnormally");
29428947Speter	else if (WEXITSTATUS(status))
29528947Speter		error("Shell exited with status %d", WEXITSTATUS(status));
29628947Speter}
29728947Speter
29828947Speterstatic void
29928947Speterlocal_do_ls(const char *args)
30028947Speter{
301271723Sbdrewery	if (!args || !*args)
30240688Sjdp		local_do_shell(_PATH_LS);
30340688Sjdp	else {
30440688Sjdp		int len = strlen(_PATH_LS " ") + strlen(args) + 1;
30540688Sjdp		char *buf = xmalloc(len);
30628947Speter
30728947Speter		/* XXX: quoting - rip quoting code from ftp? */
30828947Speter		snprintf(buf, len, _PATH_LS " %s", args);
30928947Speter		local_do_shell(buf);
31028947Speter		xfree(buf);
31128947Speter	}
31228947Speter}
31328947Speter
31428947Speter/* Strip one path (usually the pwd) from the start of another */
31528947Speterstatic char *
31628947Speterpath_strip(char *path, char *strip)
31728947Speter{
31828947Speter	size_t len;
31928947Speter
32028947Speter	if (strip == NULL)
32128947Speter		return (xstrdup(path));
32228947Speter
32328947Speter	len = strlen(strip);
32428947Speter	if (strncmp(path, strip, len) == 0) {
32528947Speter		if (strip[len - 1] != '/' && path[len] == '/')
32628947Speter			len++;
32728947Speter		return (xstrdup(path + len));
32828947Speter	}
32928947Speter
33028947Speter	return (xstrdup(path));
33128947Speter}
33228947Speter
33328947Speterstatic char *
33428947Spetermake_absolute(char *p, char *pwd)
33540688Sjdp{
33628947Speter	char *abs_str;
33756698Sjasone
33828947Speter	/* Derelativise */
33928947Speter	if (p && p[0] != '/') {
34040688Sjdp		abs_str = path_append(pwd, p);
34140688Sjdp		xfree(p);
34240688Sjdp		return(abs_str);
34328947Speter	} else
34440688Sjdp		return(p);
34540688Sjdp}
34628947Speter
34728947Speterstatic int
34828947Speterparse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
34940688Sjdp    int *rflag)
35040688Sjdp{
35140688Sjdp	extern int opterr, optind, optopt, optreset;
35240688Sjdp	int ch;
35328947Speter
35428947Speter	optind = optreset = 1;
35528947Speter	opterr = 0;
35628947Speter
35728947Speter	*rflag = *pflag = 0;
35828947Speter	while ((ch = getopt(argc, argv, "PpRr")) != -1) {
35928947Speter		switch (ch) {
36040688Sjdp		case 'p':
36140688Sjdp		case 'P':
36240688Sjdp			*pflag = 1;
36340688Sjdp			break;
36440688Sjdp		case 'r':
36540688Sjdp		case 'R':
36628947Speter			*rflag = 1;
36728947Speter			break;
36840688Sjdp		default:
36940688Sjdp			error("%s: Invalid flag -%c", cmd, optopt);
37040688Sjdp			return -1;
37140688Sjdp		}
37240688Sjdp	}
37340688Sjdp
374287793Srodrigc	return optind;
37540688Sjdp}
37640688Sjdp
37740688Sjdpstatic int
37840688Sjdpparse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
37940688Sjdp{
38040688Sjdp	extern int opterr, optind, optopt, optreset;
38140688Sjdp	int ch;
38240688Sjdp
38340688Sjdp	optind = optreset = 1;
38440688Sjdp	opterr = 0;
38540688Sjdp
38640688Sjdp	*sflag = 0;
38740688Sjdp	while ((ch = getopt(argc, argv, "s")) != -1) {
38840688Sjdp		switch (ch) {
38940688Sjdp		case 's':
39040688Sjdp			*sflag = 1;
39140688Sjdp			break;
39240688Sjdp		default:
39340688Sjdp			error("%s: Invalid flag -%c", cmd, optopt);
39440688Sjdp			return -1;
39540688Sjdp		}
39640688Sjdp	}
39740688Sjdp
39840688Sjdp	return optind;
39940688Sjdp}
40040688Sjdp
40140688Sjdpstatic int
40240688Sjdpparse_ls_flags(char **argv, int argc, int *lflag)
40340688Sjdp{
40428947Speter	extern int opterr, optind, optopt, optreset;
405	int ch;
406
407	optind = optreset = 1;
408	opterr = 0;
409
410	*lflag = LS_NAME_SORT;
411	while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
412		switch (ch) {
413		case '1':
414			*lflag &= ~VIEW_FLAGS;
415			*lflag |= LS_SHORT_VIEW;
416			break;
417		case 'S':
418			*lflag &= ~SORT_FLAGS;
419			*lflag |= LS_SIZE_SORT;
420			break;
421		case 'a':
422			*lflag |= LS_SHOW_ALL;
423			break;
424		case 'f':
425			*lflag &= ~SORT_FLAGS;
426			break;
427		case 'h':
428			*lflag |= LS_SI_UNITS;
429			break;
430		case 'l':
431			*lflag &= ~LS_SHORT_VIEW;
432			*lflag |= LS_LONG_VIEW;
433			break;
434		case 'n':
435			*lflag &= ~LS_SHORT_VIEW;
436			*lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
437			break;
438		case 'r':
439			*lflag |= LS_REVERSE_SORT;
440			break;
441		case 't':
442			*lflag &= ~SORT_FLAGS;
443			*lflag |= LS_TIME_SORT;
444			break;
445		default:
446			error("ls: Invalid flag -%c", optopt);
447			return -1;
448		}
449	}
450
451	return optind;
452}
453
454static int
455parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
456{
457	extern int opterr, optind, optopt, optreset;
458	int ch;
459
460	optind = optreset = 1;
461	opterr = 0;
462
463	*hflag = *iflag = 0;
464	while ((ch = getopt(argc, argv, "hi")) != -1) {
465		switch (ch) {
466		case 'h':
467			*hflag = 1;
468			break;
469		case 'i':
470			*iflag = 1;
471			break;
472		default:
473			error("%s: Invalid flag -%c", cmd, optopt);
474			return -1;
475		}
476	}
477
478	return optind;
479}
480
481static int
482is_dir(char *path)
483{
484	struct stat sb;
485
486	/* XXX: report errors? */
487	if (stat(path, &sb) == -1)
488		return(0);
489
490	return(S_ISDIR(sb.st_mode));
491}
492
493static int
494remote_is_dir(struct sftp_conn *conn, char *path)
495{
496	Attrib *a;
497
498	/* XXX: report errors? */
499	if ((a = do_stat(conn, path, 1)) == NULL)
500		return(0);
501	if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
502		return(0);
503	return(S_ISDIR(a->perm));
504}
505
506/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
507static int
508pathname_is_dir(char *pathname)
509{
510	size_t l = strlen(pathname);
511
512	return l > 0 && pathname[l - 1] == '/';
513}
514
515static int
516process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
517    int pflag, int rflag)
518{
519	char *abs_src = NULL;
520	char *abs_dst = NULL;
521	glob_t g;
522	char *filename, *tmp=NULL;
523	int i, err = 0;
524
525	abs_src = xstrdup(src);
526	abs_src = make_absolute(abs_src, pwd);
527	memset(&g, 0, sizeof(g));
528
529	debug3("Looking up %s", abs_src);
530	if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
531		error("File \"%s\" not found.", abs_src);
532		err = -1;
533		goto out;
534	}
535
536	/*
537	 * If multiple matches then dst must be a directory or
538	 * unspecified.
539	 */
540	if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
541		error("Multiple source paths, but destination "
542		    "\"%s\" is not a directory", dst);
543		err = -1;
544		goto out;
545	}
546
547	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
548		tmp = xstrdup(g.gl_pathv[i]);
549		if ((filename = basename(tmp)) == NULL) {
550			error("basename %s: %s", tmp, strerror(errno));
551			xfree(tmp);
552			err = -1;
553			goto out;
554		}
555
556		if (g.gl_matchc == 1 && dst) {
557			if (is_dir(dst)) {
558				abs_dst = path_append(dst, filename);
559			} else {
560				abs_dst = xstrdup(dst);
561			}
562		} else if (dst) {
563			abs_dst = path_append(dst, filename);
564		} else {
565			abs_dst = xstrdup(filename);
566		}
567		xfree(tmp);
568
569		printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
570		if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
571			if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
572			    pflag || global_pflag, 1) == -1)
573				err = -1;
574		} else {
575			if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
576			    pflag || global_pflag) == -1)
577				err = -1;
578		}
579		xfree(abs_dst);
580		abs_dst = NULL;
581	}
582
583out:
584	xfree(abs_src);
585	globfree(&g);
586	return(err);
587}
588
589static int
590process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
591    int pflag, int rflag)
592{
593	char *tmp_dst = NULL;
594	char *abs_dst = NULL;
595	char *tmp = NULL, *filename = NULL;
596	glob_t g;
597	int err = 0;
598	int i, dst_is_dir = 1;
599	struct stat sb;
600
601	if (dst) {
602		tmp_dst = xstrdup(dst);
603		tmp_dst = make_absolute(tmp_dst, pwd);
604	}
605
606	memset(&g, 0, sizeof(g));
607	debug3("Looking up %s", src);
608	if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
609		error("File \"%s\" not found.", src);
610		err = -1;
611		goto out;
612	}
613
614	/* If we aren't fetching to pwd then stash this status for later */
615	if (tmp_dst != NULL)
616		dst_is_dir = remote_is_dir(conn, tmp_dst);
617
618	/* If multiple matches, dst may be directory or unspecified */
619	if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
620		error("Multiple paths match, but destination "
621		    "\"%s\" is not a directory", tmp_dst);
622		err = -1;
623		goto out;
624	}
625
626	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
627		if (stat(g.gl_pathv[i], &sb) == -1) {
628			err = -1;
629			error("stat %s: %s", g.gl_pathv[i], strerror(errno));
630			continue;
631		}
632
633		tmp = xstrdup(g.gl_pathv[i]);
634		if ((filename = basename(tmp)) == NULL) {
635			error("basename %s: %s", tmp, strerror(errno));
636			xfree(tmp);
637			err = -1;
638			goto out;
639		}
640
641		if (g.gl_matchc == 1 && tmp_dst) {
642			/* If directory specified, append filename */
643			if (dst_is_dir)
644				abs_dst = path_append(tmp_dst, filename);
645			else
646				abs_dst = xstrdup(tmp_dst);
647		} else if (tmp_dst) {
648			abs_dst = path_append(tmp_dst, filename);
649		} else {
650			abs_dst = make_absolute(xstrdup(filename), pwd);
651		}
652		xfree(tmp);
653
654		printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
655		if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
656			if (upload_dir(conn, g.gl_pathv[i], abs_dst,
657			    pflag || global_pflag, 1) == -1)
658				err = -1;
659		} else {
660			if (do_upload(conn, g.gl_pathv[i], abs_dst,
661			    pflag || global_pflag) == -1)
662				err = -1;
663		}
664	}
665
666out:
667	if (abs_dst)
668		xfree(abs_dst);
669	if (tmp_dst)
670		xfree(tmp_dst);
671	globfree(&g);
672	return(err);
673}
674
675static int
676sdirent_comp(const void *aa, const void *bb)
677{
678	SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
679	SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
680	int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
681
682#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
683	if (sort_flag & LS_NAME_SORT)
684		return (rmul * strcmp(a->filename, b->filename));
685	else if (sort_flag & LS_TIME_SORT)
686		return (rmul * NCMP(a->a.mtime, b->a.mtime));
687	else if (sort_flag & LS_SIZE_SORT)
688		return (rmul * NCMP(a->a.size, b->a.size));
689
690	fatal("Unknown ls sort type");
691}
692
693/* sftp ls.1 replacement for directories */
694static int
695do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
696{
697	int n;
698	u_int c = 1, colspace = 0, columns = 1;
699	SFTP_DIRENT **d;
700
701	if ((n = do_readdir(conn, path, &d)) != 0)
702		return (n);
703
704	if (!(lflag & LS_SHORT_VIEW)) {
705		u_int m = 0, width = 80;
706		struct winsize ws;
707		char *tmp;
708
709		/* Count entries for sort and find longest filename */
710		for (n = 0; d[n] != NULL; n++) {
711			if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
712				m = MAX(m, strlen(d[n]->filename));
713		}
714
715		/* Add any subpath that also needs to be counted */
716		tmp = path_strip(path, strip_path);
717		m += strlen(tmp);
718		xfree(tmp);
719
720		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
721			width = ws.ws_col;
722
723		columns = width / (m + 2);
724		columns = MAX(columns, 1);
725		colspace = width / columns;
726		colspace = MIN(colspace, width);
727	}
728
729	if (lflag & SORT_FLAGS) {
730		for (n = 0; d[n] != NULL; n++)
731			;	/* count entries */
732		sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
733		qsort(d, n, sizeof(*d), sdirent_comp);
734	}
735
736	for (n = 0; d[n] != NULL && !interrupted; n++) {
737		char *tmp, *fname;
738
739		if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
740			continue;
741
742		tmp = path_append(path, d[n]->filename);
743		fname = path_strip(tmp, strip_path);
744		xfree(tmp);
745
746		if (lflag & LS_LONG_VIEW) {
747			if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
748				char *lname;
749				struct stat sb;
750
751				memset(&sb, 0, sizeof(sb));
752				attrib_to_stat(&d[n]->a, &sb);
753				lname = ls_file(fname, &sb, 1,
754				    (lflag & LS_SI_UNITS));
755				printf("%s\n", lname);
756				xfree(lname);
757			} else
758				printf("%s\n", d[n]->longname);
759		} else {
760			printf("%-*s", colspace, fname);
761			if (c >= columns) {
762				printf("\n");
763				c = 1;
764			} else
765				c++;
766		}
767
768		xfree(fname);
769	}
770
771	if (!(lflag & LS_LONG_VIEW) && (c != 1))
772		printf("\n");
773
774	free_sftp_dirents(d);
775	return (0);
776}
777
778/* sftp ls.1 replacement which handles path globs */
779static int
780do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
781    int lflag)
782{
783	char *fname, *lname;
784	glob_t g;
785	int err;
786	struct winsize ws;
787	u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
788
789	memset(&g, 0, sizeof(g));
790
791	if (remote_glob(conn, path,
792	    GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
793	    NULL, &g) ||
794	    (g.gl_pathc && !g.gl_matchc)) {
795		if (g.gl_pathc)
796			globfree(&g);
797		error("Can't ls: \"%s\" not found", path);
798		return -1;
799	}
800
801	if (interrupted)
802		goto out;
803
804	/*
805	 * If the glob returns a single match and it is a directory,
806	 * then just list its contents.
807	 */
808	if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
809	    S_ISDIR(g.gl_statv[0]->st_mode)) {
810		err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
811		globfree(&g);
812		return err;
813	}
814
815	if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
816		width = ws.ws_col;
817
818	if (!(lflag & LS_SHORT_VIEW)) {
819		/* Count entries for sort and find longest filename */
820		for (i = 0; g.gl_pathv[i]; i++)
821			m = MAX(m, strlen(g.gl_pathv[i]));
822
823		columns = width / (m + 2);
824		columns = MAX(columns, 1);
825		colspace = width / columns;
826	}
827
828	for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
829		fname = path_strip(g.gl_pathv[i], strip_path);
830		if (lflag & LS_LONG_VIEW) {
831			if (g.gl_statv[i] == NULL) {
832				error("no stat information for %s", fname);
833				continue;
834			}
835			lname = ls_file(fname, g.gl_statv[i], 1,
836			    (lflag & LS_SI_UNITS));
837			printf("%s\n", lname);
838			xfree(lname);
839		} else {
840			printf("%-*s", colspace, fname);
841			if (c >= columns) {
842				printf("\n");
843				c = 1;
844			} else
845				c++;
846		}
847		xfree(fname);
848	}
849
850	if (!(lflag & LS_LONG_VIEW) && (c != 1))
851		printf("\n");
852
853 out:
854	if (g.gl_pathc)
855		globfree(&g);
856
857	return 0;
858}
859
860static int
861do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
862{
863	struct sftp_statvfs st;
864	char s_used[FMT_SCALED_STRSIZE];
865	char s_avail[FMT_SCALED_STRSIZE];
866	char s_root[FMT_SCALED_STRSIZE];
867	char s_total[FMT_SCALED_STRSIZE];
868	unsigned long long ffree;
869
870	if (do_statvfs(conn, path, &st, 1) == -1)
871		return -1;
872	if (iflag) {
873		ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
874		printf("     Inodes        Used       Avail      "
875		    "(root)    %%Capacity\n");
876		printf("%11llu %11llu %11llu %11llu         %3llu%%\n",
877		    (unsigned long long)st.f_files,
878		    (unsigned long long)(st.f_files - st.f_ffree),
879		    (unsigned long long)st.f_favail,
880		    (unsigned long long)st.f_ffree, ffree);
881	} else if (hflag) {
882		strlcpy(s_used, "error", sizeof(s_used));
883		strlcpy(s_avail, "error", sizeof(s_avail));
884		strlcpy(s_root, "error", sizeof(s_root));
885		strlcpy(s_total, "error", sizeof(s_total));
886		fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
887		fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
888		fmt_scaled(st.f_bfree * st.f_frsize, s_root);
889		fmt_scaled(st.f_blocks * st.f_frsize, s_total);
890		printf("    Size     Used    Avail   (root)    %%Capacity\n");
891		printf("%7sB %7sB %7sB %7sB         %3llu%%\n",
892		    s_total, s_used, s_avail, s_root,
893		    (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
894		    st.f_blocks));
895	} else {
896		printf("        Size         Used        Avail       "
897		    "(root)    %%Capacity\n");
898		printf("%12llu %12llu %12llu %12llu         %3llu%%\n",
899		    (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
900		    (unsigned long long)(st.f_frsize *
901		    (st.f_blocks - st.f_bfree) / 1024),
902		    (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
903		    (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
904		    (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
905		    st.f_blocks));
906	}
907	return 0;
908}
909
910/*
911 * Undo escaping of glob sequences in place. Used to undo extra escaping
912 * applied in makeargv() when the string is destined for a function that
913 * does not glob it.
914 */
915static void
916undo_glob_escape(char *s)
917{
918	size_t i, j;
919
920	for (i = j = 0;;) {
921		if (s[i] == '\0') {
922			s[j] = '\0';
923			return;
924		}
925		if (s[i] != '\\') {
926			s[j++] = s[i++];
927			continue;
928		}
929		/* s[i] == '\\' */
930		++i;
931		switch (s[i]) {
932		case '?':
933		case '[':
934		case '*':
935		case '\\':
936			s[j++] = s[i++];
937			break;
938		case '\0':
939			s[j++] = '\\';
940			s[j] = '\0';
941			return;
942		default:
943			s[j++] = '\\';
944			s[j++] = s[i++];
945			break;
946		}
947	}
948}
949
950/*
951 * Split a string into an argument vector using sh(1)-style quoting,
952 * comment and escaping rules, but with some tweaks to handle glob(3)
953 * wildcards.
954 * The "sloppy" flag allows for recovery from missing terminating quote, for
955 * use in parsing incomplete commandlines during tab autocompletion.
956 *
957 * Returns NULL on error or a NULL-terminated array of arguments.
958 *
959 * If "lastquote" is not NULL, the quoting character used for the last
960 * argument is placed in *lastquote ("\0", "'" or "\"").
961 *
962 * If "terminated" is not NULL, *terminated will be set to 1 when the
963 * last argument's quote has been properly terminated or 0 otherwise.
964 * This parameter is only of use if "sloppy" is set.
965 */
966#define MAXARGS 	128
967#define MAXARGLEN	8192
968static char **
969makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
970    u_int *terminated)
971{
972	int argc, quot;
973	size_t i, j;
974	static char argvs[MAXARGLEN];
975	static char *argv[MAXARGS + 1];
976	enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
977
978	*argcp = argc = 0;
979	if (strlen(arg) > sizeof(argvs) - 1) {
980 args_too_longs:
981		error("string too long");
982		return NULL;
983	}
984	if (terminated != NULL)
985		*terminated = 1;
986	if (lastquote != NULL)
987		*lastquote = '\0';
988	state = MA_START;
989	i = j = 0;
990	for (;;) {
991		if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
992			error("Too many arguments.");
993			return NULL;
994		}
995		if (isspace(arg[i])) {
996			if (state == MA_UNQUOTED) {
997				/* Terminate current argument */
998				argvs[j++] = '\0';
999				argc++;
1000				state = MA_START;
1001			} else if (state != MA_START)
1002				argvs[j++] = arg[i];
1003		} else if (arg[i] == '"' || arg[i] == '\'') {
1004			q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1005			if (state == MA_START) {
1006				argv[argc] = argvs + j;
1007				state = q;
1008				if (lastquote != NULL)
1009					*lastquote = arg[i];
1010			} else if (state == MA_UNQUOTED)
1011				state = q;
1012			else if (state == q)
1013				state = MA_UNQUOTED;
1014			else
1015				argvs[j++] = arg[i];
1016		} else if (arg[i] == '\\') {
1017			if (state == MA_SQUOTE || state == MA_DQUOTE) {
1018				quot = state == MA_SQUOTE ? '\'' : '"';
1019				/* Unescape quote we are in */
1020				/* XXX support \n and friends? */
1021				if (arg[i + 1] == quot) {
1022					i++;
1023					argvs[j++] = arg[i];
1024				} else if (arg[i + 1] == '?' ||
1025				    arg[i + 1] == '[' || arg[i + 1] == '*') {
1026					/*
1027					 * Special case for sftp: append
1028					 * double-escaped glob sequence -
1029					 * glob will undo one level of
1030					 * escaping. NB. string can grow here.
1031					 */
1032					if (j >= sizeof(argvs) - 5)
1033						goto args_too_longs;
1034					argvs[j++] = '\\';
1035					argvs[j++] = arg[i++];
1036					argvs[j++] = '\\';
1037					argvs[j++] = arg[i];
1038				} else {
1039					argvs[j++] = arg[i++];
1040					argvs[j++] = arg[i];
1041				}
1042			} else {
1043				if (state == MA_START) {
1044					argv[argc] = argvs + j;
1045					state = MA_UNQUOTED;
1046					if (lastquote != NULL)
1047						*lastquote = '\0';
1048				}
1049				if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1050				    arg[i + 1] == '*' || arg[i + 1] == '\\') {
1051					/*
1052					 * Special case for sftp: append
1053					 * escaped glob sequence -
1054					 * glob will undo one level of
1055					 * escaping.
1056					 */
1057					argvs[j++] = arg[i++];
1058					argvs[j++] = arg[i];
1059				} else {
1060					/* Unescape everything */
1061					/* XXX support \n and friends? */
1062					i++;
1063					argvs[j++] = arg[i];
1064				}
1065			}
1066		} else if (arg[i] == '#') {
1067			if (state == MA_SQUOTE || state == MA_DQUOTE)
1068				argvs[j++] = arg[i];
1069			else
1070				goto string_done;
1071		} else if (arg[i] == '\0') {
1072			if (state == MA_SQUOTE || state == MA_DQUOTE) {
1073				if (sloppy) {
1074					state = MA_UNQUOTED;
1075					if (terminated != NULL)
1076						*terminated = 0;
1077					goto string_done;
1078				}
1079				error("Unterminated quoted argument");
1080				return NULL;
1081			}
1082 string_done:
1083			if (state == MA_UNQUOTED) {
1084				argvs[j++] = '\0';
1085				argc++;
1086			}
1087			break;
1088		} else {
1089			if (state == MA_START) {
1090				argv[argc] = argvs + j;
1091				state = MA_UNQUOTED;
1092				if (lastquote != NULL)
1093					*lastquote = '\0';
1094			}
1095			if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1096			    (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1097				/*
1098				 * Special case for sftp: escape quoted
1099				 * glob(3) wildcards. NB. string can grow
1100				 * here.
1101				 */
1102				if (j >= sizeof(argvs) - 3)
1103					goto args_too_longs;
1104				argvs[j++] = '\\';
1105				argvs[j++] = arg[i];
1106			} else
1107				argvs[j++] = arg[i];
1108		}
1109		i++;
1110	}
1111	*argcp = argc;
1112	return argv;
1113}
1114
1115static int
1116parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1117    int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2)
1118{
1119	const char *cmd, *cp = *cpp;
1120	char *cp2, **argv;
1121	int base = 0;
1122	long l;
1123	int i, cmdnum, optidx, argc;
1124
1125	/* Skip leading whitespace */
1126	cp = cp + strspn(cp, WHITESPACE);
1127
1128	/* Check for leading '-' (disable error processing) */
1129	*iflag = 0;
1130	if (*cp == '-') {
1131		*iflag = 1;
1132		cp++;
1133		cp = cp + strspn(cp, WHITESPACE);
1134	}
1135
1136	/* Ignore blank lines and lines which begin with comment '#' char */
1137	if (*cp == '\0' || *cp == '#')
1138		return (0);
1139
1140	if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1141		return -1;
1142
1143	/* Figure out which command we have */
1144	for (i = 0; cmds[i].c != NULL; i++) {
1145		if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1146			break;
1147	}
1148	cmdnum = cmds[i].n;
1149	cmd = cmds[i].c;
1150
1151	/* Special case */
1152	if (*cp == '!') {
1153		cp++;
1154		cmdnum = I_SHELL;
1155	} else if (cmdnum == -1) {
1156		error("Invalid command.");
1157		return -1;
1158	}
1159
1160	/* Get arguments and parse flags */
1161	*lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1162	*path1 = *path2 = NULL;
1163	optidx = 1;
1164	switch (cmdnum) {
1165	case I_GET:
1166	case I_PUT:
1167		if ((optidx = parse_getput_flags(cmd, argv, argc,
1168		    pflag, rflag)) == -1)
1169			return -1;
1170		/* Get first pathname (mandatory) */
1171		if (argc - optidx < 1) {
1172			error("You must specify at least one path after a "
1173			    "%s command.", cmd);
1174			return -1;
1175		}
1176		*path1 = xstrdup(argv[optidx]);
1177		/* Get second pathname (optional) */
1178		if (argc - optidx > 1) {
1179			*path2 = xstrdup(argv[optidx + 1]);
1180			/* Destination is not globbed */
1181			undo_glob_escape(*path2);
1182		}
1183		break;
1184	case I_LINK:
1185		if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1186			return -1;
1187	case I_SYMLINK:
1188	case I_RENAME:
1189		if (argc - optidx < 2) {
1190			error("You must specify two paths after a %s "
1191			    "command.", cmd);
1192			return -1;
1193		}
1194		*path1 = xstrdup(argv[optidx]);
1195		*path2 = xstrdup(argv[optidx + 1]);
1196		/* Paths are not globbed */
1197		undo_glob_escape(*path1);
1198		undo_glob_escape(*path2);
1199		break;
1200	case I_RM:
1201	case I_MKDIR:
1202	case I_RMDIR:
1203	case I_CHDIR:
1204	case I_LCHDIR:
1205	case I_LMKDIR:
1206		/* Get pathname (mandatory) */
1207		if (argc - optidx < 1) {
1208			error("You must specify a path after a %s command.",
1209			    cmd);
1210			return -1;
1211		}
1212		*path1 = xstrdup(argv[optidx]);
1213		/* Only "rm" globs */
1214		if (cmdnum != I_RM)
1215			undo_glob_escape(*path1);
1216		break;
1217	case I_DF:
1218		if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1219		    iflag)) == -1)
1220			return -1;
1221		/* Default to current directory if no path specified */
1222		if (argc - optidx < 1)
1223			*path1 = NULL;
1224		else {
1225			*path1 = xstrdup(argv[optidx]);
1226			undo_glob_escape(*path1);
1227		}
1228		break;
1229	case I_LS:
1230		if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1231			return(-1);
1232		/* Path is optional */
1233		if (argc - optidx > 0)
1234			*path1 = xstrdup(argv[optidx]);
1235		break;
1236	case I_LLS:
1237		/* Skip ls command and following whitespace */
1238		cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1239	case I_SHELL:
1240		/* Uses the rest of the line */
1241		break;
1242	case I_LUMASK:
1243	case I_CHMOD:
1244		base = 8;
1245	case I_CHOWN:
1246	case I_CHGRP:
1247		/* Get numeric arg (mandatory) */
1248		if (argc - optidx < 1)
1249			goto need_num_arg;
1250		errno = 0;
1251		l = strtol(argv[optidx], &cp2, base);
1252		if (cp2 == argv[optidx] || *cp2 != '\0' ||
1253		    ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1254		    l < 0) {
1255 need_num_arg:
1256			error("You must supply a numeric argument "
1257			    "to the %s command.", cmd);
1258			return -1;
1259		}
1260		*n_arg = l;
1261		if (cmdnum == I_LUMASK)
1262			break;
1263		/* Get pathname (mandatory) */
1264		if (argc - optidx < 2) {
1265			error("You must specify a path after a %s command.",
1266			    cmd);
1267			return -1;
1268		}
1269		*path1 = xstrdup(argv[optidx + 1]);
1270		break;
1271	case I_QUIT:
1272	case I_PWD:
1273	case I_LPWD:
1274	case I_HELP:
1275	case I_VERSION:
1276	case I_PROGRESS:
1277		break;
1278	default:
1279		fatal("Command not implemented");
1280	}
1281
1282	*cpp = cp;
1283	return(cmdnum);
1284}
1285
1286static int
1287parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1288    int err_abort)
1289{
1290	char *path1, *path2, *tmp;
1291	int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0;
1292	int cmdnum, i;
1293	unsigned long n_arg = 0;
1294	Attrib a, *aa;
1295	char path_buf[MAXPATHLEN];
1296	int err = 0;
1297	glob_t g;
1298
1299	path1 = path2 = NULL;
1300	cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag,
1301	    &sflag, &n_arg, &path1, &path2);
1302
1303	if (iflag != 0)
1304		err_abort = 0;
1305
1306	memset(&g, 0, sizeof(g));
1307
1308	/* Perform command */
1309	switch (cmdnum) {
1310	case 0:
1311		/* Blank line */
1312		break;
1313	case -1:
1314		/* Unrecognized command */
1315		err = -1;
1316		break;
1317	case I_GET:
1318		err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1319		break;
1320	case I_PUT:
1321		err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1322		break;
1323	case I_RENAME:
1324		path1 = make_absolute(path1, *pwd);
1325		path2 = make_absolute(path2, *pwd);
1326		err = do_rename(conn, path1, path2);
1327		break;
1328	case I_SYMLINK:
1329		sflag = 1;
1330	case I_LINK:
1331		path1 = make_absolute(path1, *pwd);
1332		path2 = make_absolute(path2, *pwd);
1333		err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1334		break;
1335	case I_RM:
1336		path1 = make_absolute(path1, *pwd);
1337		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1338		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1339			printf("Removing %s\n", g.gl_pathv[i]);
1340			err = do_rm(conn, g.gl_pathv[i]);
1341			if (err != 0 && err_abort)
1342				break;
1343		}
1344		break;
1345	case I_MKDIR:
1346		path1 = make_absolute(path1, *pwd);
1347		attrib_clear(&a);
1348		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1349		a.perm = 0777;
1350		err = do_mkdir(conn, path1, &a, 1);
1351		break;
1352	case I_RMDIR:
1353		path1 = make_absolute(path1, *pwd);
1354		err = do_rmdir(conn, path1);
1355		break;
1356	case I_CHDIR:
1357		path1 = make_absolute(path1, *pwd);
1358		if ((tmp = do_realpath(conn, path1)) == NULL) {
1359			err = 1;
1360			break;
1361		}
1362		if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1363			xfree(tmp);
1364			err = 1;
1365			break;
1366		}
1367		if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1368			error("Can't change directory: Can't check target");
1369			xfree(tmp);
1370			err = 1;
1371			break;
1372		}
1373		if (!S_ISDIR(aa->perm)) {
1374			error("Can't change directory: \"%s\" is not "
1375			    "a directory", tmp);
1376			xfree(tmp);
1377			err = 1;
1378			break;
1379		}
1380		xfree(*pwd);
1381		*pwd = tmp;
1382		break;
1383	case I_LS:
1384		if (!path1) {
1385			do_ls_dir(conn, *pwd, *pwd, lflag);
1386			break;
1387		}
1388
1389		/* Strip pwd off beginning of non-absolute paths */
1390		tmp = NULL;
1391		if (*path1 != '/')
1392			tmp = *pwd;
1393
1394		path1 = make_absolute(path1, *pwd);
1395		err = do_globbed_ls(conn, path1, tmp, lflag);
1396		break;
1397	case I_DF:
1398		/* Default to current directory if no path specified */
1399		if (path1 == NULL)
1400			path1 = xstrdup(*pwd);
1401		path1 = make_absolute(path1, *pwd);
1402		err = do_df(conn, path1, hflag, iflag);
1403		break;
1404	case I_LCHDIR:
1405		if (chdir(path1) == -1) {
1406			error("Couldn't change local directory to "
1407			    "\"%s\": %s", path1, strerror(errno));
1408			err = 1;
1409		}
1410		break;
1411	case I_LMKDIR:
1412		if (mkdir(path1, 0777) == -1) {
1413			error("Couldn't create local directory "
1414			    "\"%s\": %s", path1, strerror(errno));
1415			err = 1;
1416		}
1417		break;
1418	case I_LLS:
1419		local_do_ls(cmd);
1420		break;
1421	case I_SHELL:
1422		local_do_shell(cmd);
1423		break;
1424	case I_LUMASK:
1425		umask(n_arg);
1426		printf("Local umask: %03lo\n", n_arg);
1427		break;
1428	case I_CHMOD:
1429		path1 = make_absolute(path1, *pwd);
1430		attrib_clear(&a);
1431		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1432		a.perm = n_arg;
1433		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1434		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1435			printf("Changing mode on %s\n", g.gl_pathv[i]);
1436			err = do_setstat(conn, g.gl_pathv[i], &a);
1437			if (err != 0 && err_abort)
1438				break;
1439		}
1440		break;
1441	case I_CHOWN:
1442	case I_CHGRP:
1443		path1 = make_absolute(path1, *pwd);
1444		remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1445		for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1446			if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1447				if (err_abort) {
1448					err = -1;
1449					break;
1450				} else
1451					continue;
1452			}
1453			if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1454				error("Can't get current ownership of "
1455				    "remote file \"%s\"", g.gl_pathv[i]);
1456				if (err_abort) {
1457					err = -1;
1458					break;
1459				} else
1460					continue;
1461			}
1462			aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1463			if (cmdnum == I_CHOWN) {
1464				printf("Changing owner on %s\n", g.gl_pathv[i]);
1465				aa->uid = n_arg;
1466			} else {
1467				printf("Changing group on %s\n", g.gl_pathv[i]);
1468				aa->gid = n_arg;
1469			}
1470			err = do_setstat(conn, g.gl_pathv[i], aa);
1471			if (err != 0 && err_abort)
1472				break;
1473		}
1474		break;
1475	case I_PWD:
1476		printf("Remote working directory: %s\n", *pwd);
1477		break;
1478	case I_LPWD:
1479		if (!getcwd(path_buf, sizeof(path_buf))) {
1480			error("Couldn't get local cwd: %s", strerror(errno));
1481			err = -1;
1482			break;
1483		}
1484		printf("Local working directory: %s\n", path_buf);
1485		break;
1486	case I_QUIT:
1487		/* Processed below */
1488		break;
1489	case I_HELP:
1490		help();
1491		break;
1492	case I_VERSION:
1493		printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1494		break;
1495	case I_PROGRESS:
1496		showprogress = !showprogress;
1497		if (showprogress)
1498			printf("Progress meter enabled\n");
1499		else
1500			printf("Progress meter disabled\n");
1501		break;
1502	default:
1503		fatal("%d is not implemented", cmdnum);
1504	}
1505
1506	if (g.gl_pathc)
1507		globfree(&g);
1508	if (path1)
1509		xfree(path1);
1510	if (path2)
1511		xfree(path2);
1512
1513	/* If an unignored error occurs in batch mode we should abort. */
1514	if (err_abort && err != 0)
1515		return (-1);
1516	else if (cmdnum == I_QUIT)
1517		return (1);
1518
1519	return (0);
1520}
1521
1522#ifdef USE_LIBEDIT
1523static char *
1524prompt(EditLine *el)
1525{
1526	return ("sftp> ");
1527}
1528
1529/* Display entries in 'list' after skipping the first 'len' chars */
1530static void
1531complete_display(char **list, u_int len)
1532{
1533	u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1534	struct winsize ws;
1535	char *tmp;
1536
1537	/* Count entries for sort and find longest */
1538	for (y = 0; list[y]; y++)
1539		m = MAX(m, strlen(list[y]));
1540
1541	if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1542		width = ws.ws_col;
1543
1544	m = m > len ? m - len : 0;
1545	columns = width / (m + 2);
1546	columns = MAX(columns, 1);
1547	colspace = width / columns;
1548	colspace = MIN(colspace, width);
1549
1550	printf("\n");
1551	m = 1;
1552	for (y = 0; list[y]; y++) {
1553		llen = strlen(list[y]);
1554		tmp = llen > len ? list[y] + len : "";
1555		printf("%-*s", colspace, tmp);
1556		if (m >= columns) {
1557			printf("\n");
1558			m = 1;
1559		} else
1560			m++;
1561	}
1562	printf("\n");
1563}
1564
1565/*
1566 * Given a "list" of words that begin with a common prefix of "word",
1567 * attempt to find an autocompletion to extends "word" by the next
1568 * characters common to all entries in "list".
1569 */
1570static char *
1571complete_ambiguous(const char *word, char **list, size_t count)
1572{
1573	if (word == NULL)
1574		return NULL;
1575
1576	if (count > 0) {
1577		u_int y, matchlen = strlen(list[0]);
1578
1579		/* Find length of common stem */
1580		for (y = 1; list[y]; y++) {
1581			u_int x;
1582
1583			for (x = 0; x < matchlen; x++)
1584				if (list[0][x] != list[y][x])
1585					break;
1586
1587			matchlen = x;
1588		}
1589
1590		if (matchlen > strlen(word)) {
1591			char *tmp = xstrdup(list[0]);
1592
1593			tmp[matchlen] = '\0';
1594			return tmp;
1595		}
1596	}
1597
1598	return xstrdup(word);
1599}
1600
1601/* Autocomplete a sftp command */
1602static int
1603complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1604    int terminated)
1605{
1606	u_int y, count = 0, cmdlen, tmplen;
1607	char *tmp, **list, argterm[3];
1608	const LineInfo *lf;
1609
1610	list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1611
1612	/* No command specified: display all available commands */
1613	if (cmd == NULL) {
1614		for (y = 0; cmds[y].c; y++)
1615			list[count++] = xstrdup(cmds[y].c);
1616
1617		list[count] = NULL;
1618		complete_display(list, 0);
1619
1620		for (y = 0; list[y] != NULL; y++)
1621			xfree(list[y]);
1622		xfree(list);
1623		return count;
1624	}
1625
1626	/* Prepare subset of commands that start with "cmd" */
1627	cmdlen = strlen(cmd);
1628	for (y = 0; cmds[y].c; y++)  {
1629		if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1630			list[count++] = xstrdup(cmds[y].c);
1631	}
1632	list[count] = NULL;
1633
1634	if (count == 0) {
1635		xfree(list);
1636		return 0;
1637	}
1638
1639	/* Complete ambigious command */
1640	tmp = complete_ambiguous(cmd, list, count);
1641	if (count > 1)
1642		complete_display(list, 0);
1643
1644	for (y = 0; list[y]; y++)
1645		xfree(list[y]);
1646	xfree(list);
1647
1648	if (tmp != NULL) {
1649		tmplen = strlen(tmp);
1650		cmdlen = strlen(cmd);
1651		/* If cmd may be extended then do so */
1652		if (tmplen > cmdlen)
1653			if (el_insertstr(el, tmp + cmdlen) == -1)
1654				fatal("el_insertstr failed.");
1655		lf = el_line(el);
1656		/* Terminate argument cleanly */
1657		if (count == 1) {
1658			y = 0;
1659			if (!terminated)
1660				argterm[y++] = quote;
1661			if (lastarg || *(lf->cursor) != ' ')
1662				argterm[y++] = ' ';
1663			argterm[y] = '\0';
1664			if (y > 0 && el_insertstr(el, argterm) == -1)
1665				fatal("el_insertstr failed.");
1666		}
1667		xfree(tmp);
1668	}
1669
1670	return count;
1671}
1672
1673/*
1674 * Determine whether a particular sftp command's arguments (if any)
1675 * represent local or remote files.
1676 */
1677static int
1678complete_is_remote(char *cmd) {
1679	int i;
1680
1681	if (cmd == NULL)
1682		return -1;
1683
1684	for (i = 0; cmds[i].c; i++) {
1685		if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1686			return cmds[i].t;
1687	}
1688
1689	return -1;
1690}
1691
1692/* Autocomplete a filename "file" */
1693static int
1694complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1695    char *file, int remote, int lastarg, char quote, int terminated)
1696{
1697	glob_t g;
1698	char *tmp, *tmp2, ins[3];
1699	u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1700	const LineInfo *lf;
1701
1702	/* Glob from "file" location */
1703	if (file == NULL)
1704		tmp = xstrdup("*");
1705	else
1706		xasprintf(&tmp, "%s*", file);
1707
1708	/* Check if the path is absolute. */
1709	isabs = tmp[0] == '/';
1710
1711	memset(&g, 0, sizeof(g));
1712	if (remote != LOCAL) {
1713		tmp = make_absolute(tmp, remote_path);
1714		remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1715	} else
1716		glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1717
1718	/* Determine length of pwd so we can trim completion display */
1719	for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1720		/* Terminate counting on first unescaped glob metacharacter */
1721		if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1722			if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1723				hadglob = 1;
1724			break;
1725		}
1726		if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1727			tmplen++;
1728		if (tmp[tmplen] == '/')
1729			pwdlen = tmplen + 1;	/* track last seen '/' */
1730	}
1731	xfree(tmp);
1732
1733	if (g.gl_matchc == 0)
1734		goto out;
1735
1736	if (g.gl_matchc > 1)
1737		complete_display(g.gl_pathv, pwdlen);
1738
1739	tmp = NULL;
1740	/* Don't try to extend globs */
1741	if (file == NULL || hadglob)
1742		goto out;
1743
1744	tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1745	tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1746	xfree(tmp2);
1747
1748	if (tmp == NULL)
1749		goto out;
1750
1751	tmplen = strlen(tmp);
1752	filelen = strlen(file);
1753
1754	/* Count the number of escaped characters in the input string. */
1755	cesc = isesc = 0;
1756	for (i = 0; i < filelen; i++) {
1757		if (!isesc && file[i] == '\\' && i + 1 < filelen){
1758			isesc = 1;
1759			cesc++;
1760		} else
1761			isesc = 0;
1762	}
1763
1764	if (tmplen > (filelen - cesc)) {
1765		tmp2 = tmp + filelen - cesc;
1766		len = strlen(tmp2);
1767		/* quote argument on way out */
1768		for (i = 0; i < len; i++) {
1769			ins[0] = '\\';
1770			ins[1] = tmp2[i];
1771			ins[2] = '\0';
1772			switch (tmp2[i]) {
1773			case '\'':
1774			case '"':
1775			case '\\':
1776			case '\t':
1777			case '[':
1778			case ' ':
1779			case '#':
1780			case '*':
1781				if (quote == '\0' || tmp2[i] == quote) {
1782					if (el_insertstr(el, ins) == -1)
1783						fatal("el_insertstr "
1784						    "failed.");
1785					break;
1786				}
1787				/* FALLTHROUGH */
1788			default:
1789				if (el_insertstr(el, ins + 1) == -1)
1790					fatal("el_insertstr failed.");
1791				break;
1792			}
1793		}
1794	}
1795
1796	lf = el_line(el);
1797	if (g.gl_matchc == 1) {
1798		i = 0;
1799		if (!terminated)
1800			ins[i++] = quote;
1801		if (*(lf->cursor - 1) != '/' &&
1802		    (lastarg || *(lf->cursor) != ' '))
1803			ins[i++] = ' ';
1804		ins[i] = '\0';
1805		if (i > 0 && el_insertstr(el, ins) == -1)
1806			fatal("el_insertstr failed.");
1807	}
1808	xfree(tmp);
1809
1810 out:
1811	globfree(&g);
1812	return g.gl_matchc;
1813}
1814
1815/* tab-completion hook function, called via libedit */
1816static unsigned char
1817complete(EditLine *el, int ch)
1818{
1819	char **argv, *line, quote;
1820	u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1821	const LineInfo *lf;
1822	struct complete_ctx *complete_ctx;
1823
1824	lf = el_line(el);
1825	if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1826		fatal("%s: el_get failed", __func__);
1827
1828	/* Figure out which argument the cursor points to */
1829	cursor = lf->cursor - lf->buffer;
1830	line = (char *)xmalloc(cursor + 1);
1831	memcpy(line, lf->buffer, cursor);
1832	line[cursor] = '\0';
1833	argv = makeargv(line, &carg, 1, &quote, &terminated);
1834	xfree(line);
1835
1836	/* Get all the arguments on the line */
1837	len = lf->lastchar - lf->buffer;
1838	line = (char *)xmalloc(len + 1);
1839	memcpy(line, lf->buffer, len);
1840	line[len] = '\0';
1841	argv = makeargv(line, &argc, 1, NULL, NULL);
1842
1843	/* Ensure cursor is at EOL or a argument boundary */
1844	if (line[cursor] != ' ' && line[cursor] != '\0' &&
1845	    line[cursor] != '\n') {
1846		xfree(line);
1847		return ret;
1848	}
1849
1850	if (carg == 0) {
1851		/* Show all available commands */
1852		complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1853		ret = CC_REDISPLAY;
1854	} else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ')  {
1855		/* Handle the command parsing */
1856		if (complete_cmd_parse(el, argv[0], argc == carg,
1857		    quote, terminated) != 0)
1858			ret = CC_REDISPLAY;
1859	} else if (carg >= 1) {
1860		/* Handle file parsing */
1861		int remote = complete_is_remote(argv[0]);
1862		char *filematch = NULL;
1863
1864		if (carg > 1 && line[cursor-1] != ' ')
1865			filematch = argv[carg - 1];
1866
1867		if (remote != 0 &&
1868		    complete_match(el, complete_ctx->conn,
1869		    *complete_ctx->remote_pathp, filematch,
1870		    remote, carg == argc, quote, terminated) != 0)
1871			ret = CC_REDISPLAY;
1872	}
1873
1874	xfree(line);
1875	return ret;
1876}
1877#endif /* USE_LIBEDIT */
1878
1879int
1880interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1881{
1882	char *remote_path;
1883	char *dir = NULL;
1884	char cmd[2048];
1885	int err, interactive;
1886	EditLine *el = NULL;
1887#ifdef USE_LIBEDIT
1888	History *hl = NULL;
1889	HistEvent hev;
1890	extern char *__progname;
1891	struct complete_ctx complete_ctx;
1892
1893	if (!batchmode && isatty(STDIN_FILENO)) {
1894		if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1895			fatal("Couldn't initialise editline");
1896		if ((hl = history_init()) == NULL)
1897			fatal("Couldn't initialise editline history");
1898		history(hl, &hev, H_SETSIZE, 100);
1899		el_set(el, EL_HIST, history, hl);
1900
1901		el_set(el, EL_PROMPT, prompt);
1902		el_set(el, EL_EDITOR, "emacs");
1903		el_set(el, EL_TERMINAL, NULL);
1904		el_set(el, EL_SIGNAL, 1);
1905		el_source(el, NULL);
1906
1907		/* Tab Completion */
1908		el_set(el, EL_ADDFN, "ftp-complete",
1909		    "Context sensitive argument completion", complete);
1910		complete_ctx.conn = conn;
1911		complete_ctx.remote_pathp = &remote_path;
1912		el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1913		el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1914	}
1915#endif /* USE_LIBEDIT */
1916
1917	remote_path = do_realpath(conn, ".");
1918	if (remote_path == NULL)
1919		fatal("Need cwd");
1920
1921	if (file1 != NULL) {
1922		dir = xstrdup(file1);
1923		dir = make_absolute(dir, remote_path);
1924
1925		if (remote_is_dir(conn, dir) && file2 == NULL) {
1926			printf("Changing to: %s\n", dir);
1927			snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1928			if (parse_dispatch_command(conn, cmd,
1929			    &remote_path, 1) != 0) {
1930				xfree(dir);
1931				xfree(remote_path);
1932				xfree(conn);
1933				return (-1);
1934			}
1935		} else {
1936			/* XXX this is wrong wrt quoting */
1937			if (file2 == NULL)
1938				snprintf(cmd, sizeof cmd, "get %s", dir);
1939			else
1940				snprintf(cmd, sizeof cmd, "get %s %s", dir,
1941				    file2);
1942
1943			err = parse_dispatch_command(conn, cmd,
1944			    &remote_path, 1);
1945			xfree(dir);
1946			xfree(remote_path);
1947			xfree(conn);
1948			return (err);
1949		}
1950		xfree(dir);
1951	}
1952
1953	setlinebuf(stdout);
1954	setlinebuf(infile);
1955
1956	interactive = !batchmode && isatty(STDIN_FILENO);
1957	err = 0;
1958	for (;;) {
1959		char *cp;
1960
1961		signal(SIGINT, SIG_IGN);
1962
1963		if (el == NULL) {
1964			if (interactive)
1965				printf("sftp> ");
1966			if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1967				if (interactive)
1968					printf("\n");
1969				break;
1970			}
1971			if (!interactive) { /* Echo command */
1972				printf("sftp> %s", cmd);
1973				if (strlen(cmd) > 0 &&
1974				    cmd[strlen(cmd) - 1] != '\n')
1975					printf("\n");
1976			}
1977		} else {
1978#ifdef USE_LIBEDIT
1979			const char *line;
1980			int count = 0;
1981
1982			if ((line = el_gets(el, &count)) == NULL ||
1983			    count <= 0) {
1984				printf("\n");
1985 				break;
1986			}
1987			history(hl, &hev, H_ENTER, line);
1988			if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1989				fprintf(stderr, "Error: input line too long\n");
1990				continue;
1991			}
1992#endif /* USE_LIBEDIT */
1993		}
1994
1995		cp = strrchr(cmd, '\n');
1996		if (cp)
1997			*cp = '\0';
1998
1999		/* Handle user interrupts gracefully during commands */
2000		interrupted = 0;
2001		signal(SIGINT, cmd_interrupt);
2002
2003		err = parse_dispatch_command(conn, cmd, &remote_path,
2004		    batchmode);
2005		if (err != 0)
2006			break;
2007	}
2008	xfree(remote_path);
2009	xfree(conn);
2010
2011#ifdef USE_LIBEDIT
2012	if (el != NULL)
2013		el_end(el);
2014#endif /* USE_LIBEDIT */
2015
2016	/* err == 1 signifies normal "quit" exit */
2017	return (err >= 0 ? 0 : -1);
2018}
2019
2020static void
2021connect_to_server(char *path, char **args, int *in, int *out)
2022{
2023	int c_in, c_out;
2024
2025#ifdef USE_PIPES
2026	int pin[2], pout[2];
2027
2028	if ((pipe(pin) == -1) || (pipe(pout) == -1))
2029		fatal("pipe: %s", strerror(errno));
2030	*in = pin[0];
2031	*out = pout[1];
2032	c_in = pout[0];
2033	c_out = pin[1];
2034#else /* USE_PIPES */
2035	int inout[2];
2036
2037	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2038		fatal("socketpair: %s", strerror(errno));
2039	*in = *out = inout[0];
2040	c_in = c_out = inout[1];
2041#endif /* USE_PIPES */
2042
2043	if ((sshpid = fork()) == -1)
2044		fatal("fork: %s", strerror(errno));
2045	else if (sshpid == 0) {
2046		if ((dup2(c_in, STDIN_FILENO) == -1) ||
2047		    (dup2(c_out, STDOUT_FILENO) == -1)) {
2048			fprintf(stderr, "dup2: %s\n", strerror(errno));
2049			_exit(1);
2050		}
2051		close(*in);
2052		close(*out);
2053		close(c_in);
2054		close(c_out);
2055
2056		/*
2057		 * The underlying ssh is in the same process group, so we must
2058		 * ignore SIGINT if we want to gracefully abort commands,
2059		 * otherwise the signal will make it to the ssh process and
2060		 * kill it too.  Contrawise, since sftp sends SIGTERMs to the
2061		 * underlying ssh, it must *not* ignore that signal.
2062		 */
2063		signal(SIGINT, SIG_IGN);
2064		signal(SIGTERM, SIG_DFL);
2065		execvp(path, args);
2066		fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2067		_exit(1);
2068	}
2069
2070	signal(SIGTERM, killchild);
2071	signal(SIGINT, killchild);
2072	signal(SIGHUP, killchild);
2073	close(c_in);
2074	close(c_out);
2075}
2076
2077static void
2078usage(void)
2079{
2080	extern char *__progname;
2081
2082	fprintf(stderr,
2083	    "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2084	    "          [-D sftp_server_path] [-F ssh_config] "
2085	    "[-i identity_file] [-l limit]\n"
2086	    "          [-o ssh_option] [-P port] [-R num_requests] "
2087	    "[-S program]\n"
2088	    "          [-s subsystem | sftp_server] host\n"
2089	    "       %s [user@]host[:file ...]\n"
2090	    "       %s [user@]host[:dir[/]]\n"
2091	    "       %s -b batchfile [user@]host\n",
2092	    __progname, __progname, __progname, __progname);
2093	exit(1);
2094}
2095
2096int
2097main(int argc, char **argv)
2098{
2099	int in, out, ch, err;
2100	char *host = NULL, *userhost, *cp, *file2 = NULL;
2101	int debug_level = 0, sshver = 2;
2102	char *file1 = NULL, *sftp_server = NULL;
2103	char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2104	const char *errstr;
2105	LogLevel ll = SYSLOG_LEVEL_INFO;
2106	arglist args;
2107	extern int optind;
2108	extern char *optarg;
2109	struct sftp_conn *conn;
2110	size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2111	size_t num_requests = DEFAULT_NUM_REQUESTS;
2112	long long limit_kbps = 0;
2113
2114	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2115	sanitise_stdfd();
2116
2117	__progname = ssh_get_progname(argv[0]);
2118	memset(&args, '\0', sizeof(args));
2119	args.list = NULL;
2120	addargs(&args, "%s", ssh_program);
2121	addargs(&args, "-oForwardX11 no");
2122	addargs(&args, "-oForwardAgent no");
2123	addargs(&args, "-oPermitLocalCommand no");
2124	addargs(&args, "-oClearAllForwardings yes");
2125
2126	ll = SYSLOG_LEVEL_INFO;
2127	infile = stdin;
2128
2129	while ((ch = getopt(argc, argv,
2130	    "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2131		switch (ch) {
2132		/* Passed through to ssh(1) */
2133		case '4':
2134		case '6':
2135		case 'C':
2136			addargs(&args, "-%c", ch);
2137			break;
2138		/* Passed through to ssh(1) with argument */
2139		case 'F':
2140		case 'c':
2141		case 'i':
2142		case 'o':
2143			addargs(&args, "-%c", ch);
2144			addargs(&args, "%s", optarg);
2145			break;
2146		case 'q':
2147			showprogress = 0;
2148			addargs(&args, "-%c", ch);
2149			break;
2150		case 'P':
2151			addargs(&args, "-oPort %s", optarg);
2152			break;
2153		case 'v':
2154			if (debug_level < 3) {
2155				addargs(&args, "-v");
2156				ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2157			}
2158			debug_level++;
2159			break;
2160		case '1':
2161			sshver = 1;
2162			if (sftp_server == NULL)
2163				sftp_server = _PATH_SFTP_SERVER;
2164			break;
2165		case '2':
2166			sshver = 2;
2167			break;
2168		case 'B':
2169			copy_buffer_len = strtol(optarg, &cp, 10);
2170			if (copy_buffer_len == 0 || *cp != '\0')
2171				fatal("Invalid buffer size \"%s\"", optarg);
2172			break;
2173		case 'b':
2174			if (batchmode)
2175				fatal("Batch file already specified.");
2176
2177			/* Allow "-" as stdin */
2178			if (strcmp(optarg, "-") != 0 &&
2179			    (infile = fopen(optarg, "r")) == NULL)
2180				fatal("%s (%s).", strerror(errno), optarg);
2181			showprogress = 0;
2182			batchmode = 1;
2183			addargs(&args, "-obatchmode yes");
2184			break;
2185		case 'p':
2186			global_pflag = 1;
2187			break;
2188		case 'D':
2189			sftp_direct = optarg;
2190			break;
2191		case 'l':
2192			limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2193			    &errstr);
2194			if (errstr != NULL)
2195				usage();
2196			limit_kbps *= 1024; /* kbps */
2197			break;
2198		case 'r':
2199			global_rflag = 1;
2200			break;
2201		case 'R':
2202			num_requests = strtol(optarg, &cp, 10);
2203			if (num_requests == 0 || *cp != '\0')
2204				fatal("Invalid number of requests \"%s\"",
2205				    optarg);
2206			break;
2207		case 's':
2208			sftp_server = optarg;
2209			break;
2210		case 'S':
2211			ssh_program = optarg;
2212			replacearg(&args, 0, "%s", ssh_program);
2213			break;
2214		case 'h':
2215		default:
2216			usage();
2217		}
2218	}
2219
2220	if (!isatty(STDERR_FILENO))
2221		showprogress = 0;
2222
2223	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2224
2225	if (sftp_direct == NULL) {
2226		if (optind == argc || argc > (optind + 2))
2227			usage();
2228
2229		userhost = xstrdup(argv[optind]);
2230		file2 = argv[optind+1];
2231
2232		if ((host = strrchr(userhost, '@')) == NULL)
2233			host = userhost;
2234		else {
2235			*host++ = '\0';
2236			if (!userhost[0]) {
2237				fprintf(stderr, "Missing username\n");
2238				usage();
2239			}
2240			addargs(&args, "-l");
2241			addargs(&args, "%s", userhost);
2242		}
2243
2244		if ((cp = colon(host)) != NULL) {
2245			*cp++ = '\0';
2246			file1 = cp;
2247		}
2248
2249		host = cleanhostname(host);
2250		if (!*host) {
2251			fprintf(stderr, "Missing hostname\n");
2252			usage();
2253		}
2254
2255		addargs(&args, "-oProtocol %d", sshver);
2256
2257		/* no subsystem if the server-spec contains a '/' */
2258		if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2259			addargs(&args, "-s");
2260
2261		addargs(&args, "--");
2262		addargs(&args, "%s", host);
2263		addargs(&args, "%s", (sftp_server != NULL ?
2264		    sftp_server : "sftp"));
2265
2266		connect_to_server(ssh_program, args.list, &in, &out);
2267	} else {
2268		args.list = NULL;
2269		addargs(&args, "sftp-server");
2270
2271		connect_to_server(sftp_direct, args.list, &in, &out);
2272	}
2273	freeargs(&args);
2274
2275	conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2276	if (conn == NULL)
2277		fatal("Couldn't initialise connection to server");
2278
2279	if (!batchmode) {
2280		if (sftp_direct == NULL)
2281			fprintf(stderr, "Connected to %s.\n", host);
2282		else
2283			fprintf(stderr, "Attached to %s.\n", sftp_direct);
2284	}
2285
2286	err = interactive_loop(conn, file1, file2);
2287
2288#if !defined(USE_PIPES)
2289	shutdown(in, SHUT_RDWR);
2290	shutdown(out, SHUT_RDWR);
2291#endif
2292
2293	close(in);
2294	close(out);
2295	if (batchmode)
2296		fclose(infile);
2297
2298	while (waitpid(sshpid, NULL, 0) == -1)
2299		if (errno != EINTR)
2300			fatal("Couldn't wait for ssh process: %s",
2301			    strerror(errno));
2302
2303	exit(err == 0 ? 0 : 1);
2304}
2305