1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-03-23 15:09:24 -05:00

Implement mget and mput in PSFTP, supporting wildcards.

[originally from svn r4991]
[this svn revision also touched putty-wishlist]
This commit is contained in:
Simon Tatham 2004-12-16 17:35:20 +00:00
parent bee5812a49
commit a4fe439184
3 changed files with 264 additions and 96 deletions

View File

@ -291,6 +291,26 @@ have to use the \c{--} special argument, which stops \c{put} from
interpreting anything as a switch after it. For example, \cq{put -- interpreting anything as a switch after it. For example, \cq{put --
-silly-name-}.) -silly-name-}.)
\S{psftp-cmd-mgetput} The \c{mget} and \c{mput} commands: fetch or
send multiple files
\c{mget} works almost exactly like \c{get}, except that it allows
you to specify more than one file to fetch at once. You can do this
in two ways:
\b by giving two or more explicit file names (\cq{mget file1.txt
file2.txt})
\b by using a wildcard (\cq{mget *.txt}).
Every argument to \c{mget} is treated as the name of a file to fetch
(unlike \c{get}, which will interpret at most one argument like
that, and a second argument will be treated as an alternative name
under which to store the retrieved file), or a wildcard expression
matching more than one file.
\c{mput} is similar to \c{put}, with the same differences.
\S{psftp-cmd-regetput} The \c{reget} and \c{reput} commands: \S{psftp-cmd-regetput} The \c{reget} and \c{reput} commands:
resuming file transfers resuming file transfers

195
psftp.c
View File

@ -200,7 +200,8 @@ static int bare_name_compare(const void *av, const void *bv)
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
* The meat of the `get' and `put' commands. * The meat of the `get' and `put' commands.
*/ */
int sftp_get_file(char *fname, char *outfname, int recurse, int restart) int sftp_get_file(char *fname, char *outfname, int recurse, int restart,
char *wildcard)
{ {
struct fxp_handle *fh; struct fxp_handle *fh;
struct sftp_packet *pktin; struct sftp_packet *pktin;
@ -215,17 +216,22 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
* (If we're not in recursive mode, we need not even check: the * (If we're not in recursive mode, we need not even check: the
* subsequent FXP_OPEN will return a usable error message.) * subsequent FXP_OPEN will return a usable error message.)
*/ */
if (recurse) { if (wildcard || recurse) {
struct fxp_attrs attrs; struct fxp_attrs attrs;
int result; int result;
if (!wildcard) {
sftp_register(req = fxp_stat_send(fname)); sftp_register(req = fxp_stat_send(fname));
rreq = sftp_find_request(pktin = sftp_recv()); rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req); assert(rreq == req);
result = fxp_stat_recv(pktin, rreq, &attrs); result = fxp_stat_recv(pktin, rreq, &attrs);
if (result && } else
result = 0; /* placate optimisers */
if (wildcard ||
(result &&
(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
(attrs.permissions & 0040000)) { (attrs.permissions & 0040000))) {
struct fxp_handle *dirhandle; struct fxp_handle *dirhandle;
int nnames, namesize; int nnames, namesize;
@ -235,9 +241,11 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
/* /*
* First, attempt to create the destination directory, * First, attempt to create the destination directory,
* unless it already exists. * unless it already exists (or this is a wildcard
* run).
*/ */
if (file_type(outfname) != FILE_TYPE_DIRECTORY && if (!wildcard &&
file_type(outfname) != FILE_TYPE_DIRECTORY &&
!create_directory(outfname)) { !create_directory(outfname)) {
printf("%s: Cannot create directory\n", outfname); printf("%s: Cannot create directory\n", outfname);
return 0; return 0;
@ -283,7 +291,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
ournames = sresize(ournames, namesize, struct fxp_name *); ournames = sresize(ournames, namesize, struct fxp_name *);
} }
for (i = 0; i < names->nnames; i++) for (i = 0; i < names->nnames; i++)
if (!is_dots(names->names[i].filename)) if (!is_dots(names->names[i].filename) &&
(!wildcard || wc_match(wildcard,
names->names[i].filename)))
ournames[nnames++] = fxp_dup_name(&names->names[i]); ournames[nnames++] = fxp_dup_name(&names->names[i]);
fxp_free_names(names); fxp_free_names(names);
} }
@ -292,6 +302,14 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
assert(rreq == req); assert(rreq == req);
fxp_close_recv(pktin, rreq); fxp_close_recv(pktin, rreq);
/*
* A polite warning if nothing at all matched the
* wildcard.
*/
if (wildcard && !nnames) {
printf("%s: nothing matched\n", wildcard);
}
/* /*
* Sort the names into a clear order. This ought to * Sort the names into a clear order. This ought to
* make things more predictable when we're doing a * make things more predictable when we're doing a
@ -313,7 +331,11 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
while (i < nnames) { while (i < nnames) {
char *nextoutfname; char *nextoutfname;
int ret; int ret;
nextoutfname = dir_file_cat(outfname, ournames[i]->filename); if (outfname)
nextoutfname = dir_file_cat(outfname,
ournames[i]->filename);
else
nextoutfname = dupstr(ournames[i]->filename);
ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT); ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT);
sfree(nextoutfname); sfree(nextoutfname);
if (ret) if (ret)
@ -334,8 +356,13 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
int ret; int ret;
nextfname = dupcat(fname, "/", ournames[i]->filename, NULL); nextfname = dupcat(fname, "/", ournames[i]->filename, NULL);
nextoutfname = dir_file_cat(outfname, ournames[i]->filename); if (outfname)
ret = sftp_get_file(nextfname, nextoutfname, recurse, restart); nextoutfname = dir_file_cat(outfname,
ournames[i]->filename);
else
nextoutfname = dupstr(ournames[i]->filename);
ret = sftp_get_file(nextfname, nextoutfname,
recurse, restart, NULL);
restart = FALSE; /* after first partial file, do full */ restart = FALSE; /* after first partial file, do full */
sfree(nextoutfname); sfree(nextoutfname);
sfree(nextfname); sfree(nextfname);
@ -453,7 +480,8 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
return ret; return ret;
} }
int sftp_put_file(char *fname, char *outfname, int recurse, int restart) int sftp_put_file(char *fname, char *outfname, int recurse, int restart,
char *wildcard)
{ {
struct fxp_handle *fh; struct fxp_handle *fh;
struct fxp_xfer *xfer; struct fxp_xfer *xfer;
@ -468,7 +496,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
* (If we're not in recursive mode, we need not even check: the * (If we're not in recursive mode, we need not even check: the
* subsequent fopen will return an error message.) * subsequent fopen will return an error message.)
*/ */
if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) { if (wildcard || (recurse && file_type(fname) == FILE_TYPE_DIRECTORY)) {
struct fxp_attrs attrs; struct fxp_attrs attrs;
int result; int result;
int nnames, namesize; int nnames, namesize;
@ -476,6 +504,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
DirHandle *dh; DirHandle *dh;
int i; int i;
if (!wildcard) {
/* /*
* First, attempt to create the destination directory, * First, attempt to create the destination directory,
* unless it already exists. * unless it already exists.
@ -493,21 +522,38 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
result = fxp_mkdir_recv(pktin, rreq); result = fxp_mkdir_recv(pktin, rreq);
if (!result) { if (!result) {
printf("%s: create directory: %s\n", outfname, fxp_error()); printf("%s: create directory: %s\n",
outfname, fxp_error());
return 0; return 0;
} }
} }
}
/* /*
* Now get the list of filenames in the local directory. * Now get the list of filenames in the local directory.
*/ */
nnames = namesize = 0;
ournames = NULL;
if (wildcard) {
WildcardMatcher *wcm;
wcm = begin_wildcard_matching(wildcard);
if (wcm) {
while ((name = wildcard_get_filename(wcm)) != NULL) {
if (nnames >= namesize) {
namesize += 128;
ournames = sresize(ournames, namesize, char *);
}
ournames[nnames++] = name;
}
finish_wildcard_matching(wcm);
}
} else {
dh = open_directory(fname); dh = open_directory(fname);
if (!dh) { if (!dh) {
printf("%s: unable to open directory\n", fname); printf("%s: unable to open directory\n", fname);
return 0; return 0;
} }
nnames = namesize = 0;
ournames = NULL;
while ((name = read_filename(dh)) != NULL) { while ((name = read_filename(dh)) != NULL) {
if (nnames >= namesize) { if (nnames >= namesize) {
namesize += 128; namesize += 128;
@ -516,6 +562,15 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
ournames[nnames++] = name; ournames[nnames++] = name;
} }
close_directory(dh); close_directory(dh);
}
/*
* A polite warning if nothing at all matched the
* wildcard.
*/
if (wildcard && !nnames) {
printf("%s: nothing matched\n", wildcard);
}
/* /*
* Sort the names into a clear order. This ought to make * Sort the names into a clear order. This ought to make
@ -559,9 +614,13 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
char *nextfname, *nextoutfname; char *nextfname, *nextoutfname;
int ret; int ret;
if (fname)
nextfname = dir_file_cat(fname, ournames[i]); nextfname = dir_file_cat(fname, ournames[i]);
else
nextfname = dupstr(ournames[i]);
nextoutfname = dupcat(outfname, "/", ournames[i], NULL); nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
ret = sftp_put_file(nextfname, nextoutfname, recurse, restart); ret = sftp_put_file(nextfname, nextoutfname,
recurse, restart, NULL);
restart = FALSE; /* after first partial file, do full */ restart = FALSE; /* after first partial file, do full */
sfree(nextoutfname); sfree(nextoutfname);
sfree(nextfname); sfree(nextfname);
@ -870,14 +929,17 @@ int sftp_cmd_pwd(struct sftp_command *cmd)
} }
/* /*
* Get a file and save it at the local end. We have two very * Get a file and save it at the local end. We have three very
* similar commands here: `get' and `reget', which differ in that * similar commands here. The basic one is `get'; `reget' differs
* `reget' checks for the existence of the destination file and * in that it checks for the existence of the destination file and
* starts from where a previous aborted transfer left off. * starts from where a previous aborted transfer left off; `mget'
* differs in that it interprets all its arguments as files to
* transfer (never as a different local name for a remote file) and
* can handle wildcards.
*/ */
int sftp_general_get(struct sftp_command *cmd, int restart) int sftp_general_get(struct sftp_command *cmd, int restart, int multiple)
{ {
char *fname, *origfname, *outfname; char *fname, *unwcfname, *origfname, *outfname;
int i, ret; int i, ret;
int recurse = FALSE; int recurse = FALSE;
@ -906,38 +968,62 @@ int sftp_general_get(struct sftp_command *cmd, int restart)
return 0; return 0;
} }
do {
unwcfname = NULL;
origfname = cmd->words[i++]; origfname = cmd->words[i++];
if (multiple &&
!wc_unescape(unwcfname = snewn(strlen(origfname)+1, char),
origfname)) {
ret = sftp_get_file(pwd, NULL, recurse, restart, origfname);
} else {
fname = canonify(origfname); fname = canonify(origfname);
if (!fname) { if (!fname) {
printf("%s: %s\n", origfname, fxp_error()); printf("%s: %s\n", origfname, fxp_error());
sfree(unwcfname);
return 0; return 0;
} }
outfname = (i >= cmd->nwords ? if (!multiple && i < cmd->nwords)
stripslashes(origfname, 0) : cmd->words[i++]); outfname = cmd->words[i++];
else
outfname = stripslashes(origfname, 1);
ret = sftp_get_file(fname, outfname, recurse, restart); ret = sftp_get_file(fname, outfname, recurse, restart, NULL);
sfree(fname); sfree(fname);
}
sfree(unwcfname);
if (!ret)
return ret;
} while (multiple && i < cmd->nwords);
return ret; return ret;
} }
int sftp_cmd_get(struct sftp_command *cmd) int sftp_cmd_get(struct sftp_command *cmd)
{ {
return sftp_general_get(cmd, 0); return sftp_general_get(cmd, 0, 0);
}
int sftp_cmd_mget(struct sftp_command *cmd)
{
return sftp_general_get(cmd, 0, 1);
} }
int sftp_cmd_reget(struct sftp_command *cmd) int sftp_cmd_reget(struct sftp_command *cmd)
{ {
return sftp_general_get(cmd, 1); return sftp_general_get(cmd, 1, 0);
} }
/* /*
* Send a file and store it at the remote end. We have two very * Send a file and store it at the remote end. We have three very
* similar commands here: `put' and `reput', which differ in that * similar commands here. The basic one is `put'; `reput' differs
* `reput' checks for the existence of the destination file and * in that it checks for the existence of the destination file and
* starts from where a previous aborted transfer left off. * starts from where a previous aborted transfer left off; `mput'
* differs in that it interprets all its arguments as files to
* transfer (never as a different remote name for a local file) and
* can handle wildcards.
*/ */
int sftp_general_put(struct sftp_command *cmd, int restart) int sftp_general_put(struct sftp_command *cmd, int restart, int multiple)
{ {
char *fname, *origoutfname, *outfname; char *fname, *origoutfname, *outfname;
int i, ret; int i, ret;
@ -968,28 +1054,43 @@ int sftp_general_put(struct sftp_command *cmd, int restart)
return 0; return 0;
} }
do {
fname = cmd->words[i++]; fname = cmd->words[i++];
origoutfname = (i >= cmd->nwords ?
stripslashes(fname, 1) : cmd->words[i++]); if (multiple && test_wildcard(fname, FALSE) == WCTYPE_WILDCARD) {
ret = sftp_put_file(NULL, pwd, recurse, restart, fname);
} else {
if (!multiple && i < cmd->nwords)
origoutfname = cmd->words[i++];
else
origoutfname = stripslashes(fname, 1);
outfname = canonify(origoutfname); outfname = canonify(origoutfname);
if (!outfname) { if (!outfname) {
printf("%s: %s\n", origoutfname, fxp_error()); printf("%s: %s\n", origoutfname, fxp_error());
return 0; return 0;
} }
ret = sftp_put_file(fname, outfname, recurse, restart, NULL);
ret = sftp_put_file(fname, outfname, recurse, restart);
sfree(outfname); sfree(outfname);
}
if (!ret)
return ret;
} while (multiple && i < cmd->nwords);
return ret; return ret;
} }
int sftp_cmd_put(struct sftp_command *cmd) int sftp_cmd_put(struct sftp_command *cmd)
{ {
return sftp_general_put(cmd, 0); return sftp_general_put(cmd, 0, 0);
}
int sftp_cmd_mput(struct sftp_command *cmd)
{
return sftp_general_put(cmd, 0, 1);
} }
int sftp_cmd_reput(struct sftp_command *cmd) int sftp_cmd_reput(struct sftp_command *cmd)
{ {
return sftp_general_put(cmd, 1); return sftp_general_put(cmd, 1, 0);
} }
int sftp_cmd_mkdir(struct sftp_command *cmd) int sftp_cmd_mkdir(struct sftp_command *cmd)
@ -1556,12 +1657,28 @@ static struct sftp_cmd_lookup {
"ls", TRUE, "dir", NULL, "ls", TRUE, "dir", NULL,
sftp_cmd_ls sftp_cmd_ls
}, },
{
"mget", TRUE, "download multiple files at once",
" <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
" Downloads many files from the server, storing each one under\n"
" the same name it has on the server side. You can use wildcards\n"
" such as \"*.c\" to specify lots of files at once.\n",
sftp_cmd_mget
},
{ {
"mkdir", TRUE, "create a directory on the remote server", "mkdir", TRUE, "create a directory on the remote server",
" <directory-name>\n" " <directory-name>\n"
" Creates a directory with the given name on the server.\n", " Creates a directory with the given name on the server.\n",
sftp_cmd_mkdir sftp_cmd_mkdir
}, },
{
"mput", TRUE, "upload multiple files at once",
" <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
" Uploads many files to the server, storing each one under the\n"
" same name it has on the client side. You can use wildcards\n"
" such as \"*.c\" to specify lots of files at once.\n",
sftp_cmd_mput
},
{ {
"mv", TRUE, "move or rename a file on the remote server", "mv", TRUE, "move or rename a file on the remote server",
" <source-filename> <destination-filename>\n" " <source-filename> <destination-filename>\n"

View File

@ -12,6 +12,7 @@
#include <utime.h> #include <utime.h>
#include <errno.h> #include <errno.h>
#include <assert.h> #include <assert.h>
#include <glob.h>
#include "putty.h" #include "putty.h"
#include "psftp.h" #include "psftp.h"
@ -285,30 +286,60 @@ void close_directory(DirHandle *dir)
int test_wildcard(char *name, int cmdline) int test_wildcard(char *name, int cmdline)
{ {
/*
* On Unix, we currently don't support local wildcards at all.
* We will have to do so (FIXME) once PSFTP starts implementing
* mput, but until then we can assume `cmdline' is always set.
*/
struct stat statbuf; struct stat statbuf;
assert(cmdline); if (stat(name, &statbuf) == 0) {
if (stat(name, &statbuf) < 0)
return WCTYPE_NONEXISTENT;
else
return WCTYPE_FILENAME; return WCTYPE_FILENAME;
} else if (cmdline) {
/*
* On Unix, we never need to parse wildcards coming from
* the command line, because the shell will have expanded
* them into a filename list already.
*/
return WCTYPE_NONEXISTENT;
} else {
glob_t globbed;
int ret = WCTYPE_NONEXISTENT;
if (glob(name, GLOB_ERR, NULL, &globbed) == 0) {
if (globbed.gl_pathc > 0)
ret = WCTYPE_WILDCARD;
globfree(&globbed);
}
return ret;
}
} }
/* /*
* Actually return matching file names for a local wildcard. FIXME: * Actually return matching file names for a local wildcard.
* we currently don't support this at all.
*/ */
struct WildcardMatcher { struct WildcardMatcher {
int x; glob_t globbed;
int i;
}; };
WildcardMatcher *begin_wildcard_matching(char *name) { return NULL; } WildcardMatcher *begin_wildcard_matching(char *name) {
char *wildcard_get_filename(WildcardMatcher *dir) { return NULL; } WildcardMatcher *ret = snew(WildcardMatcher);
void finish_wildcard_matching(WildcardMatcher *dir) {}
if (glob(name, 0, NULL, &ret->globbed) < 0) {
sfree(ret);
return NULL;
}
ret->i = 0;
return ret;
}
char *wildcard_get_filename(WildcardMatcher *dir) {
if (dir->i < dir->globbed.gl_pathc) {
return dupstr(dir->globbed.gl_pathv[dir->i++]);
} else
return NULL;
}
void finish_wildcard_matching(WildcardMatcher *dir) {
globfree(&dir->globbed);
sfree(dir);
}
int create_directory(char *name) int create_directory(char *name)
{ {