Moved list-only output from flist.c to generator.c so that it works
[rsync/rsync.git] / flist.c
diff --git a/flist.c b/flist.c
index 6642cf2..1452d6c 100644 (file)
--- a/flist.c
+++ b/flist.c
  */
 
 #include "rsync.h"
+#include "ifuncs.h"
 #include "rounding.h"
 #include "io.h"
 
 extern int verbose;
-extern int list_only;
 extern int am_root;
 extern int am_server;
 extern int am_daemon;
@@ -75,6 +75,7 @@ extern struct filter_list_struct server_filter_list;
 
 #ifdef ICONV_OPTION
 extern int ic_ndx;
+extern int filesfrom_convert;
 extern int need_unsorted_flist;
 extern iconv_t ic_send, ic_recv;
 #endif
@@ -115,6 +116,7 @@ static char tmp_sum[MAX_DIGEST_LEN];
 static char empty_sum[MAX_DIGEST_LEN];
 static int flist_count_offset; /* for --delete --progress */
 static int dir_count = 0;
+static int high_hlink_ndx;
 
 static void clean_flist(struct file_list *flist, int strip_root);
 static void output_flist(struct file_list *flist);
@@ -169,35 +171,6 @@ void show_flist_stats(void)
        /* Nothing yet */
 }
 
-static void list_file_entry(struct file_struct *f)
-{
-       char permbuf[PERMSTRING_SIZE];
-       double len;
-
-       if (!F_IS_ACTIVE(f)) {
-               /* this can happen if duplicate names were removed */
-               return;
-       }
-
-       permstring(permbuf, f->mode);
-       len = F_LENGTH(f);
-
-       /* TODO: indicate '+' if the entry has an ACL. */
-
-#ifdef SUPPORT_LINKS
-       if (preserve_links && S_ISLNK(f->mode)) {
-               rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
-                       permbuf, len, timestring(f->modtime),
-                       f_name(f, NULL), F_SYMLINK(f));
-       } else
-#endif
-       {
-               rprintf(FINFO, "%s %11.0f %s %s\n",
-                       permbuf, len, timestring(f->modtime),
-                       f_name(f, NULL));
-       }
-}
-
 /* Stat either a symlink or its referent, depending on the settings of
  * copy_links, copy_unsafe_links, etc.  Returns -1 on error, 0 on success.
  *
@@ -376,22 +349,21 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
 
 #ifdef ICONV_OPTION
        if (ic_send != (iconv_t)-1) {
-               ICONV_CONST char *ibuf;
-               char *obuf = fname;
-               size_t ocnt = MAXPATHLEN, icnt;
-
-               iconv(ic_send, NULL,0, NULL,0);
-               if ((ibuf = (ICONV_CONST char *)file->dirname) != NULL) {
-                   icnt = strlen(ibuf);
-                   ocnt--; /* pre-subtract the space for the '/' */
-                   if (iconv(ic_send, &ibuf,&icnt, &obuf,&ocnt) == (size_t)-1)
-                       goto convert_error;
-                   *obuf++ = '/';
-               }
-
-               ibuf = (ICONV_CONST char *)file->basename;
-               icnt = strlen(ibuf);
-               if (iconv(ic_send, &ibuf,&icnt, &obuf,&ocnt) == (size_t)-1) {
+               xbuf outbuf, inbuf;
+
+               INIT_CONST_XBUF(outbuf, fname);
+
+               if (file->dirname) {
+                       INIT_XBUF_STRLEN(inbuf, (char*)file->dirname);
+                       outbuf.size -= 2; /* Reserve room for '/' & 1 more char. */
+                       if (iconvbufs(ic_send, &inbuf, &outbuf, 0) < 0)
+                               goto convert_error;
+                       outbuf.size += 2;
+                       outbuf.buf[outbuf.len++] = '/';
+               }
+
+               INIT_XBUF_STRLEN(inbuf, (char*)file->basename);
+               if (iconvbufs(ic_send, &inbuf, &outbuf, 0) < 0) {
                  convert_error:
                        io_error |= IOERR_GENERAL;
                        rprintf(FINFO,
@@ -399,7 +371,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
                            who_am_i(), f_name(file, fname), strerror(errno));
                        return;
                }
-               *obuf = '\0';
+               outbuf.buf[outbuf.len] = '\0';
        } else
 #endif
                f_name(file, fname);
@@ -467,7 +439,8 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
                        struct ht_int64_node *np = idev_find(tmp_dev, tmp_ino);
                        first_hlink_ndx = (int32)(long)np->data - 1;
                        if (first_hlink_ndx < 0) {
-                               np->data = (void*)(long)(ndx + first_ndx + 1);
+                               high_hlink_ndx = ndx + first_ndx;
+                               np->data = (void*)(long)(high_hlink_ndx + 1);
                                xflags |= XMIT_HLINK_FIRST;
                        }
                        xflags |= XMIT_HLINKED;
@@ -665,25 +638,19 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
 
 #ifdef ICONV_OPTION
        if (ic_recv != (iconv_t)-1) {
-               char *obuf = thisname;
-               ICONV_CONST char *ibuf = (ICONV_CONST char *)lastname;
-               size_t ocnt = MAXPATHLEN, icnt = basename_len;
+               xbuf outbuf, inbuf;
 
-               if (icnt >= MAXPATHLEN) {
-                       errno = E2BIG;
-                       goto convert_error;
-               }
+               INIT_CONST_XBUF(outbuf, thisname);
+               INIT_XBUF(inbuf, lastname, basename_len, -1);
 
-               iconv(ic_recv, NULL,0, NULL,0);
-               if (iconv(ic_recv, &ibuf,&icnt, &obuf,&ocnt) == (size_t)-1) {
-                 convert_error:
+               if (iconvbufs(ic_recv, &inbuf, &outbuf, 0) < 0) {
                        io_error |= IOERR_GENERAL;
                        rprintf(FINFO,
                            "[%s] cannot convert filename: %s (%s)\n",
                            who_am_i(), lastname, strerror(errno));
-                       obuf = thisname;
+                       outbuf.len = 0;
                }
-               *obuf = '\0';
+               outbuf.buf[outbuf.len] = '\0';
        }
 #endif
 
@@ -941,9 +908,11 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
 #ifdef SUPPORT_HARD_LINKS
        if (preserve_hard_links && xflags & XMIT_HLINKED) {
                if (protocol_version >= 30) {
-                       F_HL_GNUM(file) = xflags & XMIT_HLINK_FIRST
-                                       ? flist->ndx_start + flist->used
-                                       : first_hlink_ndx;
+                       if (xflags & XMIT_HLINK_FIRST) {
+                               high_hlink_ndx = flist->ndx_start + flist->used;
+                               F_HL_GNUM(file) = high_hlink_ndx;
+                       } else
+                               F_HL_GNUM(file) = first_hlink_ndx;
                } else {
                        static int32 cnt = 0;
                        struct ht_int64_node *np;
@@ -1078,16 +1047,19 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
        /* -x only affects directories because we need to avoid recursing
         * into a mount-point directory, not to avoid copying a symlinked
         * file if -L (or similar) was specified. */
-       if (one_file_system && st.st_dev != filesystem_dev
-        && S_ISDIR(st.st_mode)) {
-               if (one_file_system > 1) {
-                       if (verbose > 2) {
-                               rprintf(FINFO, "skipping mount-point dir %s\n",
-                                       thisname);
+       if (one_file_system && S_ISDIR(st.st_mode)) {
+               if (flags & FLAG_TOP_DIR)
+                       filesystem_dev = st.st_dev;
+               else if (st.st_dev != filesystem_dev) {
+                       if (one_file_system > 1) {
+                               if (verbose > 2) {
+                                       rprintf(FINFO, "skipping mount-point dir %s\n",
+                                               thisname);
+                               }
+                               return NULL;
                        }
-                       return NULL;
+                       flags |= FLAG_MOUNT_DIR;
                }
-               flags |= FLAG_MOUNT_DIR;
        }
 
        if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) {
@@ -1112,6 +1084,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                 && flags & FLAG_DIVERT_DIRS) {
                        /* Room for parent/sibling/next-child info. */
                        extra_len += DIRNODE_EXTRA_CNT * EXTRA_LEN;
+                       if (relative_paths)
+                               extra_len += PTR_EXTRA_CNT * EXTRA_LEN;
                        dir_count++;
                        pool = dir_flist->file_pool;
                } else
@@ -1425,8 +1399,7 @@ static void add_dirs_to_tree(int parent_ndx, struct file_list *from_flist,
                dir_flist->files[dir_flist->used++] = file;
                dir_cnt--;
 
-               if (!(file->flags & FLAG_XFER_DIR)
-                || file->flags & FLAG_MOUNT_DIR)
+               if (file->flags & FLAG_MOUNT_DIR)
                        continue;
 
                if (dp)
@@ -1514,10 +1487,86 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
        }
 }
 
+static char lastpath[MAXPATHLEN] = "";
+static int lastpath_len = 0;
+static struct file_struct *lastpath_struct;
+
+static void send_implied_dirs(int f, struct file_list *flist, char *fname,
+                             char *start, char *limit, int flags, int is_dot_dir)
+{
+       struct file_struct *file;
+       item_list *rel_list;
+       char **ep, *slash;
+       int len, need_new_dir;
+
+       flags &= ~FLAG_XFER_DIR;
+
+       if (inc_recurse) {
+               if (lastpath_struct && F_PATHNAME(lastpath_struct) == pathname
+                && lastpath_len == limit - fname
+                && strncmp(lastpath, fname, lastpath_len) == 0)
+                       need_new_dir = 0;
+               else
+                       need_new_dir = 1;
+       } else
+               need_new_dir = 1;
+
+       if (need_new_dir) {
+               int save_copy_links = copy_links;
+               int save_xfer_dirs = xfer_dirs;
+
+               copy_links = xfer_dirs = 1;
+
+               *limit = '\0';
+
+               for (slash = start; (slash = strchr(slash+1, '/')) != NULL; ) {
+                       *slash = '\0';
+                       send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+                       *slash = '/';
+               }
+
+               file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+               if (inc_recurse) {
+                       if (file && !S_ISDIR(file->mode))
+                               file = NULL;
+                       lastpath_struct = file;
+               }
+
+               strlcpy(lastpath, fname, sizeof lastpath);
+               lastpath_len = limit - fname;
+
+               *limit = '/';
+
+               copy_links = save_copy_links;
+               xfer_dirs = save_xfer_dirs;
+
+               if (!inc_recurse)
+                       return;
+       }
+
+       if (!lastpath_struct)
+               return; /* dir must have vanished */
+
+       len = strlen(limit+1);
+       memcpy(&rel_list, F_DIR_RELS_P(lastpath_struct), sizeof rel_list);
+       if (!rel_list) {
+               if (!(rel_list = new0(item_list)))
+                       out_of_memory("send_implied_dirs");
+               memcpy(F_DIR_RELS_P(lastpath_struct), &rel_list, sizeof rel_list);
+       }
+       ep = EXPAND_ITEM_LIST(rel_list, char *, 32);
+       if (!(*ep = new_array(char, 1 + len + 1)))
+               out_of_memory("send_implied_dirs");
+       **ep = is_dot_dir;
+       strlcpy(*ep + 1, limit+1, len + 1);
+}
+
 static void send1extra(int f, struct file_struct *file, struct file_list *flist)
 {
        char fbuf[MAXPATHLEN];
-       int dlen;
+       item_list *rel_list;
+       int len, dlen, flags = FLAG_DIVERT_DIRS | FLAG_XFER_DIR;
+       size_t j;
 
        f_name(file, fbuf);
        dlen = strlen(fbuf);
@@ -1529,7 +1578,51 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
 
        change_local_filter_dir(fbuf, dlen, send_dir_depth);
 
-       send_directory(f, flist, fbuf, dlen, FLAG_DIVERT_DIRS | FLAG_XFER_DIR);
+       if (file->flags & FLAG_XFER_DIR)
+               send_directory(f, flist, fbuf, dlen, flags);
+
+       if (!relative_paths)
+               return;
+
+       memcpy(&rel_list, F_DIR_RELS_P(file), sizeof rel_list);
+       if (!rel_list)
+               return;
+
+       for (j = 0; j < rel_list->count; j++) {
+               char *slash, *ep = ((char**)rel_list->items)[j];
+               int is_dot_dir = *ep;
+
+               fbuf[dlen] = '/';
+               len = strlcpy(fbuf + dlen + 1, ep+1, sizeof fbuf - dlen - 1);
+               free(ep);
+               if (len >= (int)sizeof fbuf)
+                       continue; /* Impossible... */
+
+               slash = strchr(fbuf+dlen+1, '/');
+               if (slash) {
+                       send_implied_dirs(f, flist, fbuf, fbuf+dlen+1, slash, flags, is_dot_dir);
+                       continue;
+               }
+
+               if (is_dot_dir) {
+                       STRUCT_STAT st;
+                       if (link_stat(fbuf, &st, copy_dirlinks) != 0) {
+                               io_error |= IOERR_GENERAL;
+                               rsyserr(FERROR, errno, "link_stat %s failed",
+                                       full_fname(fbuf));
+                               continue;
+                       }
+                       send_file_name(f, flist, fbuf, &st,
+                           recurse || xfer_dirs ? FLAG_TOP_DIR | flags : flags,
+                           ALL_FILTERS);
+               } else {
+                       send_file_name(f, flist, fbuf, NULL,
+                           recurse ? FLAG_TOP_DIR | flags : flags,
+                           ALL_FILTERS);
+               }
+       }
+
+       free(rel_list);
 }
 
 void send_extra_file_list(int f, int at_least)
@@ -1551,6 +1644,7 @@ void send_extra_file_list(int f, int at_least)
        while (future_cnt < at_least) {
                struct file_struct *file = dir_flist->sorted[send_dir_ndx];
                int dir_ndx, dstart = dir_count;
+               const char *pathname = F_PATHNAME(file);
                int32 *dp;
 
                flist = flist_new(0, "send_extra_file_list");
@@ -1575,7 +1669,8 @@ void send_extra_file_list(int f, int at_least)
                    && dir_flist->sorted[dir_ndx]->flags & FLAG_DUPLICATE) {
                        send_dir_ndx = dir_ndx;
                        file = dir_flist->sorted[dir_ndx];
-                       send1extra(f, file, flist);
+                       if (F_PATHNAME(file) != pathname)
+                               send1extra(f, file, flist);
                        dp = F_DIR_NODE_P(file);
                }
 
@@ -1637,14 +1732,17 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
        int len, dirlen;
        STRUCT_STAT st;
        char *p, *dir;
-       char lastpath[MAXPATHLEN] = "";
        struct file_list *flist;
        struct timeval start_tv, end_tv;
        int64 start_write;
        int use_ff_fd = 0;
        int flags, disable_buffering;
        int reading_remotely = filesfrom_host != NULL;
-       int rl_nulls = eol_nulls || reading_remotely;
+       int rl_flags = (reading_remotely ? 0 : RL_DUMP_COMMENTS)
+#ifdef ICONV_OPTION
+                    | (filesfrom_convert ? RL_CONVERT : 0)
+#endif
+                    | (eol_nulls || reading_remotely ? RL_EOL_NULLS : 0);
 
        rprintf(FLOG, "building file list\n");
        if (show_filelist_p())
@@ -1685,7 +1783,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                int is_dot_dir;
 
                if (use_ff_fd) {
-                       if (read_line(filesfrom_fd, fbuf, sizeof fbuf, !reading_remotely, rl_nulls) == 0)
+                       if (read_line(filesfrom_fd, fbuf, sizeof fbuf, rl_flags) == 0)
                                break;
                        sanitize_path(fbuf, fbuf, "", 0, NULL);
                } else {
@@ -1812,7 +1910,12 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                        continue;
                }
 
-               if (implied_dirs && (p=strrchr(fbuf,'/')) && p != fbuf) {
+               if (inc_recurse && relative_paths && *fbuf) {
+                       if ((p = strchr(fbuf+1, '/')) != NULL) {
+                               send_implied_dirs(f, flist, fbuf, fbuf, p, flags, is_dot_dir);
+                               continue;
+                       }
+               } else if (implied_dirs && (p=strrchr(fbuf,'/')) && p != fbuf) {
                        /* Send the implied directories at the start of the
                         * source spec, so we get their permissions right. */
                        char *lp = lastpath, *slash = fbuf;
@@ -1824,29 +1927,10 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                                        slash = fn;
                        }
                        *p = '/';
-                       if (fn != p || (*lp && *lp != '/')) {
-                               int save_copy_links = copy_links;
-                               int save_xfer_dirs = xfer_dirs;
-                               int dir_flags = flags & ~FLAG_XFER_DIR;
-                               copy_links |= copy_unsafe_links;
-                               xfer_dirs = 1;
-                               while ((slash = strchr(slash+1, '/')) != 0) {
-                                       *slash = '\0';
-                                       send_file_name(f, flist, fbuf, NULL,
-                                                      dir_flags, ALL_FILTERS);
-                                       *slash = '/';
-                               }
-                               copy_links = save_copy_links;
-                               xfer_dirs = save_xfer_dirs;
-                               *p = '\0';
-                               strlcpy(lastpath, fbuf, sizeof lastpath);
-                               *p = '/';
-                       }
+                       if (fn != p || (*lp && *lp != '/'))
+                               send_implied_dirs(f, flist, fbuf, slash, p, flags, 0);
                }
 
-               if (one_file_system)
-                       filesystem_dev = st.st_dev;
-
                if (recurse || (xfer_dirs && is_dot_dir)) {
                        struct file_struct *file;
                        int top_flags = FLAG_TOP_DIR | flags;
@@ -2064,12 +2148,6 @@ struct file_list *recv_file_list(int f)
        if (verbose > 3)
                output_flist(flist);
 
-       if (list_only) {
-               int i;
-               for (i = flist->low; i <= flist->high; i++)
-                       list_file_entry(flist->files[i]);
-       }
-
        if (verbose > 2)
                rprintf(FINFO, "recv_file_list done\n");
 
@@ -2178,8 +2256,7 @@ struct file_list *flist_new(int flags, char *msg)
 {
        struct file_list *flist;
 
-       flist = new0(struct file_list);
-       if (!flist)
+       if (!(flist = new0(struct file_list)))
                out_of_memory(msg);
 
        if (flags & FLIST_TEMP) {
@@ -2200,6 +2277,8 @@ struct file_list *flist_new(int flags, char *msg)
                        flist->file_pool = first_flist->file_pool;
 
                        flist->ndx_start = first_flist->prev->ndx_end + 2;
+                       if (flist->ndx_start <= high_hlink_ndx)
+                               flist->ndx_start = high_hlink_ndx + 1;
 
                        flist->prev = first_flist->prev;
                        flist->prev->next = first_flist->prev = flist;