- Improved get_local_name() using ideas from Matt McCutchen's
authorWayne Davison <wayned@samba.org>
Sun, 15 Jan 2006 07:11:23 +0000 (07:11 +0000)
committerWayne Davison <wayned@samba.org>
Sun, 15 Jan 2006 07:11:23 +0000 (07:11 +0000)
  version:  lots more comments, and a "local name" copy now
  does a chdir to the parent dir of the destination file.
- Moved the setlocale() call prior to the parse_arguments() call.

main.c

diff --git a/main.c b/main.c
index d4963b1..b4516e2 100644 (file)
--- a/main.c
+++ b/main.c
@@ -426,57 +426,108 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char *path,
        return 0; /* not reached */
 }
 
-
-static char *get_local_name(struct file_list *flist,char *name)
+/* The receiving side operates in one of two modes:
+ *
+ * 1. it enters a directory and receives one or more files, placing them
+ * according to their names in the file-list.
+ *
+ * 2. it receives a single file and saves it using the name in the
+ * destination path instead of its file-list name.  This requires a
+ * "local name" for writing out the destination file.
+ *
+ * So, our task is to figure out what mode/local-name we need and return
+ * either a NULL for mode 1, or the local-name for mode 2.  We also
+ * change directory if there are any path components in dest_path. */
+static char *get_local_name(struct file_list *flist, char *dest_path)
 {
        STRUCT_STAT st;
-       int e;
+       char *cp;
 
-       if (verbose > 2)
-               rprintf(FINFO,"get_local_name count=%d %s\n",
-                       flist->count, NS(name));
+       if (verbose > 2) {
+               rprintf(FINFO, "get_local_name count=%d %s\n",
+                       flist->count, NS(dest_path));
+       }
 
-       if (!name)
+       if (!dest_path)
                return NULL;
 
-       if (do_stat(name,&st) == 0) {
+       /* If the destination path refers to an existing directory, enter
+        * it and use mode 1.  If there is something other than a directory
+        * at the destination path, we must be transferring one file
+        * (anything at the destination will be overwritten). */
+       if (do_stat(dest_path, &st) == 0) {
                if (S_ISDIR(st.st_mode)) {
-                       if (!push_dir(name)) {
+                       if (!push_dir(dest_path)) {
                                rsyserr(FERROR, errno, "push_dir#1 %s failed",
-                                       full_fname(name));
+                                       full_fname(dest_path));
                                exit_cleanup(RERR_FILESELECT);
                        }
                        return NULL;
                }
                if (flist->count > 1) {
-                       rprintf(FERROR,"ERROR: destination must be a directory when copying more than 1 file\n");
+                       rprintf(FERROR,
+                               "ERROR: destination must be a directory when"
+                               " copying more than 1 file\n");
                        exit_cleanup(RERR_FILESELECT);
                }
-               return name;
        }
 
-       if (flist->count <= 1 && ((e = strlen(name)) <= 1 || name[e-1] != '/'))
-               return name;
+       cp = strrchr(dest_path, '/');
 
-       if (do_mkdir(name,0777 & ~orig_umask) != 0) {
-               rsyserr(FERROR, errno, "mkdir %s failed", full_fname(name));
-               exit_cleanup(RERR_FILEIO);
-       }
-       if (verbose > 0)
-               rprintf(FINFO, "created directory %s\n", name);
+       /* If the destination path ends in a slash or we are transferring
+        * multiple files, create a directory at the destination path,
+        * enter the new directory, and use mode 1. */
+       if (flist->count > 1 || (cp && !cp[1])) {
+               /* Lop off the final slash (if any). */
+               if (cp && !cp[1])
+                       *cp = '\0';
+
+               umask(orig_umask);
+               if (do_mkdir(dest_path, 0777) != 0) {
+                       rsyserr(FERROR, errno, "mkdir %s failed",
+                               full_fname(dest_path));
+                       exit_cleanup(RERR_FILEIO);
+               }
+               umask(0);
+
+               if (verbose)
+                       rprintf(FINFO, "created directory %s\n", dest_path);
+
+               if (dry_run) {
+                       /* Indicate that the destination directory doesn't
+                        * really exist and return mode 1. */
+                       dry_run++;
+                       return NULL;
+               }
+
+               if (!push_dir(dest_path)) {
+                       rsyserr(FERROR, errno, "push_dir#2 %s failed",
+                               full_fname(dest_path));
+                       exit_cleanup(RERR_FILESELECT);
+               }
 
-       if (dry_run) {
-               dry_run++;
                return NULL;
        }
 
-       if (!push_dir(name)) {
-               rsyserr(FERROR, errno, "push_dir#2 %s failed",
-                       full_fname(name));
+       /* Otherwise, we are writing a single file, possibly on top of an
+        * existing non-directory.  Change to the item's parent directory
+        * (if it has a path component), return the basename of the
+        * destination file as the local name, and use mode 2. */
+       if (!cp)
+               return dest_path;
+
+       if (cp == dest_path)
+               dest_path = "/";
+
+       *cp = '\0';
+       if (!push_dir(dest_path)) {
+               rsyserr(FERROR, errno, "push_dir#3 %s failed",
+                       full_fname(dest_path));
                exit_cleanup(RERR_FILESELECT);
        }
+       *cp = '/';
 
-       return NULL;
+       return cp + 1;
 }
 
 
@@ -504,7 +555,7 @@ static void read_final_goodbye(int f_in, int f_out)
 }
 
 
-static void do_server_sender(int f_in, int f_out, int argc,char *argv[])
+static void do_server_sender(int f_in, int f_out, int argc, char *argv[])
 {
        struct file_list *flist;
        char *dir = argv[0];
@@ -1133,6 +1184,10 @@ int main(int argc,char *argv[])
         * carried across */
        orig_umask = (int)umask(0);
 
+#if defined CONFIG_LOCALE && defined HAVE_SETLOCALE
+       setlocale(LC_CTYPE, "");
+#endif
+
        if (!parse_arguments(&argc, (const char ***) &argv, 1)) {
                /* FIXME: We ought to call the same error-handling
                 * code here, rather than relying on getopt. */
@@ -1148,10 +1203,6 @@ int main(int argc,char *argv[])
         * see the EPIPE. */
        signal(SIGPIPE, SIG_IGN);
 
-#if defined CONFIG_LOCALE && defined HAVE_SETLOCALE
-       setlocale(LC_CTYPE, "");
-#endif
-
        /* Initialize push_dir here because on some old systems getcwd
         * (implemented by forking "pwd" and reading its output) doesn't
         * work when there are other child processes.  Also, on all systems