summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libftpconn/ftpcp.c246
1 files changed, 145 insertions, 101 deletions
diff --git a/libftpconn/ftpcp.c b/libftpconn/ftpcp.c
index 88567305..03bd3d57 100644
--- a/libftpconn/ftpcp.c
+++ b/libftpconn/ftpcp.c
@@ -38,8 +38,10 @@ static struct argp_option options[] =
};
static char *args_doc = "SRC [DST]";
static char *doc = "Copy file SRC over ftp to DST."
-"\vBoth SRC and DST may have the form HOST:FILE, FILE, or -, where - is standard"
-" input for SRC or standard output for DST, and FILE is a local file.";
+"\vBoth SRC and DST may have the form HOST:FILE, FILE, or -, where - is"
+" standard input for SRC or standard output for DST, and FILE is a local"
+" file. DST may be a directory, in which case the basename of SRC is"
+" appended to make the actual destination filename.";
/* customization hooks. */
static struct ftp_conn_hooks conn_hooks = { 0 };
@@ -121,19 +123,105 @@ cp (int src, const char *src_name, int dst, const char *dst_name)
struct epoint
{
char *name; /* Name, of the form HOST:FILE, FILE, or -. */
- char *rmt_file; /* If NAME is remote, the FILE portion, or 0. */
- char *rmt_host; /* If NAME is remote, the HOST portion, or 0. */
+ char *file; /* If remote, the FILE portion, or 0. */
int fd; /* A file descriptor to use. */
struct ftp_conn *conn; /* An ftp connection to use. */
struct ftp_conn_params params;
};
+static void
+econnect (struct epoint *e, struct ftp_conn_params *def_params)
+{
+ char *rmt;
+
+ if (! e->name)
+ e->name = "-";
+
+ rmt = strchr (e->name, ':');
+ if (rmt)
+ {
+ error_t err;
+
+ *rmt++ = 0;
+
+ if (! e->params.user)
+ e->params.user = def_params->user;
+ if (! e->params.pass)
+ e->params.pass = def_params->pass;
+ if (! e->params.acct)
+ e->params.acct = def_params->acct;
+
+ e->conn = get_host_conn (e->name, &e->params, &e->name);
+ e->name = realloc (e->name, strlen (e->name) + 1 + strlen (rmt) + 1);
+ if (! e->name)
+ error (22, ENOMEM, "Cannot allocate name storage");
+
+ err = ftp_conn_set_type (e->conn, "I");
+ if (err)
+ error (23, err, "%s: Cannot set connection type to binary",
+ e->name);
+
+ strcat (e->name, ":");
+ strcat (e->name, rmt);
+
+ e->file = rmt;
+ }
+ else if (e->params.user || e->params.pass || e->params.acct)
+ error (20, 0,
+ "%s: Ftp login parameter specified for a local endpoint (%s,%s,%s)",
+ e->name, e->params.user, e->params.pass, e->params.acct);
+ else
+ e->file = strdup (e->name);
+}
+
+static error_t
+eopen_wr (struct epoint *e, int *fd)
+{
+ if (e->conn)
+ return ftp_conn_start_store (e->conn, e->file, fd);
+ else if (strcmp (e->name, "-") == 0)
+ *fd = 1;
+ else
+ {
+ *fd = open (e->name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (*fd < 0)
+ return errno;
+ }
+ return 0;
+}
+
+static error_t
+eopen_rd (struct epoint *e, int *fd)
+{
+ if (e->conn)
+ return ftp_conn_start_retrieve (e->conn, e->file, fd);
+ else if (strcmp (e->name, "-") == 0)
+ *fd = 0;
+ else
+ {
+ *fd = open (e->name, O_RDONLY, 0666);
+ if (*fd < 0)
+ return errno;
+ }
+ return 0;
+}
+
+static void
+efinish (struct epoint *e)
+{
+ if (e->conn)
+ {
+ error_t err = ftp_conn_finish_transfer (e->conn);
+ if (err)
+ error (31, err, "%s", e->name);
+ }
+}
+
int
main (int argc, char **argv)
{
- int i;
error_t err;
- struct epoint epoints[2] = { {0}, {0} };
+ struct epoint rd = { 0 }, wr = { 0 };
struct ftp_conn_params def_params = { 0 }; /* default params */
/* Parse our options... */
@@ -142,10 +230,13 @@ main (int argc, char **argv)
switch (key)
{
case ARGP_KEY_ARG:
- if (state->arg_num < 2)
- epoints[state->arg_num].name = arg;
+ switch (state->arg_num)
+ {
+ case 0: rd.name = arg; break;
+ case 1: wr.name = arg; break;
+ default: return ARGP_ERR_UNKNOWN;
+ }
break;
-
case ARGP_KEY_NO_ARGS:
argp_usage (state);
@@ -153,13 +244,13 @@ main (int argc, char **argv)
case 'p': def_params.pass = arg; break;
case 'a': def_params.acct = arg; break;
- case OPT_SRC_U: epoints[0].params.user = arg; break;
- case OPT_SRC_P: epoints[0].params.pass = arg; break;
- case OPT_SRC_A: epoints[0].params.acct = arg; break;
+ case OPT_SRC_U: rd.params.user = arg; break;
+ case OPT_SRC_P: rd.params.pass = arg; break;
+ case OPT_SRC_A: rd.params.acct = arg; break;
- case OPT_DST_U: epoints[1].params.user = arg; break;
- case OPT_DST_P: epoints[1].params.pass = arg; break;
- case OPT_DST_A: epoints[1].params.acct = arg; break;
+ case OPT_DST_U: wr.params.user = arg; break;
+ case OPT_DST_P: wr.params.pass = arg; break;
+ case OPT_DST_A: wr.params.acct = arg; break;
case 'D': conn_hooks.cntl_debug = cntl_debug; break;
@@ -172,100 +263,53 @@ main (int argc, char **argv)
argp_parse (&argp, argc, argv, 0, 0, 0);
- for (i = 0; i < 2; i++)
- {
- char *rmt;
-
- if (! epoints[i].name)
- epoints[i].name = "-";
-
- rmt = strchr (epoints[i].name, ':');
- if (rmt)
- {
- *rmt++ = 0;
-
- if (! epoints[i].params.user)
- epoints[i].params.user = def_params.user;
- if (! epoints[i].params.pass)
- epoints[i].params.pass = def_params.pass;
- if (! epoints[i].params.acct)
- epoints[i].params.acct = def_params.acct;
-
- epoints[i].conn =
- get_host_conn (epoints[i].name, &epoints[i].params,
- &epoints[i].name);
- epoints[i].name =
- realloc (epoints[i].name,
- strlen (epoints[i].name) + 1 + strlen (rmt) + 1);
- if (! epoints[i].name)
- error (22, ENOMEM, "Cannot allocate name storage");
-
- err = ftp_conn_set_type (epoints[i].conn, "I");
- if (err)
- error (23, err, "%s: Cannot set connection type to binary",
- epoints[i].name);
-
- strcat (epoints[i].name, ":");
- strcat (epoints[i].name, rmt);
-
- epoints[i].rmt_file = rmt;
- }
- else if (epoints[i].params.user
- || epoints[i].params.pass
- || epoints[i].params.acct)
- error (20, 0,
- "%s: Ftp login parameter specified for a local endpoint (%s,%s,%s)",
- epoints[i].name,
- epoints[i].params.user,
- epoints[i].params.pass,
- epoints[i].params.acct);
- }
+ econnect (&rd, &def_params);
+ econnect (&wr, &def_params);
- if (epoints[0].conn && epoints[1].conn)
+ if (rd.conn && wr.conn)
{
- err = ftp_conn_rmt_copy (epoints[0].conn, epoints[0].rmt_file,
- epoints[1].conn, epoints[1].rmt_file);
+ err = ftp_conn_rmt_copy (rd.conn, rd.file, wr.conn, wr.file);
if (err)
error (30, err, "Remote copy");
}
else
{
- for (i = 0; i < 2; i++)
- if (epoints[i].conn)
- {
- if (i == 0)
- err = ftp_conn_start_retrieve (epoints[i].conn,
- epoints[i].rmt_file,
- &epoints[i].fd);
- else
- err = ftp_conn_start_store (epoints[i].conn,
- epoints[i].rmt_file,
- &epoints[i].fd);
- if (err)
- error (31, err, "%s", epoints[i].name);
- }
- else if (strcmp (epoints[i].name, "-") == 0)
- epoints[i].fd = i;
- else
- {
- int flags = (i == 0) ? O_RDONLY : (O_WRONLY | O_CREAT | O_TRUNC);
- epoints[i].fd = open (epoints[i].name, flags, 0666);
- if (epoints[i].fd < 0)
- error (32, errno, "%s", epoints[i].name);
- }
-
- cp (epoints[0].fd, epoints[0].name, epoints[1].fd, epoints[1].name);
-
- for (i = 0; i < 2; i++)
+ int rd_fd, wr_fd;
+
+ err = eopen_rd (&rd, &rd_fd);
+ if (err)
+ error (31, err, "%s", rd.name);
+
+ err = eopen_wr (&wr, &wr_fd);
+ if (err == EISDIR)
+ /* The destination name is a directory; try again with the source
+ basename appended. */
{
- close (epoints[i].fd);
- if (epoints[i].conn)
- {
- err = ftp_conn_finish_transfer (epoints[i].conn);
- if (err)
- error (31, err, "%s", epoints[i].name);
- }
- }
+ char *bname = basename (rd.file);
+ size_t bname_len = strlen (bname);
+ char *dir = wr.file;
+ char *name = malloc (strlen (dir) + 1 + bname_len + 1);
+
+ if (! name)
+ error (99, ENOMEM, "%s", dir);
+
+ strcpy (name, dir);
+ strcat (name, "/");
+ strcat (name, bname);
+ wr.file = name;
+
+ err = eopen_wr (&wr, &wr_fd);
+ }
+ if (err)
+ error (32, err, "%s", wr.name);
+
+ cp (rd_fd, rd.name, wr_fd, wr.name);
+
+ close (rd_fd);
+ close (wr_fd);
+
+ efinish (&rd);
+ efinish (&wr);
}
exit (0);