mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00: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:
parent
bee5812a49
commit
a4fe439184
@ -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 --
|
||||
-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:
|
||||
resuming file transfers
|
||||
|
||||
|
279
psftp.c
279
psftp.c
@ -200,7 +200,8 @@ static int bare_name_compare(const void *av, const void *bv)
|
||||
/* ----------------------------------------------------------------------
|
||||
* 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 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
|
||||
* subsequent FXP_OPEN will return a usable error message.)
|
||||
*/
|
||||
if (recurse) {
|
||||
if (wildcard || recurse) {
|
||||
struct fxp_attrs attrs;
|
||||
int result;
|
||||
|
||||
sftp_register(req = fxp_stat_send(fname));
|
||||
rreq = sftp_find_request(pktin = sftp_recv());
|
||||
assert(rreq == req);
|
||||
result = fxp_stat_recv(pktin, rreq, &attrs);
|
||||
if (result &&
|
||||
(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
|
||||
(attrs.permissions & 0040000)) {
|
||||
if (!wildcard) {
|
||||
sftp_register(req = fxp_stat_send(fname));
|
||||
rreq = sftp_find_request(pktin = sftp_recv());
|
||||
assert(rreq == req);
|
||||
result = fxp_stat_recv(pktin, rreq, &attrs);
|
||||
} else
|
||||
result = 0; /* placate optimisers */
|
||||
|
||||
if (wildcard ||
|
||||
(result &&
|
||||
(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
|
||||
(attrs.permissions & 0040000))) {
|
||||
|
||||
struct fxp_handle *dirhandle;
|
||||
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,
|
||||
* 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)) {
|
||||
printf("%s: Cannot create directory\n", outfname);
|
||||
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 *);
|
||||
}
|
||||
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]);
|
||||
fxp_free_names(names);
|
||||
}
|
||||
@ -292,6 +302,14 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
|
||||
assert(rreq == req);
|
||||
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
|
||||
* 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) {
|
||||
char *nextoutfname;
|
||||
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);
|
||||
sfree(nextoutfname);
|
||||
if (ret)
|
||||
@ -334,8 +356,13 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
|
||||
int ret;
|
||||
|
||||
nextfname = dupcat(fname, "/", ournames[i]->filename, NULL);
|
||||
nextoutfname = dir_file_cat(outfname, ournames[i]->filename);
|
||||
ret = sftp_get_file(nextfname, nextoutfname, recurse, restart);
|
||||
if (outfname)
|
||||
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 */
|
||||
sfree(nextoutfname);
|
||||
sfree(nextfname);
|
||||
@ -453,7 +480,8 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
|
||||
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_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
|
||||
* 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;
|
||||
int result;
|
||||
int nnames, namesize;
|
||||
@ -476,46 +504,73 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
|
||||
DirHandle *dh;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* First, attempt to create the destination directory,
|
||||
* unless it already exists.
|
||||
*/
|
||||
sftp_register(req = fxp_stat_send(outfname));
|
||||
rreq = sftp_find_request(pktin = sftp_recv());
|
||||
assert(rreq == req);
|
||||
result = fxp_stat_recv(pktin, rreq, &attrs);
|
||||
if (!result ||
|
||||
!(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||
|
||||
!(attrs.permissions & 0040000)) {
|
||||
sftp_register(req = fxp_mkdir_send(outfname));
|
||||
if (!wildcard) {
|
||||
/*
|
||||
* First, attempt to create the destination directory,
|
||||
* unless it already exists.
|
||||
*/
|
||||
sftp_register(req = fxp_stat_send(outfname));
|
||||
rreq = sftp_find_request(pktin = sftp_recv());
|
||||
assert(rreq == req);
|
||||
result = fxp_mkdir_recv(pktin, rreq);
|
||||
result = fxp_stat_recv(pktin, rreq, &attrs);
|
||||
if (!result ||
|
||||
!(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||
|
||||
!(attrs.permissions & 0040000)) {
|
||||
sftp_register(req = fxp_mkdir_send(outfname));
|
||||
rreq = sftp_find_request(pktin = sftp_recv());
|
||||
assert(rreq == req);
|
||||
result = fxp_mkdir_recv(pktin, rreq);
|
||||
|
||||
if (!result) {
|
||||
printf("%s: create directory: %s\n", outfname, fxp_error());
|
||||
return 0;
|
||||
if (!result) {
|
||||
printf("%s: create directory: %s\n",
|
||||
outfname, fxp_error());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now get the list of filenames in the local directory.
|
||||
*/
|
||||
dh = open_directory(fname);
|
||||
if (!dh) {
|
||||
printf("%s: unable to open directory\n", fname);
|
||||
return 0;
|
||||
}
|
||||
nnames = namesize = 0;
|
||||
ournames = NULL;
|
||||
while ((name = read_filename(dh)) != NULL) {
|
||||
if (nnames >= namesize) {
|
||||
namesize += 128;
|
||||
ournames = sresize(ournames, namesize, char *);
|
||||
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);
|
||||
}
|
||||
ournames[nnames++] = name;
|
||||
} else {
|
||||
dh = open_directory(fname);
|
||||
if (!dh) {
|
||||
printf("%s: unable to open directory\n", fname);
|
||||
return 0;
|
||||
}
|
||||
while ((name = read_filename(dh)) != NULL) {
|
||||
if (nnames >= namesize) {
|
||||
namesize += 128;
|
||||
ournames = sresize(ournames, namesize, char *);
|
||||
}
|
||||
ournames[nnames++] = name;
|
||||
}
|
||||
close_directory(dh);
|
||||
}
|
||||
|
||||
/*
|
||||
* A polite warning if nothing at all matched the
|
||||
* wildcard.
|
||||
*/
|
||||
if (wildcard && !nnames) {
|
||||
printf("%s: nothing matched\n", wildcard);
|
||||
}
|
||||
close_directory(dh);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
int ret;
|
||||
|
||||
nextfname = dir_file_cat(fname, ournames[i]);
|
||||
if (fname)
|
||||
nextfname = dir_file_cat(fname, ournames[i]);
|
||||
else
|
||||
nextfname = dupstr(ournames[i]);
|
||||
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 */
|
||||
sfree(nextoutfname);
|
||||
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
|
||||
* similar commands here: `get' and `reget', which differ in that
|
||||
* `reget' checks for the existence of the destination file and
|
||||
* starts from where a previous aborted transfer left off.
|
||||
* Get a file and save it at the local end. We have three very
|
||||
* similar commands here. The basic one is `get'; `reget' differs
|
||||
* in that it checks for the existence of the destination file and
|
||||
* 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 recurse = FALSE;
|
||||
|
||||
@ -906,38 +968,62 @@ int sftp_general_get(struct sftp_command *cmd, int restart)
|
||||
return 0;
|
||||
}
|
||||
|
||||
origfname = cmd->words[i++];
|
||||
fname = canonify(origfname);
|
||||
if (!fname) {
|
||||
printf("%s: %s\n", origfname, fxp_error());
|
||||
return 0;
|
||||
}
|
||||
do {
|
||||
unwcfname = NULL;
|
||||
origfname = cmd->words[i++];
|
||||
|
||||
outfname = (i >= cmd->nwords ?
|
||||
stripslashes(origfname, 0) : 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);
|
||||
if (!fname) {
|
||||
printf("%s: %s\n", origfname, fxp_error());
|
||||
sfree(unwcfname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = sftp_get_file(fname, outfname, recurse, restart);
|
||||
if (!multiple && i < cmd->nwords)
|
||||
outfname = cmd->words[i++];
|
||||
else
|
||||
outfname = stripslashes(origfname, 1);
|
||||
|
||||
sfree(fname);
|
||||
ret = sftp_get_file(fname, outfname, recurse, restart, NULL);
|
||||
|
||||
sfree(fname);
|
||||
}
|
||||
sfree(unwcfname);
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
} while (multiple && i < cmd->nwords);
|
||||
|
||||
return ret;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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
|
||||
* similar commands here: `put' and `reput', which differ in that
|
||||
* `reput' checks for the existence of the destination file and
|
||||
* starts from where a previous aborted transfer left off.
|
||||
* Send a file and store it at the remote end. We have three very
|
||||
* similar commands here. The basic one is `put'; `reput' differs
|
||||
* in that it checks for the existence of the destination file and
|
||||
* 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;
|
||||
int i, ret;
|
||||
@ -968,28 +1054,43 @@ int sftp_general_put(struct sftp_command *cmd, int restart)
|
||||
return 0;
|
||||
}
|
||||
|
||||
fname = cmd->words[i++];
|
||||
origoutfname = (i >= cmd->nwords ?
|
||||
stripslashes(fname, 1) : cmd->words[i++]);
|
||||
outfname = canonify(origoutfname);
|
||||
if (!outfname) {
|
||||
printf("%s: %s\n", origoutfname, fxp_error());
|
||||
return 0;
|
||||
}
|
||||
do {
|
||||
fname = cmd->words[i++];
|
||||
|
||||
ret = sftp_put_file(fname, outfname, recurse, restart);
|
||||
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);
|
||||
|
||||
sfree(outfname);
|
||||
outfname = canonify(origoutfname);
|
||||
if (!outfname) {
|
||||
printf("%s: %s\n", origoutfname, fxp_error());
|
||||
return 0;
|
||||
}
|
||||
ret = sftp_put_file(fname, outfname, recurse, restart, NULL);
|
||||
sfree(outfname);
|
||||
}
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
} while (multiple && i < cmd->nwords);
|
||||
|
||||
return ret;
|
||||
}
|
||||
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)
|
||||
{
|
||||
return sftp_general_put(cmd, 1);
|
||||
return sftp_general_put(cmd, 1, 0);
|
||||
}
|
||||
|
||||
int sftp_cmd_mkdir(struct sftp_command *cmd)
|
||||
@ -1556,12 +1657,28 @@ static struct sftp_cmd_lookup {
|
||||
"ls", TRUE, "dir", NULL,
|
||||
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",
|
||||
" <directory-name>\n"
|
||||
" Creates a directory with the given name on the server.\n",
|
||||
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",
|
||||
" <source-filename> <destination-filename>\n"
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <utime.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <glob.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "psftp.h"
|
||||
@ -285,30 +286,60 @@ void close_directory(DirHandle *dir)
|
||||
|
||||
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;
|
||||
|
||||
assert(cmdline);
|
||||
if (stat(name, &statbuf) < 0)
|
||||
return WCTYPE_NONEXISTENT;
|
||||
else
|
||||
if (stat(name, &statbuf) == 0) {
|
||||
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:
|
||||
* we currently don't support this at all.
|
||||
* Actually return matching file names for a local wildcard.
|
||||
*/
|
||||
struct WildcardMatcher {
|
||||
int x;
|
||||
glob_t globbed;
|
||||
int i;
|
||||
};
|
||||
WildcardMatcher *begin_wildcard_matching(char *name) { return NULL; }
|
||||
char *wildcard_get_filename(WildcardMatcher *dir) { return NULL; }
|
||||
void finish_wildcard_matching(WildcardMatcher *dir) {}
|
||||
WildcardMatcher *begin_wildcard_matching(char *name) {
|
||||
WildcardMatcher *ret = snew(WildcardMatcher);
|
||||
|
||||
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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user