-/* $OpenBSD: sftp.c,v 1.132 2010/12/04 00:18:01 djm Exp $ */
+/* $OpenBSD: sftp.c,v 1.171 2015/08/20 22:32:42 deraadt Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
#include "includes.h"
+#include <sys/param.h> /* MIN MAX */
#include <sys/types.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_STAT_H
#ifdef HAVE_LIBGEN_H
#include <libgen.h>
#endif
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
#ifdef USE_LIBEDIT
#include <histedit.h>
#else
typedef void EditLine;
#endif
+#include <limits.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
# include <util.h>
#endif
-#ifdef HAVE_LIBUTIL_H
-# include <libutil.h>
-#endif
-
#include "xmalloc.h"
#include "log.h"
#include "pathnames.h"
#include "misc.h"
#include "sftp.h"
-#include "buffer.h"
+#include "ssherr.h"
+#include "sshbuf.h"
#include "sftp-common.h"
#include "sftp-client.h"
/* PID of ssh transport process */
static pid_t sshpid = -1;
+/* Suppress diagnositic messages */
+int quiet = 0;
+
/* This is set to 0 if the progressmeter is not desired. */
int showprogress = 1;
/* When this option is set, we always recursively download/upload directories */
int global_rflag = 0;
+/* When this option is set, we resume download or upload if possible */
+int global_aflag = 0;
+
/* When this option is set, the file transfers will always preserve times */
int global_pflag = 0;
+/* When this option is set, transfers will have fsync() called on each file */
+int global_fflag = 0;
+
/* SIGINT received during command processing */
volatile sig_atomic_t interrupted = 0;
#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
/* Commands for interactive mode */
-#define I_CHDIR 1
-#define I_CHGRP 2
-#define I_CHMOD 3
-#define I_CHOWN 4
-#define I_DF 24
-#define I_GET 5
-#define I_HELP 6
-#define I_LCHDIR 7
-#define I_LINK 25
-#define I_LLS 8
-#define I_LMKDIR 9
-#define I_LPWD 10
-#define I_LS 11
-#define I_LUMASK 12
-#define I_MKDIR 13
-#define I_PUT 14
-#define I_PWD 15
-#define I_QUIT 16
-#define I_RENAME 17
-#define I_RM 18
-#define I_RMDIR 19
-#define I_SHELL 20
-#define I_SYMLINK 21
-#define I_VERSION 22
-#define I_PROGRESS 23
+enum sftp_command {
+ I_CHDIR = 1,
+ I_CHGRP,
+ I_CHMOD,
+ I_CHOWN,
+ I_DF,
+ I_GET,
+ I_HELP,
+ I_LCHDIR,
+ I_LINK,
+ I_LLS,
+ I_LMKDIR,
+ I_LPWD,
+ I_LS,
+ I_LUMASK,
+ I_MKDIR,
+ I_PUT,
+ I_PWD,
+ I_QUIT,
+ I_REGET,
+ I_RENAME,
+ I_REPUT,
+ I_RM,
+ I_RMDIR,
+ I_SHELL,
+ I_SYMLINK,
+ I_VERSION,
+ I_PROGRESS,
+};
struct CMD {
const char *c;
{ "put", I_PUT, LOCAL },
{ "pwd", I_PWD, REMOTE },
{ "quit", I_QUIT, NOARGS },
+ { "reget", I_REGET, REMOTE },
{ "rename", I_RENAME, REMOTE },
+ { "reput", I_REPUT, LOCAL },
{ "rm", I_RM, REMOTE },
{ "rmdir", I_RMDIR, REMOTE },
{ "symlink", I_SYMLINK, REMOTE },
const char msg[] = "\rInterrupt \n";
int olderrno = errno;
- write(STDERR_FILENO, msg, sizeof(msg) - 1);
+ (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
interrupted = 1;
errno = olderrno;
}
"df [-hi] [path] Display statistics for current directory or\n"
" filesystem containing 'path'\n"
"exit Quit sftp\n"
- "get [-Ppr] remote [local] Download file\n"
+ "get [-afPpRr] remote [local] Download file\n"
+ "reget [-fPpRr] remote [local] Resume download file\n"
+ "reput [-fPpRr] [local] remote Resume upload file\n"
"help Display this help text\n"
"lcd path Change local directory to 'path'\n"
"lls [ls-options [path]] Display local directory listing\n"
"lumask umask Set local umask to 'umask'\n"
"mkdir path Create remote directory\n"
"progress Toggle display of progress meter\n"
- "put [-Ppr] local [remote] Upload file\n"
+ "put [-afPpRr] local [remote] Upload file\n"
"pwd Display remote working directory\n"
"quit Quit sftp\n"
"rename oldpath newpath Rename remote file\n"
/* XXX: quoting - rip quoting code from ftp? */
snprintf(buf, len, _PATH_LS " %s", args);
local_do_shell(buf);
- xfree(buf);
+ free(buf);
}
}
/* Derelativise */
if (p && p[0] != '/') {
abs_str = path_append(pwd, p);
- xfree(p);
+ free(p);
return(abs_str);
} else
return(p);
}
static int
-parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
- int *rflag)
+parse_getput_flags(const char *cmd, char **argv, int argc,
+ int *aflag, int *fflag, int *pflag, int *rflag)
{
extern int opterr, optind, optopt, optreset;
int ch;
optind = optreset = 1;
opterr = 0;
- *rflag = *pflag = 0;
- while ((ch = getopt(argc, argv, "PpRr")) != -1) {
+ *aflag = *fflag = *rflag = *pflag = 0;
+ while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
switch (ch) {
+ case 'a':
+ *aflag = 1;
+ break;
+ case 'f':
+ *fflag = 1;
+ break;
case 'p':
case 'P':
*pflag = 1;
}
static int
+parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
+{
+ extern int opterr, optind, optopt, optreset;
+ int ch;
+
+ optind = optreset = 1;
+ opterr = 0;
+
+ *lflag = 0;
+ while ((ch = getopt(argc, argv, "l")) != -1) {
+ switch (ch) {
+ case 'l':
+ *lflag = 1;
+ break;
+ default:
+ error("%s: Invalid flag -%c", cmd, optopt);
+ return -1;
+ }
+ }
+
+ return optind;
+}
+
+static int
parse_ls_flags(char **argv, int argc, int *lflag)
{
extern int opterr, optind, optopt, optreset;
}
static int
+parse_no_flags(const char *cmd, char **argv, int argc)
+{
+ extern int opterr, optind, optopt, optreset;
+ int ch;
+
+ optind = optreset = 1;
+ opterr = 0;
+
+ while ((ch = getopt(argc, argv, "")) != -1) {
+ switch (ch) {
+ default:
+ error("%s: Invalid flag -%c", cmd, optopt);
+ return -1;
+ }
+ }
+
+ return optind;
+}
+
+static int
is_dir(char *path)
{
struct stat sb;
static int
process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
- int pflag, int rflag)
+ int pflag, int rflag, int resume, int fflag)
{
char *abs_src = NULL;
char *abs_dst = NULL;
glob_t g;
char *filename, *tmp=NULL;
- int i, err = 0;
+ int i, r, err = 0;
abs_src = xstrdup(src);
abs_src = make_absolute(abs_src, pwd);
memset(&g, 0, sizeof(g));
debug3("Looking up %s", abs_src);
- if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
- error("File \"%s\" not found.", abs_src);
+ if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
+ if (r == GLOB_NOSPACE) {
+ error("Too many matches for \"%s\".", abs_src);
+ } else {
+ error("File \"%s\" not found.", abs_src);
+ }
err = -1;
goto out;
}
tmp = xstrdup(g.gl_pathv[i]);
if ((filename = basename(tmp)) == NULL) {
error("basename %s: %s", tmp, strerror(errno));
- xfree(tmp);
+ free(tmp);
err = -1;
goto out;
}
} else {
abs_dst = xstrdup(filename);
}
- xfree(tmp);
+ free(tmp);
- printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
+ resume |= global_aflag;
+ if (!quiet && resume)
+ printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
+ else if (!quiet && !resume)
+ printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
- if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
- pflag || global_pflag, 1) == -1)
+ if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
+ pflag || global_pflag, 1, resume,
+ fflag || global_fflag) == -1)
err = -1;
} else {
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
- pflag || global_pflag) == -1)
+ pflag || global_pflag, resume,
+ fflag || global_fflag) == -1)
err = -1;
}
- xfree(abs_dst);
+ free(abs_dst);
abs_dst = NULL;
}
out:
- xfree(abs_src);
+ free(abs_src);
globfree(&g);
return(err);
}
static int
process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
- int pflag, int rflag)
+ int pflag, int rflag, int resume, int fflag)
{
char *tmp_dst = NULL;
char *abs_dst = NULL;
error("stat %s: %s", g.gl_pathv[i], strerror(errno));
continue;
}
-
+
tmp = xstrdup(g.gl_pathv[i]);
if ((filename = basename(tmp)) == NULL) {
error("basename %s: %s", tmp, strerror(errno));
- xfree(tmp);
+ free(tmp);
err = -1;
goto out;
}
} else {
abs_dst = make_absolute(xstrdup(filename), pwd);
}
- xfree(tmp);
-
- printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
+ free(tmp);
+
+ resume |= global_aflag;
+ if (!quiet && resume)
+ printf("Resuming upload of %s to %s\n", g.gl_pathv[i],
+ abs_dst);
+ else if (!quiet && !resume)
+ printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
if (upload_dir(conn, g.gl_pathv[i], abs_dst,
- pflag || global_pflag, 1) == -1)
+ pflag || global_pflag, 1, resume,
+ fflag || global_fflag) == -1)
err = -1;
} else {
if (do_upload(conn, g.gl_pathv[i], abs_dst,
- pflag || global_pflag) == -1)
+ pflag || global_pflag, resume,
+ fflag || global_fflag) == -1)
err = -1;
}
}
out:
- if (abs_dst)
- xfree(abs_dst);
- if (tmp_dst)
- xfree(tmp_dst);
+ free(abs_dst);
+ free(tmp_dst);
globfree(&g);
return(err);
}
/* Add any subpath that also needs to be counted */
tmp = path_strip(path, strip_path);
m += strlen(tmp);
- xfree(tmp);
+ free(tmp);
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
width = ws.ws_col;
tmp = path_append(path, d[n]->filename);
fname = path_strip(tmp, strip_path);
- xfree(tmp);
+ free(tmp);
if (lflag & LS_LONG_VIEW) {
if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
lname = ls_file(fname, &sb, 1,
(lflag & LS_SI_UNITS));
printf("%s\n", lname);
- xfree(lname);
+ free(lname);
} else
printf("%s\n", d[n]->longname);
} else {
c++;
}
- xfree(fname);
+ free(fname);
}
if (!(lflag & LS_LONG_VIEW) && (c != 1))
do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
int lflag)
{
- Attrib *a = NULL;
char *fname, *lname;
glob_t g;
- int err;
+ int err, r;
struct winsize ws;
u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
memset(&g, 0, sizeof(g));
- if (remote_glob(conn, path,
- GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT, NULL, &g) ||
+ if ((r = remote_glob(conn, path,
+ GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
+ NULL, &g)) != 0 ||
(g.gl_pathc && !g.gl_matchc)) {
if (g.gl_pathc)
globfree(&g);
- error("Can't ls: \"%s\" not found", path);
+ if (r == GLOB_NOSPACE) {
+ error("Can't ls: Too many matches for \"%s\"", path);
+ } else {
+ error("Can't ls: \"%s\" not found", path);
+ }
return -1;
}
colspace = width / columns;
}
- for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
+ for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
fname = path_strip(g.gl_pathv[i], strip_path);
if (lflag & LS_LONG_VIEW) {
if (g.gl_statv[i] == NULL) {
lname = ls_file(fname, g.gl_statv[i], 1,
(lflag & LS_SI_UNITS));
printf("%s\n", lname);
- xfree(lname);
+ free(lname);
} else {
printf("%-*s", colspace, fname);
if (c >= columns) {
} else
c++;
}
- xfree(fname);
+ free(fname);
}
if (!(lflag & LS_LONG_VIEW) && (c != 1))
*
* If "lastquote" is not NULL, the quoting character used for the last
* argument is placed in *lastquote ("\0", "'" or "\"").
- *
+ *
* If "terminated" is not NULL, *terminated will be set to 1 when the
* last argument's quote has been properly terminated or 0 otherwise.
* This parameter is only of use if "sloppy" is set.
state = MA_START;
i = j = 0;
for (;;) {
- if (isspace(arg[i])) {
+ if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
+ error("Too many arguments.");
+ return NULL;
+ }
+ if (isspace((unsigned char)arg[i])) {
if (state == MA_UNQUOTED) {
/* Terminate current argument */
argvs[j++] = '\0';
state = q;
if (lastquote != NULL)
*lastquote = arg[i];
- } else if (state == MA_UNQUOTED)
+ } else if (state == MA_UNQUOTED)
state = q;
else if (state == q)
state = MA_UNQUOTED;
}
static int
-parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
- int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2)
+parse_args(const char **cpp, int *ignore_errors, int *aflag,
+ int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
+ int *rflag, int *sflag,
+ unsigned long *n_arg, char **path1, char **path2)
{
const char *cmd, *cp = *cpp;
char *cp2, **argv;
cp = cp + strspn(cp, WHITESPACE);
/* Check for leading '-' (disable error processing) */
- *iflag = 0;
+ *ignore_errors = 0;
if (*cp == '-') {
- *iflag = 1;
+ *ignore_errors = 1;
cp++;
cp = cp + strspn(cp, WHITESPACE);
}
/* Figure out which command we have */
for (i = 0; cmds[i].c != NULL; i++) {
- if (strcasecmp(cmds[i].c, argv[0]) == 0)
+ if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
break;
}
cmdnum = cmds[i].n;
}
/* Get arguments and parse flags */
- *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
+ *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
+ *rflag = *sflag = 0;
*path1 = *path2 = NULL;
optidx = 1;
switch (cmdnum) {
case I_GET:
+ case I_REGET:
+ case I_REPUT:
case I_PUT:
if ((optidx = parse_getput_flags(cmd, argv, argc,
- pflag, rflag)) == -1)
+ aflag, fflag, pflag, rflag)) == -1)
return -1;
/* Get first pathname (mandatory) */
if (argc - optidx < 1) {
case I_LINK:
if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
return -1;
- case I_SYMLINK:
+ goto parse_two_paths;
case I_RENAME:
+ if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
+ return -1;
+ goto parse_two_paths;
+ case I_SYMLINK:
+ if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
+ return -1;
+ parse_two_paths:
if (argc - optidx < 2) {
error("You must specify two paths after a %s "
"command.", cmd);
case I_CHDIR:
case I_LCHDIR:
case I_LMKDIR:
+ if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
+ return -1;
/* Get pathname (mandatory) */
if (argc - optidx < 1) {
error("You must specify a path after a %s command.",
base = 8;
case I_CHOWN:
case I_CHGRP:
+ if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
+ return -1;
/* Get numeric arg (mandatory) */
if (argc - optidx < 1)
goto need_num_arg;
case I_HELP:
case I_VERSION:
case I_PROGRESS:
+ if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
+ return -1;
break;
default:
fatal("Command not implemented");
int err_abort)
{
char *path1, *path2, *tmp;
- int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0;
+ int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0,
+ iflag = 0;
+ int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
int cmdnum, i;
unsigned long n_arg = 0;
Attrib a, *aa;
- char path_buf[MAXPATHLEN];
+ char path_buf[PATH_MAX];
int err = 0;
glob_t g;
path1 = path2 = NULL;
- cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag,
- &sflag, &n_arg, &path1, &path2);
-
- if (iflag != 0)
+ cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
+ &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
+ if (ignore_errors != 0)
err_abort = 0;
memset(&g, 0, sizeof(g));
/* Unrecognized command */
err = -1;
break;
+ case I_REGET:
+ aflag = 1;
+ /* FALLTHROUGH */
case I_GET:
- err = process_get(conn, path1, path2, *pwd, pflag, rflag);
+ err = process_get(conn, path1, path2, *pwd, pflag,
+ rflag, aflag, fflag);
break;
+ case I_REPUT:
+ aflag = 1;
+ /* FALLTHROUGH */
case I_PUT:
- err = process_put(conn, path1, path2, *pwd, pflag, rflag);
+ err = process_put(conn, path1, path2, *pwd, pflag,
+ rflag, aflag, fflag);
break;
case I_RENAME:
path1 = make_absolute(path1, *pwd);
path2 = make_absolute(path2, *pwd);
- err = do_rename(conn, path1, path2);
+ err = do_rename(conn, path1, path2, lflag);
break;
case I_SYMLINK:
sflag = 1;
case I_LINK:
- path1 = make_absolute(path1, *pwd);
+ if (!sflag)
+ path1 = make_absolute(path1, *pwd);
path2 = make_absolute(path2, *pwd);
err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
break;
path1 = make_absolute(path1, *pwd);
remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
- printf("Removing %s\n", g.gl_pathv[i]);
+ if (!quiet)
+ printf("Removing %s\n", g.gl_pathv[i]);
err = do_rm(conn, g.gl_pathv[i]);
if (err != 0 && err_abort)
break;
break;
}
if ((aa = do_stat(conn, tmp, 0)) == NULL) {
- xfree(tmp);
+ free(tmp);
err = 1;
break;
}
if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
error("Can't change directory: Can't check target");
- xfree(tmp);
+ free(tmp);
err = 1;
break;
}
if (!S_ISDIR(aa->perm)) {
error("Can't change directory: \"%s\" is not "
"a directory", tmp);
- xfree(tmp);
+ free(tmp);
err = 1;
break;
}
- xfree(*pwd);
+ free(*pwd);
*pwd = tmp;
break;
case I_LS:
err = do_df(conn, path1, hflag, iflag);
break;
case I_LCHDIR:
+ tmp = tilde_expand_filename(path1, getuid());
+ free(path1);
+ path1 = tmp;
if (chdir(path1) == -1) {
error("Couldn't change local directory to "
"\"%s\": %s", path1, strerror(errno));
a.perm = n_arg;
remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
- printf("Changing mode on %s\n", g.gl_pathv[i]);
+ if (!quiet)
+ printf("Changing mode on %s\n", g.gl_pathv[i]);
err = do_setstat(conn, g.gl_pathv[i], &a);
if (err != 0 && err_abort)
break;
}
aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
if (cmdnum == I_CHOWN) {
- printf("Changing owner on %s\n", g.gl_pathv[i]);
+ if (!quiet)
+ printf("Changing owner on %s\n",
+ g.gl_pathv[i]);
aa->uid = n_arg;
} else {
- printf("Changing group on %s\n", g.gl_pathv[i]);
+ if (!quiet)
+ printf("Changing group on %s\n",
+ g.gl_pathv[i]);
aa->gid = n_arg;
}
err = do_setstat(conn, g.gl_pathv[i], aa);
if (g.gl_pathc)
globfree(&g);
- if (path1)
- xfree(path1);
- if (path2)
- xfree(path2);
+ free(path1);
+ free(path2);
/* If an unignored error occurs in batch mode we should abort. */
if (err_abort && err != 0)
char *tmp;
/* Count entries for sort and find longest */
- for (y = 0; list[y]; y++)
+ for (y = 0; list[y]; y++)
m = MAX(m, strlen(list[y]));
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
for (y = 1; list[y]; y++) {
u_int x;
- for (x = 0; x < matchlen; x++)
- if (list[0][x] != list[y][x])
+ for (x = 0; x < matchlen; x++)
+ if (list[0][x] != list[y][x])
break;
matchlen = x;
tmp[matchlen] = '\0';
return tmp;
}
- }
+ }
return xstrdup(word);
}
if (cmd == NULL) {
for (y = 0; cmds[y].c; y++)
list[count++] = xstrdup(cmds[y].c);
-
+
list[count] = NULL;
complete_display(list, 0);
- for (y = 0; list[y] != NULL; y++)
- xfree(list[y]);
- xfree(list);
+ for (y = 0; list[y] != NULL; y++)
+ free(list[y]);
+ free(list);
return count;
}
/* Prepare subset of commands that start with "cmd" */
cmdlen = strlen(cmd);
for (y = 0; cmds[y].c; y++) {
- if (!strncasecmp(cmd, cmds[y].c, cmdlen))
+ if (!strncasecmp(cmd, cmds[y].c, cmdlen))
list[count++] = xstrdup(cmds[y].c);
}
list[count] = NULL;
- if (count == 0)
+ if (count == 0) {
+ free(list);
return 0;
+ }
/* Complete ambigious command */
tmp = complete_ambiguous(cmd, list, count);
if (count > 1)
complete_display(list, 0);
- for (y = 0; list[y]; y++)
- xfree(list[y]);
- xfree(list);
+ for (y = 0; list[y]; y++)
+ free(list[y]);
+ free(list);
if (tmp != NULL) {
tmplen = strlen(tmp);
if (y > 0 && el_insertstr(el, argterm) == -1)
fatal("el_insertstr failed.");
}
- xfree(tmp);
+ free(tmp);
}
return count;
return -1;
for (i = 0; cmds[i].c; i++) {
- if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
+ if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
return cmds[i].t;
}
char *file, int remote, int lastarg, char quote, int terminated)
{
glob_t g;
- char *tmp, *tmp2, ins[3];
- u_int i, hadglob, pwdlen, len, tmplen, filelen;
+ char *tmp, *tmp2, ins[8];
+ u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
+ int clen;
const LineInfo *lf;
-
+
/* Glob from "file" location */
if (file == NULL)
tmp = xstrdup("*");
else
xasprintf(&tmp, "%s*", file);
+ /* Check if the path is absolute. */
+ isabs = tmp[0] == '/';
+
memset(&g, 0, sizeof(g));
if (remote != LOCAL) {
tmp = make_absolute(tmp, remote_path);
remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
- } else
+ } else
glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
-
+
/* Determine length of pwd so we can trim completion display */
for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
/* Terminate counting on first unescaped glob metacharacter */
if (tmp[tmplen] == '/')
pwdlen = tmplen + 1; /* track last seen '/' */
}
- xfree(tmp);
+ free(tmp);
+ tmp = NULL;
- if (g.gl_matchc == 0)
+ if (g.gl_matchc == 0)
goto out;
if (g.gl_matchc > 1)
complete_display(g.gl_pathv, pwdlen);
- tmp = NULL;
/* Don't try to extend globs */
if (file == NULL || hadglob)
goto out;
tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
- tmp = path_strip(tmp2, remote_path);
- xfree(tmp2);
+ tmp = path_strip(tmp2, isabs ? NULL : remote_path);
+ free(tmp2);
if (tmp == NULL)
goto out;
tmplen = strlen(tmp);
filelen = strlen(file);
- if (tmplen > filelen) {
- tmp2 = tmp + filelen;
- len = strlen(tmp2);
+ /* Count the number of escaped characters in the input string. */
+ cesc = isesc = 0;
+ for (i = 0; i < filelen; i++) {
+ if (!isesc && file[i] == '\\' && i + 1 < filelen){
+ isesc = 1;
+ cesc++;
+ } else
+ isesc = 0;
+ }
+
+ if (tmplen > (filelen - cesc)) {
+ tmp2 = tmp + filelen - cesc;
+ len = strlen(tmp2);
/* quote argument on way out */
- for (i = 0; i < len; i++) {
+ for (i = 0; i < len; i += clen) {
+ if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
+ (size_t)clen > sizeof(ins) - 2)
+ fatal("invalid multibyte character");
ins[0] = '\\';
- ins[1] = tmp2[i];
- ins[2] = '\0';
+ memcpy(ins + 1, tmp2 + i, clen);
+ ins[clen + 1] = '\0';
switch (tmp2[i]) {
case '\'':
case '"':
case '\t':
case '[':
case ' ':
+ case '#':
+ case '*':
if (quote == '\0' || tmp2[i] == quote) {
if (el_insertstr(el, ins) == -1)
fatal("el_insertstr "
lf = el_line(el);
if (g.gl_matchc == 1) {
i = 0;
- if (!terminated)
+ if (!terminated && quote != '\0')
ins[i++] = quote;
if (*(lf->cursor - 1) != '/' &&
(lastarg || *(lf->cursor) != ' '))
if (i > 0 && el_insertstr(el, ins) == -1)
fatal("el_insertstr failed.");
}
- xfree(tmp);
+ free(tmp);
out:
globfree(&g);
static unsigned char
complete(EditLine *el, int ch)
{
- char **argv, *line, quote;
- u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
+ char **argv, *line, quote;
+ int argc, carg;
+ u_int cursor, len, terminated, ret = CC_ERROR;
const LineInfo *lf;
struct complete_ctx *complete_ctx;
/* Figure out which argument the cursor points to */
cursor = lf->cursor - lf->buffer;
- line = (char *)xmalloc(cursor + 1);
+ line = xmalloc(cursor + 1);
memcpy(line, lf->buffer, cursor);
line[cursor] = '\0';
argv = makeargv(line, &carg, 1, "e, &terminated);
- xfree(line);
+ free(line);
/* Get all the arguments on the line */
len = lf->lastchar - lf->buffer;
- line = (char *)xmalloc(len + 1);
+ line = xmalloc(len + 1);
memcpy(line, lf->buffer, len);
line[len] = '\0';
argv = makeargv(line, &argc, 1, NULL, NULL);
/* Ensure cursor is at EOL or a argument boundary */
if (line[cursor] != ' ' && line[cursor] != '\0' &&
line[cursor] != '\n') {
- xfree(line);
+ free(line);
return ret;
}
} else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
/* Handle the command parsing */
if (complete_cmd_parse(el, argv[0], argc == carg,
- quote, terminated) != 0)
+ quote, terminated) != 0)
ret = CC_REDISPLAY;
} else if (carg >= 1) {
/* Handle file parsing */
if (remote != 0 &&
complete_match(el, complete_ctx->conn,
*complete_ctx->remote_pathp, filematch,
- remote, carg == argc, quote, terminated) != 0)
+ remote, carg == argc, quote, terminated) != 0)
ret = CC_REDISPLAY;
}
- xfree(line);
+ free(line);
return ret;
}
#endif /* USE_LIBEDIT */
el_source(el, NULL);
/* Tab Completion */
- el_set(el, EL_ADDFN, "ftp-complete",
+ el_set(el, EL_ADDFN, "ftp-complete",
"Context sensitive argument completion", complete);
complete_ctx.conn = conn;
complete_ctx.remote_pathp = &remote_path;
el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
+ /* enable ctrl-left-arrow and ctrl-right-arrow */
+ el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
+ el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
+ el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
+ el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
+ /* make ^w match ksh behaviour */
+ el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
}
#endif /* USE_LIBEDIT */
dir = make_absolute(dir, remote_path);
if (remote_is_dir(conn, dir) && file2 == NULL) {
- printf("Changing to: %s\n", dir);
+ if (!quiet)
+ printf("Changing to: %s\n", dir);
snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
if (parse_dispatch_command(conn, cmd,
&remote_path, 1) != 0) {
- xfree(dir);
- xfree(remote_path);
- xfree(conn);
+ free(dir);
+ free(remote_path);
+ free(conn);
return (-1);
}
} else {
- if (file2 == NULL)
- snprintf(cmd, sizeof cmd, "get %s", dir);
- else
- snprintf(cmd, sizeof cmd, "get %s %s", dir,
- file2);
-
+ /* XXX this is wrong wrt quoting */
+ snprintf(cmd, sizeof cmd, "get%s %s%s%s",
+ global_aflag ? " -a" : "", dir,
+ file2 == NULL ? "" : " ",
+ file2 == NULL ? "" : file2);
err = parse_dispatch_command(conn, cmd,
&remote_path, 1);
- xfree(dir);
- xfree(remote_path);
- xfree(conn);
+ free(dir);
+ free(remote_path);
+ free(conn);
return (err);
}
- xfree(dir);
+ free(dir);
}
-#if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
setvbuf(stdout, NULL, _IOLBF, 0);
setvbuf(infile, NULL, _IOLBF, 0);
-#else
- setlinebuf(stdout);
- setlinebuf(infile);
-#endif
interactive = !batchmode && isatty(STDIN_FILENO);
err = 0;
if (err != 0)
break;
}
- xfree(remote_path);
- xfree(conn);
+ free(remote_path);
+ free(conn);
#ifdef USE_LIBEDIT
if (el != NULL)
extern char *__progname;
fprintf(stderr,
- "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
+ "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
" [-D sftp_server_path] [-F ssh_config] "
"[-i identity_file] [-l limit]\n"
" [-o ssh_option] [-P port] [-R num_requests] "
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
+ setlocale(LC_CTYPE, "");
__progname = ssh_get_progname(argv[0]);
memset(&args, '\0', sizeof(args));
infile = stdin;
while ((ch = getopt(argc, argv,
- "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
+ "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
switch (ch) {
/* Passed through to ssh(1) */
case '4':
addargs(&args, "%s", optarg);
break;
case 'q':
+ ll = SYSLOG_LEVEL_ERROR;
+ quiet = 1;
showprogress = 0;
addargs(&args, "-%c", ch);
break;
case '2':
sshver = 2;
break;
+ case 'a':
+ global_aflag = 1;
+ break;
case 'B':
copy_buffer_len = strtol(optarg, &cp, 10);
if (copy_buffer_len == 0 || *cp != '\0')
(infile = fopen(optarg, "r")) == NULL)
fatal("%s (%s).", strerror(errno), optarg);
showprogress = 0;
- batchmode = 1;
+ quiet = batchmode = 1;
addargs(&args, "-obatchmode yes");
break;
+ case 'f':
+ global_fflag = 1;
+ break;
case 'p':
global_pflag = 1;
break;
if (conn == NULL)
fatal("Couldn't initialise connection to server");
- if (!batchmode) {
+ if (!quiet) {
if (sftp_direct == NULL)
fprintf(stderr, "Connected to %s.\n", host);
else