The --iconv option has now made it to the trunk.
authorWayne Davison <wayned@samba.org>
Thu, 26 Apr 2007 05:53:13 +0000 (05:53 +0000)
committerWayne Davison <wayned@samba.org>
Thu, 26 Apr 2007 05:53:13 +0000 (05:53 +0000)
16 files changed:
NEWS
clientserver.c
configure.in
flist.c
generator.c
hlink.c
io.c
log.c
main.c
options.c
receiver.c
rsync.c
rsync.h
rsync.yo
socket.c
testsuite/rsync.fns

diff --git a/NEWS b/NEWS
index 8825ffd..7bfe693 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -61,6 +61,10 @@ Changes since 2.6.9:
       all attributes of a file by using a special extended-attribute idiom.
       There is also an analogous "fake super" option for an rsync daemon.
 
+    - Added the --iconv option, which allows rsync to convert filenames from
+      one character set to another during the transfer.  (Currently must be
+      manually enabled via configure's --enable-iconv option.)
+
     - You may specify --max-delete=0 to a 3.0.0 client as long as the receiving
       side is at least version 3.0.0.  This means that you can pull from an
       older rsync with this option, but pushing to an older rsync will generate
index 7c1b81f..e557309 100644 (file)
@@ -687,7 +687,7 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
 
        if (!ret || err_msg) {
                if (err_msg)
-                       rwrite(FERROR, err_msg, strlen(err_msg));
+                       rwrite(FERROR, err_msg, strlen(err_msg), 0);
                else
                        option_error();
                msleep(400);
index 28a4c84..fba5556 100644 (file)
@@ -568,6 +568,22 @@ if test $ac_cv_func_getpgrp = yes; then
     AC_FUNC_GETPGRP
 fi
 
+AC_ARG_ENABLE(iconv,
+    AC_HELP_STRING([--disable-iconv],
+           [disable rsync's --iconv option]),
+    [], [enable_iconv=$ac_cv_func_iconv_open])
+AH_TEMPLATE([ICONV_OPTION],
+[Define if you want the --iconv option.  Specifing a value will set the
+default iconv setting (a NULL means no --iconv processing by default).])
+if test x"$enable_iconv" != x"no"; then
+       if test x"$enable_iconv" = x"yes"; then
+               AC_DEFINE(ICONV_OPTION, NULL)
+       else
+               AC_DEFINE_UNQUOTED(ICONV_OPTION, "$enable_iconv")
+       fi
+       AC_DEFINE(UTF8_CHARSET, "UTF-8", [String to pass to iconv() for the UTF-8 charset.])
+fi
+
 AC_CACHE_CHECK([whether chown() modifies symlinks],rsync_cv_chown_modifies_symlink,[
   AC_TRY_RUN([
 #if HAVE_UNISTD_H
diff --git a/flist.c b/flist.c
index a4f3ca2..237876c 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -70,6 +70,12 @@ extern struct chmod_mode_struct *chmod_modes;
 extern struct filter_list_struct filter_list;
 extern struct filter_list_struct server_filter_list;
 
+#ifdef ICONV_OPTION
+extern int ic_ndx;
+extern int need_unsorted_flist;
+extern iconv_t ic_send, ic_recv;
+#endif
+
 int io_error;
 int checksum_len;
 dev_t filesystem_dev; /* used to implement -x */
@@ -346,7 +352,35 @@ static void send_file_entry(int f, struct file_struct *file, int ndx)
        int l1, l2;
        int flags;
 
-       f_name(file, fname);
+#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) {
+                 convert_error:
+                       io_error |= IOERR_GENERAL;
+                       rprintf(FINFO,
+                           "[%s] cannot convert filename: %s (%s)\n",
+                           who_am_i(), f_name(file, fname), strerror(errno));
+                       return;
+               }
+               *obuf = '\0';
+       } else
+#endif
+               f_name(file, fname);
 
        flags = file->flags & FLAG_TOP_DIR; /* FLAG_TOP_DIR == XMIT_TOP_DIR */
 
@@ -598,7 +632,31 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
        read_sbuf(f, &thisname[l1], l2);
        thisname[l1 + l2] = 0;
 
-       strlcpy(lastname, thisname, MAXPATHLEN);
+       /* Abuse basename_len for a moment... */
+       basename_len = strlcpy(lastname, thisname, MAXPATHLEN);
+
+#ifdef ICONV_OPTION
+       if (ic_recv != (iconv_t)-1) {
+               char *obuf = thisname, *ibuf = lastname;
+               size_t ocnt = MAXPATHLEN, icnt = basename_len;
+
+               if (icnt >= MAXPATHLEN) {
+                       errno = E2BIG;
+                       goto convert_error;
+               }
+
+               iconv(ic_recv, NULL,0, NULL,0);
+               if (iconv(ic_recv, &ibuf,&icnt, &obuf,&ocnt) == (size_t)-1) {
+                 convert_error:
+                       io_error |= IOERR_GENERAL;
+                       rprintf(FINFO,
+                           "[%s] cannot convert filename: %s (%s)\n",
+                           who_am_i(), lastname, strerror(errno));
+                       obuf = thisname;
+               }
+               *obuf = '\0';
+       }
+#endif
 
        clean_fname(thisname, 0);
 
@@ -792,6 +850,10 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
                F_OWNER(file) = uid;
        if (preserve_gid)
                F_GROUP(file) = gid;
+#ifdef ICONV_OPTION
+       if (ic_ndx)
+               F_NDX(file) = flist->count + flist->ndx_start;
+#endif
 
        if (basename != thisname) {
                file->dirname = lastdir;
@@ -1385,7 +1447,13 @@ void send_extra_file_list(int f, int at_least)
                               FLAG_DIVERT_DIRS | FLAG_XFER_DIR);
                write_byte(f, 0);
 
-               clean_flist(flist, 0, 0);
+#ifdef ICONV_OPTION
+               if (!need_unsorted_flist)
+#endif
+               {
+                       flist->sorted = flist->files;
+                       clean_flist(flist, 0, 0);
+               }
                file_total += flist->count;
                future_cnt += flist->count;
                stats.flist_size += stats.total_written - start_write;
@@ -1670,12 +1738,30 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
        stats.flist_xfertime = (int64)(end_tv.tv_sec - start_tv.tv_sec) * 1000
                             + (end_tv.tv_usec - start_tv.tv_usec) / 1000;
 
+       /* When converting names, both sides keep an unsorted file-list array
+        * because the names will differ on the sending and receiving sides
+        * (both sides will use the unsorted index number for each item). */
+
        /* Sort the list without removing any duplicates in non-incremental
         * mode.  This allows the receiving side to ask for whatever name it
         * kept.  For incremental mode, the sender also removes duplicates
         * in this initial file-list so that it avoids re-sending duplicated
         * directories. */
-       clean_flist(flist, 0, inc_recurse);
+#ifdef ICONV_OPTION
+       if (need_unsorted_flist) {
+               if (inc_recurse) {
+                       if (!(flist->sorted = new_array(struct file_struct *, flist->count)))
+                               out_of_memory("recv_file_list");
+                       memcpy(flist->sorted, flist->files,
+                              flist->count * sizeof (struct file_struct*));
+                       clean_flist(flist, 0, 1);
+               }
+       } else
+#endif
+       {
+               flist->sorted = flist->files;
+               clean_flist(flist, 0, inc_recurse);
+       }
        file_total += flist->count;
 
        if (!numeric_ids && !inc_recurse)
@@ -1778,11 +1864,27 @@ struct file_list *recv_file_list(int f)
        if (show_filelist_p())
                finish_filelist_progress(flist);
 
+#ifdef ICONV_OPTION
+       if (need_unsorted_flist) {
+               /* Create an extra array of index pointers that we can sort for
+                * the generator's use (for wading through the files in sorted
+                * order and for calling flist_find()).  We keep the "files"
+                * list unsorted for our exchange of index numbers with the
+                * other side (since their names may not sort the same). */
+               if (!(flist->sorted = new_array(struct file_struct *, flist->count)))
+                       out_of_memory("recv_file_list");
+               memcpy(flist->sorted, flist->files,
+                      flist->count * sizeof (struct file_struct*));
+       } else
+#endif
+               flist->sorted = flist->files;
+
        clean_flist(flist, relative_paths, 1);
 
        if (inc_recurse) {
-               qsort(dir_flist->files + dstart, dir_flist->count - dstart,
-                     sizeof dir_flist->files[0], (int (*)())file_compare);
+               dir_flist->sorted = dir_flist->files;
+               qsort(dir_flist->sorted + dstart, dir_flist->count - dstart,
+                     sizeof dir_flist->sorted[0], (int (*)())file_compare);
        } else if (f >= 0)
                recv_uid_list(f, flist);
 
@@ -1845,36 +1947,36 @@ int flist_find(struct file_list *flist, struct file_struct *f)
 
        while (low <= high) {
                mid = (low + high) / 2;
-               if (F_IS_ACTIVE(flist->files[mid]))
+               if (F_IS_ACTIVE(flist->sorted[mid]))
                        mid_up = mid;
                else {
                        /* Scan for the next non-empty entry using the cached
                         * distance values.  If the value isn't fully up-to-
                         * date, update it. */
-                       mid_up = mid + F_DEPTH(flist->files[mid]);
-                       if (!F_IS_ACTIVE(flist->files[mid_up])) {
+                       mid_up = mid + F_DEPTH(flist->sorted[mid]);
+                       if (!F_IS_ACTIVE(flist->sorted[mid_up])) {
                                do {
-                                   mid_up += F_DEPTH(flist->files[mid_up]);
-                               } while (!F_IS_ACTIVE(flist->files[mid_up]));
-                               F_DEPTH(flist->files[mid]) = mid_up - mid;
+                                   mid_up += F_DEPTH(flist->sorted[mid_up]);
+                               } while (!F_IS_ACTIVE(flist->sorted[mid_up]));
+                               F_DEPTH(flist->sorted[mid]) = mid_up - mid;
                        }
                        if (mid_up > high) {
                                /* If there's nothing left above us, set high to
                                 * a non-empty entry below us and continue. */
-                               high = mid - (int)flist->files[mid]->len32;
-                               if (!F_IS_ACTIVE(flist->files[high])) {
+                               high = mid - (int)flist->sorted[mid]->len32;
+                               if (!F_IS_ACTIVE(flist->sorted[high])) {
                                        do {
-                                           high -= (int)flist->files[high]->len32;
-                                       } while (!F_IS_ACTIVE(flist->files[high]));
-                                       flist->files[mid]->len32 = mid - high;
+                                           high -= (int)flist->sorted[high]->len32;
+                                       } while (!F_IS_ACTIVE(flist->sorted[high]));
+                                       flist->sorted[mid]->len32 = mid - high;
                                }
                                continue;
                        }
                }
-               diff = f_name_cmp(flist->files[mid_up], f);
+               diff = f_name_cmp(flist->sorted[mid_up], f);
                if (diff == 0) {
                        if (protocol_version < 29
-                           && S_ISDIR(flist->files[mid_up]->mode)
+                           && S_ISDIR(flist->sorted[mid_up]->mode)
                            != S_ISDIR(f->mode))
                                return -1;
                        return mid_up;
@@ -1960,6 +2062,8 @@ void flist_free(struct file_list *flist)
        }
 
        pool_destroy(flist->file_pool);
+       if (flist->sorted && flist->sorted != flist->files)
+               free(flist->sorted);
        free(flist->files);
        free(flist);
 }
@@ -1980,11 +2084,11 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                return;
        }
 
-       qsort(flist->files, flist->count,
-           sizeof flist->files[0], (int (*)())file_compare);
+       qsort(flist->sorted, flist->count,
+           sizeof flist->sorted[0], (int (*)())file_compare);
 
        for (i = no_dups? 0 : flist->count; i < flist->count; i++) {
-               if (F_IS_ACTIVE(flist->files[i])) {
+               if (F_IS_ACTIVE(flist->sorted[i])) {
                        prev_i = i;
                        break;
                }
@@ -1992,11 +2096,11 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
        flist->low = prev_i;
        while (++i < flist->count) {
                int j;
-               struct file_struct *file = flist->files[i];
+               struct file_struct *file = flist->sorted[i];
 
                if (!F_IS_ACTIVE(file))
                        continue;
-               if (f_name_cmp(file, flist->files[prev_i]) == 0)
+               if (f_name_cmp(file, flist->sorted[prev_i]) == 0)
                        j = prev_i;
                else if (protocol_version >= 29 && S_ISDIR(file->mode)) {
                        int save_mode = file->mode;
@@ -2009,7 +2113,7 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                } else
                        j = -1;
                if (j >= 0) {
-                       struct file_struct *fp = flist->files[j];
+                       struct file_struct *fp = flist->sorted[j];
                        int keep, drop;
                        /* If one is a dir and the other is not, we want to
                         * keep the dir because it might have contents in the
@@ -2030,15 +2134,15 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                        }
                        /* Make sure we don't lose track of a user-specified
                         * top directory. */
-                       flist->files[keep]->flags |= flist->files[drop]->flags
+                       flist->sorted[keep]->flags |= flist->sorted[drop]->flags
                                                   & (FLAG_TOP_DIR|FLAG_XFER_DIR);
 
-                       clear_file(flist->files[drop]);
+                       clear_file(flist->sorted[drop]);
 
                        if (keep == i) {
                                if (flist->low == drop) {
                                        for (j = drop + 1;
-                                            j < i && !F_IS_ACTIVE(flist->files[j]);
+                                            j < i && !F_IS_ACTIVE(flist->sorted[j]);
                                             j++) {}
                                        flist->low = j;
                                }
@@ -2053,7 +2157,7 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                /* We need to strip off the leading slashes for relative
                 * paths, but this must be done _after_ the sorting phase. */
                for (i = flist->low; i <= flist->high; i++) {
-                       struct file_struct *file = flist->files[i];
+                       struct file_struct *file = flist->sorted[i];
 
                        if (!file->dirname)
                                continue;
@@ -2070,7 +2174,7 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                prev_i = 0; /* It's OK that this isn't really true. */
 
                for (i = flist->low; i <= flist->high; i++) {
-                       struct file_struct *fp, *file = flist->files[i];
+                       struct file_struct *fp, *file = flist->sorted[i];
 
                        /* This temporarily abuses the F_DEPTH() value for a
                         * directory that is in a chain that might get pruned.
@@ -2078,7 +2182,7 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                        if (S_ISDIR(file->mode) && F_DEPTH(file)) {
                                /* Dump empty dirs when coming back down. */
                                for (j = prev_depth; j >= F_DEPTH(file); j--) {
-                                       fp = flist->files[prev_i];
+                                       fp = flist->sorted[prev_i];
                                        if (F_DEPTH(fp) >= 0)
                                                break;
                                        prev_i = -F_DEPTH(fp)-1;
@@ -2089,7 +2193,7 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                                                       ALL_FILTERS)) {
                                        /* Keep dirs through this dir. */
                                        for (j = prev_depth-1; ; j--) {
-                                               fp = flist->files[prev_i];
+                                               fp = flist->sorted[prev_i];
                                                if (F_DEPTH(fp) >= 0)
                                                        break;
                                                prev_i = -F_DEPTH(fp)-1;
@@ -2101,7 +2205,7 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                        } else {
                                /* Keep dirs through this non-dir. */
                                for (j = prev_depth; ; j--) {
-                                       fp = flist->files[prev_i];
+                                       fp = flist->sorted[prev_i];
                                        if (F_DEPTH(fp) >= 0)
                                                break;
                                        prev_i = -F_DEPTH(fp)-1;
@@ -2111,7 +2215,7 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                }
                /* Dump all remaining empty dirs. */
                while (1) {
-                       struct file_struct *fp = flist->files[prev_i];
+                       struct file_struct *fp = flist->sorted[prev_i];
                        if (F_DEPTH(fp) >= 0)
                                break;
                        prev_i = -F_DEPTH(fp)-1;
@@ -2119,12 +2223,12 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                }
 
                for (i = flist->low; i <= flist->high; i++) {
-                       if (F_IS_ACTIVE(flist->files[i]))
+                       if (F_IS_ACTIVE(flist->sorted[i]))
                                break;
                }
                flist->low = i;
                for (i = flist->high; i >= flist->low; i--) {
-                       if (F_IS_ACTIVE(flist->files[i]))
+                       if (F_IS_ACTIVE(flist->sorted[i]))
                                break;
                }
                flist->high = i;
@@ -2369,6 +2473,7 @@ struct file_list *get_dirlist(char *dirname, int dlen, int ignore_filter_rules)
        if (do_progress)
                flist_count_offset += dirlist->count;
 
+       dirlist->sorted = dirlist->files;
        clean_flist(dirlist, 0, 0);
 
        if (verbose > 3)
index 09c6b7d..f6167df 100644 (file)
@@ -97,6 +97,9 @@ extern char *backup_suffix;
 extern int backup_suffix_len;
 extern struct file_list *cur_flist, *first_flist, *dir_flist;
 extern struct filter_list_struct server_filter_list;
+#ifdef ICONV_OPTION
+extern int ic_ndx;
+#endif
 
 int ignore_perishable = 0;
 int non_perishable_cnt = 0;
@@ -415,15 +418,14 @@ static void do_delayed_deletions(char *delbuf)
  * MAXPATHLEN buffer with the name of the directory in it (the functions we
  * call will append names onto the end, but the old dir value will be restored
  * on exit). */
-static void delete_in_dir(struct file_list *flist, char *fbuf,
-                         struct file_struct *file, dev_t *fs_dev)
+static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev)
 {
        static int already_warned = 0;
        struct file_list *dirlist;
        char delbuf[MAXPATHLEN];
        int dlen, i;
 
-       if (!flist) {
+       if (!fbuf) {
                change_local_filter_dir(NULL, 0, 0);
                return;
        }
@@ -467,7 +469,7 @@ static void delete_in_dir(struct file_list *flist, char *fbuf,
                                        f_name(fp, NULL));
                        continue;
                }
-               if (flist_find(flist, fp) < 0) {
+               if (flist_find(cur_flist, fp) < 0) {
                        f_name(fp, delbuf);
                        if (delete_during == 2) {
                                if (!remember_delete(fp, delbuf))
@@ -482,7 +484,7 @@ static void delete_in_dir(struct file_list *flist, char *fbuf,
 
 /* This deletes any files on the receiving side that are not present on the
  * sending side.  This is used by --delete-before and --delete-after. */
-static void do_delete_pass(struct file_list *flist)
+static void do_delete_pass(void)
 {
        char fbuf[MAXPATHLEN];
        STRUCT_STAT st;
@@ -492,8 +494,8 @@ static void do_delete_pass(struct file_list *flist)
        if (dry_run > 1 || list_only)
                return;
 
-       for (j = 0; j < flist->count; j++) {
-               struct file_struct *file = flist->files[j];
+       for (j = 0; j < cur_flist->count; j++) {
+               struct file_struct *file = cur_flist->sorted[j];
 
                if (!(file->flags & FLAG_XFER_DIR))
                        continue;
@@ -506,9 +508,9 @@ static void do_delete_pass(struct file_list *flist)
                 || !S_ISDIR(st.st_mode))
                        continue;
 
-               delete_in_dir(flist, fbuf, file, &st.st_dev);
+               delete_in_dir(fbuf, file, &st.st_dev);
        }
-       delete_in_dir(NULL, NULL, NULL, &dev_zero);
+       delete_in_dir(NULL, NULL, &dev_zero);
 
        if (do_progress && !am_server)
                rprintf(FINFO, "                    \r");
@@ -1271,7 +1273,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                }
                else if (delete_during && f_out != -1 && !phase && dry_run < 2
                    && (file->flags & FLAG_XFER_DIR))
-                       delete_in_dir(cur_flist, fname, file, &real_sx.st.st_dev);
+                       delete_in_dir(fname, file, &real_sx.st.st_dev);
                goto cleanup;
        }
 
@@ -1731,7 +1733,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
        /* Fix any directory permissions that were modified during the
         * transfer and/or re-set any tweaked modified-time values. */
        for (i = start, j = 0; i <= end; i++) {
-               file = flist->files[i];
+               file = flist->sorted[i];
                if (!F_IS_ACTIVE(file) || !S_ISDIR(file->mode)
                 || file->flags & FLAG_MISSING_DIR)
                        continue;
@@ -1827,7 +1829,7 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
 
 void generate_files(int f_out, const char *local_name)
 {
-       int i;
+       int i, ndx;
        char fbuf[MAXPATHLEN];
        int itemizing;
        enum logcode code;
@@ -1860,7 +1862,7 @@ void generate_files(int f_out, const char *local_name)
                rprintf(FINFO, "generator starting pid=%ld\n", (long)getpid());
 
        if (delete_before && !solo_file && cur_flist->count > 0)
-               do_delete_pass(cur_flist);
+               do_delete_pass();
        if (delete_during == 2) {
                deldelay_size = BIGPATHBUFLEN * 4;
                deldelay_buf = new_array(char, deldelay_size);
@@ -1896,21 +1898,27 @@ void generate_files(int f_out, const char *local_name)
                                        dirdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
                                } else
                                        dirdev = MAKEDEV(0, 0);
-                               delete_in_dir(cur_flist, f_name(fp, fbuf), fp, &dirdev);
+                               delete_in_dir(f_name(fp, fbuf), fp, &dirdev);
                        }
                }
                for (i = cur_flist->low; i <= cur_flist->high; i++) {
-                       struct file_struct *file = cur_flist->files[i];
+                       struct file_struct *file = cur_flist->sorted[i];
 
                        if (!F_IS_ACTIVE(file))
                                continue;
 
+#ifdef ICONV_OPTION
+                       if (ic_ndx)
+                               ndx = F_NDX(file);
+                       else
+#endif
+                               ndx = i + cur_flist->ndx_start;
+
                        if (solo_file)
                                strlcpy(fbuf, solo_file, sizeof fbuf);
                        else
                                f_name(file, fbuf);
-                       recv_generator(fbuf, file, i + cur_flist->ndx_start,
-                                      itemizing, code, f_out);
+                       recv_generator(fbuf, file, ndx, itemizing, code, f_out);
 
                        /* We need to ensure that any dirs we create have
                         * writeable permissions during the time we are putting
@@ -1952,7 +1960,7 @@ void generate_files(int f_out, const char *local_name)
        } while ((cur_flist = cur_flist->next) != NULL);
 
        if (delete_during)
-               delete_in_dir(NULL, NULL, NULL, &dev_zero);
+               delete_in_dir(NULL, NULL, &dev_zero);
        phase++;
        if (verbose > 2)
                rprintf(FINFO, "generate_files phase=%d\n", phase);
@@ -1996,7 +2004,7 @@ void generate_files(int f_out, const char *local_name)
        if (delete_during == 2)
                do_delayed_deletions(fbuf);
        if (delete_after && !solo_file && file_total > 0)
-               do_delete_pass(cur_flist);
+               do_delete_pass();
 
        if ((need_retouch_dir_perms || need_retouch_dir_times)
         && dir_tweaking && (!inc_recurse || delete_during == 2))
diff --git a/hlink.c b/hlink.c
index f52c0ed..25a7ed3 100644 (file)
--- a/hlink.c
+++ b/hlink.c
@@ -34,11 +34,13 @@ extern int stdout_format_has_i;
 extern int maybe_ATTRS_REPORT;
 extern char *basis_dir[];
 extern struct file_list *cur_flist;
+#ifdef ICONV_OPTION
+extern int ic_ndx;
+#endif
 
 #ifdef SUPPORT_HARD_LINKS
 
 #define HASH_LOAD_LIMIT(size) ((size)*3/4)
-#define FPTR(i) (cur_flist->files[i])
 
 struct ihash_table {
        int32 size;
@@ -200,8 +202,8 @@ void idev_destroy(void)
 
 static int hlink_compare_gnum(int *int1, int *int2)
 {
-       struct file_struct *f1 = FPTR(*int1);
-       struct file_struct *f2 = FPTR(*int2);
+       struct file_struct *f1 = cur_flist->sorted[*int1];
+       struct file_struct *f2 = cur_flist->sorted[*int2];
        int32 gnum1 = F_HL_GNUM(f1);
        int32 gnum2 = F_HL_GNUM(f2);
 
@@ -221,17 +223,24 @@ static void match_gnums(int32 *ndx_list, int ndx_count)
             (int (*)()) hlink_compare_gnum);
 
        for (from = 0; from < ndx_count; from++) {
-               for (file = FPTR(ndx_list[from]), gnum = F_HL_GNUM(file), prev = -1;
+               for (file = cur_flist->sorted[ndx_list[from]], gnum = F_HL_GNUM(file), prev = -1;
                     from < ndx_count-1;
-                    file = file_next, gnum = gnum_next, prev = ndx_list[from++])
+                    file = file_next, gnum = gnum_next, from++)
                {
-                       file_next = FPTR(ndx_list[from+1]);
+                       file_next = cur_flist->sorted[ndx_list[from+1]];
                        gnum_next = F_HL_GNUM(file_next);
                        if (gnum != gnum_next)
                                break;
                        if (prev < 0)
                                file->flags |= FLAG_HLINK_FIRST;
                        F_HL_PREV(file) = prev;
+                       /* The linked list must use raw ndx values. */
+#ifdef ICONV_OPTION
+                       if (ic_ndx)
+                               prev = F_NDX(file);
+                       else
+#endif
+                               prev = ndx_list[from];
                }
                if (prev < 0)
                        file->flags &= ~FLAG_HLINKED;
@@ -255,7 +264,7 @@ void match_hard_links(void)
                out_of_memory("match_hard_links");
 
        for (i = 0; i < cur_flist->count; i++) {
-               if (F_IS_HLINKED(FPTR(i)))
+               if (F_IS_HLINKED(cur_flist->sorted[i]))
                        ndx_list[ndx_count++] = i;
        }
 
@@ -317,7 +326,7 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
        STRUCT_STAT prev_st;
        char prev_name[MAXPATHLEN], altbuf[MAXPATHLEN], *realname;
        int alt_dest, prev_ndx = F_HL_PREV(file);
-       struct file_struct *prev_file = FPTR(prev_ndx);
+       struct file_struct *prev_file = cur_flist->files[prev_ndx];
 
        /* Is the previous link is not complete yet? */
        if (!(prev_file->flags & FLAG_HLINK_DONE)) {
@@ -338,7 +347,7 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
        if (!(prev_file->flags & FLAG_HLINK_FIRST)) {
                /* The previous previous will be marked with FIRST. */
                prev_ndx = F_HL_PREV(prev_file);
-               prev_file = FPTR(prev_ndx);
+               prev_file = cur_flist->files[prev_ndx];
                /* Update our previous pointer to point to the first. */
                F_HL_PREV(file) = prev_ndx;
        }
@@ -475,7 +484,7 @@ void finish_hard_link(struct file_struct *file, const char *fname,
 
        while ((ndx = prev_ndx) >= 0) {
                int val;
-               file = FPTR(ndx);
+               file = cur_flist->files[ndx];
                file->flags = (file->flags & ~FLAG_HLINK_FIRST) | FLAG_HLINK_DONE;
                prev_ndx = F_HL_PREV(file);
                prev_name = f_name(file, NULL);
diff --git a/io.c b/io.c
index a7603ff..1c85880 100644 (file)
--- a/io.c
+++ b/io.c
@@ -53,6 +53,9 @@ extern int preserve_hard_links;
 extern char *filesfrom_host;
 extern struct stats stats;
 extern struct file_list *cur_flist, *first_flist;
+#ifdef ICONV_OPTION
+extern iconv_t ic_send, ic_recv;
+#endif
 
 const char phase_unknown[] = "unknown";
 int ignore_timeout = 0;
@@ -91,6 +94,9 @@ static int write_batch_monitor_out = -1;
 static int io_filesfrom_f_in = -1;
 static int io_filesfrom_f_out = -1;
 static char io_filesfrom_buf[2048];
+#ifdef ICONV_OPTION
+static char iconv_buf[sizeof io_filesfrom_buf / 2];
+#endif
 static char *io_filesfrom_bp;
 static char io_filesfrom_lastchar;
 static int io_filesfrom_buflen;
@@ -111,6 +117,7 @@ static void writefd(int fd, const char *buf, size_t len);
 static void writefd_unbuffered(int fd, const char *buf, size_t len);
 static void decrement_active_files(int ndx);
 static void decrement_flist_in_progress(int ndx, int redo);
+static void mplex_write(int fd, enum msgcode code, const char *buf, size_t len, int convert);
 
 struct flist_ndx_item {
        struct flist_ndx_item *next;
@@ -125,7 +132,7 @@ static struct flist_ndx_list redo_list, hlink_list;
 
 struct msg_list_item {
        struct msg_list_item *next;
-       int len;
+       char convert;
        char buf[1];
 };
 
@@ -229,7 +236,7 @@ void set_msg_fd_out(int fd)
 }
 
 /* Add a message to the pending MSG_* list. */
-static void msg_list_add(struct msg_list *lst, int code, const char *buf, int len)
+static void msg_list_add(struct msg_list *lst, int code, const char *buf, int len, int convert)
 {
        struct msg_list_item *m;
        int sz = len + 4 + sizeof m[0] - 1;
@@ -237,7 +244,7 @@ static void msg_list_add(struct msg_list *lst, int code, const char *buf, int le
        if (!(m = (struct msg_list_item *)new_array(char, sz)))
                out_of_memory("msg_list_add");
        m->next = NULL;
-       m->len = len + 4;
+       m->convert = convert;
        SIVAL(m->buf, 0, ((code+MPLEX_BASE)<<24) | len);
        memcpy(m->buf + 4, buf, len);
        if (lst->tail)
@@ -252,21 +259,25 @@ static void msg_flush(void)
        if (am_generator) {
                while (msg_queue.head && io_multiplexing_out) {
                        struct msg_list_item *m = msg_queue.head;
+                       int len = IVAL(m->buf, 0) & 0xFFFFFF;
+                       int tag = *((uchar*)m->buf+3) - MPLEX_BASE;
                        if (!(msg_queue.head = m->next))
                                msg_queue.tail = NULL;
-                       stats.total_written += m->len;
+                       stats.total_written += len + 4;
                        defer_forwarding_messages++;
-                       writefd_unbuffered(sock_f_out, m->buf, m->len);
+                       mplex_write(sock_f_out, tag, m->buf + 4, len, m->convert);
                        defer_forwarding_messages--;
                        free(m);
                }
        } else {
                while (msg_queue.head) {
                        struct msg_list_item *m = msg_queue.head;
+                       int len = IVAL(m->buf, 0) & 0xFFFFFF;
+                       int tag = *((uchar*)m->buf+3) - MPLEX_BASE;
                        if (!(msg_queue.head = m->next))
                                msg_queue.tail = NULL;
                        defer_forwarding_messages++;
-                       writefd_unbuffered(msg_fd_out, m->buf, m->len);
+                       mplex_write(msg_fd_out, tag, m->buf + 4, len, m->convert);
                        defer_forwarding_messages--;
                        free(m);
                }
@@ -341,7 +352,7 @@ static void read_msg_fd(void)
                if (len >= (int)sizeof buf || !am_generator)
                        goto invalid_msg;
                readfd(fd, buf, len);
-               send_msg(MSG_DELETED, buf, len);
+               send_msg(MSG_DELETED, buf, len, 1);
                break;
        case MSG_SUCCESS:
                if (len != 4 || !am_generator)
@@ -349,7 +360,7 @@ static void read_msg_fd(void)
                readfd(fd, buf, len);
                if (remove_source_files) {
                        decrement_active_files(IVAL(buf,0));
-                       send_msg(MSG_SUCCESS, buf, len);
+                       send_msg(MSG_SUCCESS, buf, len, 0);
                }
                if (preserve_hard_links)
                        flist_ndx_push(&hlink_list, IVAL(buf,0));
@@ -378,7 +389,7 @@ static void read_msg_fd(void)
                        if (n >= sizeof buf)
                                n = sizeof buf - 1;
                        readfd(fd, buf, n);
-                       rwrite((enum logcode)tag, buf, n);
+                       rwrite((enum logcode)tag, buf, n, !am_generator);
                        len -= n;
                }
                break;
@@ -447,14 +458,19 @@ static void decrement_flist_in_progress(int ndx, int redo)
 }
 
 /* Write an message to a multiplexed stream. If this fails, rsync exits. */
-static void mplex_write(int fd, enum msgcode code, const char *buf, size_t len)
+static void mplex_write(int fd, enum msgcode code, const char *buf, size_t len, int convert)
 {
-       char buffer[1024];
+       char buffer[BIGPATHBUFLEN]; /* Oversized for use by iconv code. */
        size_t n = len;
 
        SIVAL(buffer, 0, ((MPLEX_BASE + (int)code)<<24) + len);
 
-       if (n > sizeof buffer - 4)
+#ifdef ICONV_OPTION
+       if (convert && ic_send == (iconv_t)-1)
+#endif
+               convert = 0;
+
+       if (convert || n > 1024 - 4) /* BIGPATHBUFLEN can handle 1024 bytes */
                n = 0;
        else
                memcpy(buffer + 4, buf, n);
@@ -464,6 +480,28 @@ static void mplex_write(int fd, enum msgcode code, const char *buf, size_t len)
        len -= n;
        buf += n;
 
+#ifdef ICONV_OPTION
+       if (convert) {
+               iconv(ic_send, NULL, 0, NULL, 0);
+               defer_forwarding_messages++;
+               while (len) {
+                       ICONV_CONST char *ibuf = (ICONV_CONST char *)buf;
+                       char *obuf = buffer;
+                       size_t ocnt = sizeof buffer;
+                       while (len && iconv(ic_send, &ibuf,&len,
+                                           &obuf,&ocnt) == (size_t)-1) {
+                               if (errno == E2BIG || !ocnt)
+                                       break;
+                               *obuf++ = *ibuf++;
+                               ocnt--, len--;
+                       }
+                       n = obuf - buffer;
+                       writefd_unbuffered(fd, buffer, n);
+               }
+               if (!--defer_forwarding_messages)
+                       msg_flush();
+       } else
+#endif
        if (len) {
                defer_forwarding_messages++;
                writefd_unbuffered(fd, buf, len);
@@ -472,20 +510,20 @@ static void mplex_write(int fd, enum msgcode code, const char *buf, size_t len)
        }
 }
 
-int send_msg(enum msgcode code, const char *buf, int len)
+int send_msg(enum msgcode code, const char *buf, int len, int convert)
 {
        if (msg_fd_out < 0) {
                if (!defer_forwarding_messages)
-                       return io_multiplex_write(code, buf, len);
+                       return io_multiplex_write(code, buf, len, convert);
                if (!io_multiplexing_out)
                        return 0;
-               msg_list_add(&msg_queue, code, buf, len);
+               msg_list_add(&msg_queue, code, buf, len, convert);
                return 1;
        }
        if (flist_forward_from >= 0)
-               msg_list_add(&msg_queue, code, buf, len);
+               msg_list_add(&msg_queue, code, buf, len, convert);
        else
-               mplex_write(msg_fd_out, code, buf, len);
+               mplex_write(msg_fd_out, code, buf, len, convert);
        return 1;
 }
 
@@ -493,7 +531,7 @@ void send_msg_int(enum msgcode code, int num)
 {
        char numbuf[4];
        SIVAL(numbuf, 0, num);
-       send_msg(code, numbuf, 4);
+       send_msg(code, numbuf, 4, 0);
 }
 
 void wait_for_receiver(void)
@@ -719,10 +757,8 @@ static int read_timeout(int fd, char *buf, size_t len)
        return cnt;
 }
 
-/**
- * Read a line into the "fname" buffer (which must be at least MAXPATHLEN
- * characters long).
- */
+/* Read a line into the "fname" buffer (which must be at least MAXPATHLEN
+ * characters long). */
 int read_filesfrom_line(int fd, char *fname)
 {
        char ch, *s, *eob = fname + MAXPATHLEN - 1;
@@ -833,7 +869,7 @@ void maybe_send_keepalive(void)
                        if (protocol_version < 29)
                                return; /* there's nothing we can do */
                        if (protocol_version >= 30)
-                               send_msg(MSG_NOOP, "", 0);
+                               send_msg(MSG_NOOP, "", 0, 0);
                        else {
                                write_int(sock_f_out, cur_flist->count);
                                write_shortint(sock_f_out, ITEM_IS_NEW);
@@ -932,7 +968,30 @@ static int readfd_unbuffered(int fd, char *buf, size_t len)
                case MSG_DELETED:
                        if (msg_bytes >= sizeof line)
                                goto overflow;
-                       read_loop(fd, line, msg_bytes);
+#ifdef ICONV_OPTION
+                       if (ic_recv != (iconv_t)-1) {
+                               ICONV_CONST char *ibuf;
+                               char *obuf = line;
+                               size_t icnt, ocnt = sizeof line - 1;
+                               int add_null = 0;
+                               iconv(ic_send, NULL, 0, NULL, 0);
+                               while (msg_bytes) {
+                                       icnt = msg_bytes > sizeof iconv_buf
+                                            ? sizeof iconv_buf : msg_bytes;
+                                       read_loop(fd, iconv_buf, icnt);
+                                       if (!(msg_bytes -= icnt) && !iconv_buf[icnt-1])
+                                               icnt--, add_null = 1;
+                                       ibuf = (ICONV_CONST char *)iconv_buf;
+                                       if (iconv(ic_send, &ibuf,&icnt,
+                                                 &obuf,&ocnt) == (size_t)-1)
+                                               goto overflow; // XXX
+                               }
+                               if (add_null)
+                                       *obuf++ = '\0';
+                               msg_bytes = obuf - line;
+                       } else
+#endif
+                               read_loop(fd, line, msg_bytes);
                        /* A directory name was sent with the trailing null */
                        if (msg_bytes > 0 && !line[msg_bytes-1])
                                log_delete(line, S_IFDIR);
@@ -967,7 +1026,7 @@ static int readfd_unbuffered(int fd, char *buf, size_t len)
                                exit_cleanup(RERR_STREAMIO);
                        }
                        read_loop(fd, line, msg_bytes);
-                       rwrite((enum logcode)tag, line, msg_bytes);
+                       rwrite((enum logcode)tag, line, msg_bytes, 1);
                        break;
                default:
                        rprintf(FERROR, "unexpected tag %d [%s]\n",
@@ -1372,7 +1431,7 @@ void io_flush(int flush_it_all)
                return;
 
        if (io_multiplexing_out)
-               mplex_write(sock_f_out, MSG_DATA, iobuf_out, iobuf_out_cnt);
+               mplex_write(sock_f_out, MSG_DATA, iobuf_out, iobuf_out_cnt, 0);
        else
                writefd_unbuffered(iobuf_f_out, iobuf_out, iobuf_out_cnt);
        iobuf_out_cnt = 0;
@@ -1684,13 +1743,13 @@ void io_start_multiplex_in(void)
 }
 
 /** Write an message to the multiplexed data stream. */
-int io_multiplex_write(enum msgcode code, const char *buf, size_t len)
+int io_multiplex_write(enum msgcode code, const char *buf, size_t len, int convert)
 {
        if (!io_multiplexing_out)
                return 0;
        io_flush(NORMAL_FLUSH);
        stats.total_written += (len+4);
-       mplex_write(sock_f_out, code, buf, len);
+       mplex_write(sock_f_out, code, buf, len, convert);
        return 1;
 }
 
diff --git a/log.c b/log.c
index 0dc029c..c876325 100644 (file)
--- a/log.c
+++ b/log.c
  */
 
 #include "rsync.h"
-#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
-#include <iconv.h>
-#endif
 
 extern int verbose;
 extern int dry_run;
 extern int am_daemon;
 extern int am_server;
 extern int am_sender;
+extern int am_generator;
 extern int local_server;
 extern int quiet;
 extern int module_id;
@@ -47,9 +45,12 @@ extern char *auth_user;
 extern char *stdout_format;
 extern char *logfile_format;
 extern char *logfile_name;
-#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
+#ifdef ICONV_CONST
 extern iconv_t ic_chck;
 #endif
+#ifdef ICONV_OPTION
+extern iconv_t ic_send, ic_recv;
+#endif
 extern char curr_dir[];
 extern unsigned int module_dirlen;
 
@@ -234,17 +235,25 @@ static void filtered_fwrite(FILE *f, const char *buf, int len, int use_isprint)
 /* this is the underlying (unformatted) rsync debugging function. Call
  * it with FINFO, FERROR or FLOG.  Note: recursion can happen with
  * certain fatal conditions. */
-void rwrite(enum logcode code, const char *buf, int len)
+void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
 {
        int trailing_CR_or_NL;
        FILE *f = NULL;
+#ifdef ICONV_OPTION
+       iconv_t ic = is_utf8 && ic_recv != (iconv_t)-1 ? ic_recv : ic_chck;
+#else
+#ifdef ICONV_CONST
+       iconv_t ic = ic_chck;
+#endif
+#endif
 
        if (len < 0)
                exit_cleanup(RERR_MESSAGEIO);
 
        if (am_server && msg_fd_out >= 0) {
+               assert(!is_utf8);
                /* Pass the message to our sibling. */
-               send_msg((enum msgcode)code, buf, len);
+               send_msg((enum msgcode)code, buf, len, 0);
                return;
        }
 
@@ -277,7 +286,7 @@ void rwrite(enum logcode code, const char *buf, int len)
 
        if (am_server) {
                /* Pass the message to the non-server side. */
-               if (send_msg((enum msgcode)code, buf, len))
+               if (send_msg((enum msgcode)code, buf, len, !is_utf8))
                        return;
                if (am_daemon) {
                        /* TODO: can we send the error to the user somehow? */
@@ -300,18 +309,15 @@ void rwrite(enum logcode code, const char *buf, int len)
        trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r')
                          ? buf[--len] : 0;
 
-#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
-#ifndef ICONV_CONST
-#define ICONV_CONST
-#endif
-       if (ic_chck != (iconv_t)-1) {
+#ifdef ICONV_CONST
+       if (ic != (iconv_t)-1) {
                char convbuf[1024];
                ICONV_CONST char *in_buf = (ICONV_CONST char *)buf;
                char *out_buf = convbuf;
                size_t in_cnt = len, out_cnt = sizeof convbuf - 1;
 
-               iconv(ic_chck, NULL, 0, NULL, 0);
-               while (iconv(ic_chck, &in_buf,&in_cnt,
+               iconv(ic, NULL, 0, NULL, 0);
+               while (iconv(ic, &in_buf,&in_cnt,
                             &out_buf,&out_cnt) == (size_t)-1) {
                        if (out_buf != convbuf) {
                                filtered_fwrite(f, convbuf, out_buf - convbuf, 0);
@@ -373,7 +379,7 @@ void rprintf(enum logcode code, const char *format, ...)
                }
        }
 
-       rwrite(code, buf, len);
+       rwrite(code, buf, len, 0);
 }
 
 /* This is like rprintf, but it also tries to print some
@@ -404,7 +410,7 @@ void rsyserr(enum logcode code, int errcode, const char *format, ...)
        if (len >= sizeof buf)
                exit_cleanup(RERR_MESSAGEIO);
 
-       rwrite(code, buf, len);
+       rwrite(code, buf, len, 0);
 }
 
 void rflush(enum logcode code)
@@ -681,7 +687,7 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
                p = s + len;
        }
 
-       rwrite(code, buf, total);
+       rwrite(code, buf, total, 0);
 }
 
 /* Return 1 if the format escape is in the log-format string (e.g. look for
@@ -758,7 +764,7 @@ void log_delete(const char *fname, int mode)
        else if (am_server && protocol_version >= 29 && len < MAXPATHLEN) {
                if (S_ISDIR(mode))
                        len++; /* directories include trailing null */
-               send_msg(MSG_DELETED, fname, len);
+               send_msg(MSG_DELETED, fname, len, am_generator);
        } else {
                fmt = stdout_format_has_o_or_i ? stdout_format : "deleting %n";
                log_formatted(FCLIENT, fmt, "del.", &x.file, fname, &stats,
diff --git a/main.c b/main.c
index 410ce29..5e2b03e 100644 (file)
--- a/main.c
+++ b/main.c
@@ -727,7 +727,7 @@ static int do_recv(int f_in, int f_out, char *local_name)
                io_flush(FULL_FLUSH);
                handle_stats(f_in);
 
-               send_msg(MSG_DONE, "", 1);
+               send_msg(MSG_DONE, "", 1, 0);
                write_varlong(error_pipe[1], stats.total_read, 3);
                io_flush(FULL_FLUSH);
 
@@ -900,7 +900,7 @@ void start_server(int f_in, int f_out, int argc, char *argv[])
 
        io_set_sock_fds(f_in, f_out);
        setup_protocol(f_out, f_in);
-#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
+#ifdef ICONV_CONST
        setup_iconv();
 #endif
 
@@ -937,7 +937,7 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
 
        io_set_sock_fds(f_in, f_out);
        setup_protocol(f_out,f_in);
-#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
+#ifdef ICONV_CONST
        setup_iconv();
 #endif
 
index f3d1083..084f5c3 100644 (file)
--- a/options.c
+++ b/options.c
@@ -177,6 +177,11 @@ int list_only = 0;
 #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
 char *batch_name = NULL;
 
+#ifdef ICONV_OPTION
+int need_unsorted_flist = 0;
+char *iconv_opt = ICONV_OPTION;
+#endif
+
 struct chmod_mode_struct *chmod_modes = NULL;
 
 static int daemon_opt;   /* sets am_daemon after option error-reporting */
@@ -205,6 +210,7 @@ static void print_rsync_version(enum logcode f)
        char const *acls = "no ";
        char const *xattrs = "no ";
        char const *links = "no ";
+       char const *iconv = "no ";
        char const *ipv6 = "no ";
        STRUCT_STAT *dumstat;
 
@@ -232,6 +238,9 @@ static void print_rsync_version(enum logcode f)
 #ifdef INET6
        ipv6 = "";
 #endif
+#ifdef ICONV_OPTION
+       iconv = "";
+#endif
 
        rprintf(f, "%s  version %s  protocol version %d%s\n",
                RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
@@ -245,8 +254,8 @@ static void print_rsync_version(enum logcode f)
                (int)(sizeof (int64) * 8));
        rprintf(f, "    %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
                got_socketpair, hardlinks, links, ipv6, have_inplace);
-       rprintf(f, "    %sappend, %sACLs, %sxattrs\n",
-               have_inplace, acls, xattrs);
+       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv\n",
+               have_inplace, acls, xattrs, iconv);
 
 #ifdef MAINTAINER_MODE
        rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
@@ -399,6 +408,9 @@ void usage(enum logcode F)
   rprintf(F,"     --only-write-batch=FILE like --write-batch but w/o updating destination\n");
   rprintf(F,"     --read-batch=FILE       read a batched update from FILE\n");
   rprintf(F,"     --protocol=NUM          force an older protocol version to be used\n");
+#ifdef ICONV_OPTION
+  rprintf(F,"     --iconv=CONVERT_SPEC    request charset conversion of filesnames\n");
+#endif
 #ifdef INET6
   rprintf(F," -4, --ipv4                  prefer IPv4\n");
   rprintf(F," -6, --ipv6                  prefer IPv6\n");
@@ -560,6 +572,9 @@ static struct poptOption long_options[] = {
   {"rsh",             'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 },
   {"rsync-path",       0,  POPT_ARG_STRING, &rsync_path, 0, 0, 0 },
   {"temp-dir",        'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
+#ifdef ICONV_OPTION
+  {"iconv",            0,  POPT_ARG_STRING, &iconv_opt, 0, 0, 0 },
+#endif
 #ifdef INET6
   {"ipv4",            '4', POPT_ARG_VAL,    &default_af_hint, AF_INET, 0, 0 },
   {"ipv6",            '6', POPT_ARG_VAL,    &default_af_hint, AF_INET6, 0, 0 },
@@ -832,6 +847,11 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
        if (am_daemon)
                set_refuse_options("log-file*");
 
+#ifdef ICONV_OPTION
+       if (!am_daemon && (arg = getenv("RSYNC_ICONV")) != NULL && *arg)
+               iconv_opt = strdup(arg);
+#endif
+
        /* TODO: Call poptReadDefaultConfig; handle errors. */
 
        /* The context leaks in case of an error, but if there's a
@@ -857,6 +877,9 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                                                    long_options, 0);
                                am_server = 1;
                        }
+#ifdef ICONV_OPTION
+                       iconv_opt = NULL;
+#endif
                        break;
 
                case OPT_SENDER:
@@ -874,6 +897,9 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                                        sizeof err_buf);
                                return 0;
                        }
+#ifdef ICONV_OPTION
+                       iconv_opt = NULL;
+#endif
                        poptFreeContext(pc);
                        pc = poptGetContext(RSYNC_NAME, *argc, *argv,
                                            long_daemon_options, 0);
@@ -1175,6 +1201,15 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                exit_cleanup(0);
        }
 
+#ifdef ICONV_OPTION
+       if (iconv_opt) {
+               if (!am_server && strcmp(iconv_opt, "-") == 0)
+                       iconv_opt = NULL;
+               else
+                       need_unsorted_flist = 1;
+       }
+#endif
+
 #ifndef SUPPORT_LINKS
        if (preserve_links && !am_sender) {
                snprintf(err_buf, sizeof err_buf,
@@ -1655,8 +1690,6 @@ void server_options(char **args,int *argc)
        if (list_only == 1 && !recurse)
                argstr[x++] = 'r';
 
-       argstr[x] = '\0';
-
 #if SUBPROTOCOL_VERSION != 0
        /* If we're speaking a pre-release version of a protocol, we tell
         * the server about this by (ab)using the -e option. */
@@ -1666,6 +1699,8 @@ void server_options(char **args,int *argc)
        }
 #endif
 
+       argstr[x] = '\0';
+
        if (x != 1)
                args[ac++] = argstr;
 
@@ -1704,6 +1739,19 @@ void server_options(char **args,int *argc)
                        args[ac++] = "--log-format=X";
        }
 
+#ifdef ICONV_OPTION
+       if (iconv_opt) {
+               char *set = strchr(iconv_opt, ',');
+               if (set)
+                       set++;
+               else
+                       set = iconv_opt;
+               if (asprintf(&arg, "--iconv=%s", set) < 0)
+                       goto oom;
+               args[ac++] = arg;
+       }
+#endif
+
        if (block_size) {
                if (asprintf(&arg, "-B%lu", block_size) < 0)
                        goto oom;
index 21528a7..3fdd895 100644 (file)
@@ -389,7 +389,7 @@ int recv_files(int f_in, char *local_name)
                                rprintf(FINFO, "recv_files phase=%d\n", phase);
                        if (phase == 2 && delay_updates)
                                handle_delayed_updates(local_name);
-                       send_msg(MSG_DONE, "", 0);
+                       send_msg(MSG_DONE, "", 0, 0);
                        continue;
                }
 
diff --git a/rsync.c b/rsync.c
index 1eda740..06fd30b 100644 (file)
--- a/rsync.c
+++ b/rsync.c
@@ -20,9 +20,6 @@
  */
 
 #include "rsync.h"
-#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
-#include <iconv.h>
-#endif
 #if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET
 #include <libcharset.h>
 #elif defined HAVE_LANGINFO_H && defined HAVE_NL_LANGINFO
@@ -53,9 +50,16 @@ extern int keep_dirlinks;
 extern int make_backups;
 extern struct file_list *cur_flist, *first_flist, *dir_flist;
 extern struct chmod_mode_struct *daemon_chmod_modes;
+#ifdef ICONV_OPTION
+extern char *iconv_opt;
+#endif
 
-#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
+#ifdef ICONV_CONST
 iconv_t ic_chck = (iconv_t)-1;
+#ifdef ICONV_OPTION
+iconv_t ic_send = (iconv_t)-1, ic_recv = (iconv_t)-1;
+int ic_ndx;
+#endif
 
 static const char *default_charset(void)
 {
@@ -70,8 +74,13 @@ static const char *default_charset(void)
 
 void setup_iconv()
 {
+       const char *defset = default_charset();
+# ifdef ICONV_OPTION
+       const char *charset;
+       char *cp;
+#endif
+
        if (!am_server && !allow_8bit_chars) {
-               const char *defset = default_charset();
 
                /* It's OK if this fails... */
                ic_chck = iconv_open(defset, defset);
@@ -89,6 +98,44 @@ void setup_iconv()
                        }
                }
        }
+
+# ifdef ICONV_OPTION
+       if (!iconv_opt)
+               return;
+
+       if ((cp = strchr(iconv_opt, ',')) != NULL) {
+               if (am_server) /* A local transfer needs this. */
+                       iconv_opt = cp + 1;
+               else
+                       *cp = '\0';
+       }
+
+       if (!*iconv_opt || (*iconv_opt == '.' && iconv_opt[1] == '\0'))
+               charset = defset;
+       else
+               charset = iconv_opt;
+
+       if ((ic_send = iconv_open(UTF8_CHARSET, charset)) == (iconv_t)-1) {
+               rprintf(FERROR, "iconv_open(\"%s\", \"%s\") failed\n",
+                       UTF8_CHARSET, charset);
+               exit_cleanup(RERR_UNSUPPORTED);
+       }
+
+       if ((ic_recv = iconv_open(charset, UTF8_CHARSET)) == (iconv_t)-1) {
+               rprintf(FERROR, "iconv_open(\"%s\", \"%s\") failed\n",
+                       charset, UTF8_CHARSET);
+               exit_cleanup(RERR_UNSUPPORTED);
+       }
+
+       if (!am_sender)
+               ic_ndx = ++file_extra_cnt;
+
+       if (verbose > 1) {
+               rprintf(FINFO, "%s charset: %s\n",
+                       am_server ? "server" : "client",
+                       *charset ? charset : "[LOCALE]");
+       }
+# endif
 }
 #endif
 
@@ -112,7 +159,7 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr,
                        goto invalid_ndx;
                if (ndx == NDX_FLIST_EOF) {
                        flist_eof = 1;
-                       send_msg(MSG_FLIST_EOF, "", 0);
+                       send_msg(MSG_FLIST_EOF, "", 0, 0);
                        continue;
                }
                ndx = NDX_FLIST_OFFSET - ndx;
diff --git a/rsync.h b/rsync.h
index c3ab588..8bcc134 100644 (file)
--- a/rsync.h
+++ b/rsync.h
@@ -362,6 +362,15 @@ enum msgcode {
 # include <limits.h>
 #endif
 
+#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
+#include <iconv.h>
+#ifndef ICONV_CONST
+#define ICONV_CONST
+#endif
+#elif defined ICONV_CONST
+#undef ICONV_CONST
+#endif
+
 #include <assert.h>
 
 #include "lib/pool_alloc.h"
@@ -609,6 +618,7 @@ extern int preserve_xattrs;
 #define F_GROUP(f) REQ_EXTRA(f, preserve_gid)->unum
 #define F_ACL(f) REQ_EXTRA(f, preserve_acls)->num
 #define F_XATTR(f) REQ_EXTRA(f, preserve_xattrs)->num
+#define F_NDX(f) REQ_EXTRA(f, ic_ndx)->num
 
 /* These items are per-entry optional and mutally exclusive: */
 #define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
@@ -665,7 +675,7 @@ extern int preserve_xattrs;
 
 struct file_list {
        struct file_list *next, *prev;
-       struct file_struct **files;
+       struct file_struct **files, **sorted;
        alloc_pool_t file_pool;
        int count, malloced;
        int low, high; /* 0-relative index values excluding empties */
index fd229d6..dad93e8 100644 (file)
--- a/rsync.yo
+++ b/rsync.yo
@@ -402,6 +402,7 @@ to the detailed description below for a complete description.  verb(
      --only-write-batch=FILE like --write-batch but w/o updating dest
      --read-batch=FILE       read a batched update from FILE
      --protocol=NUM          force an older protocol version to be used
+     --iconv=CONVERT_SPEC    request charset conversion of filesnames
      --checksum-seed=NUM     set block/file checksum seed (advanced)
  -4, --ipv4                  prefer IPv4
  -6, --ipv6                  prefer IPv6
@@ -1866,6 +1867,24 @@ bf(--read-batch) option, you should use "--protocol=28" when creating the
 batch file to force the older protocol version to be used in the batch
 file (assuming you can't upgrade the rsync on the reading system).
 
+dit(bf(--iconv=CONVERT_SPEC)) Rsync can convert filenames between character
+sets using this option.  Using a CONVERT_SPEC of "." tells rsync to look up
+the default character-set via the locale setting.  Alternately, you can
+fully specify what conversion to do by giving a local and a remote charset
+separated by a comma (local first), e.g. bf(--iconv=utf8,iso88591).
+Finally, you can specify a CONVERT_SPEC of "-" to turn off any conversion.
+The default setting of this option is site-specific, and can also be
+affected via the RSYNC_ICONV environment variable.
+
+Note that rsync does not do any conversion of names in filter files
+(including include/exclude files), in a files-from file, nor those
+specified on the command line.  It is up to you to ensure that you're
+requesting the right names from a remote server, and you can specify
+extra include/exclude rules if there are filename differences on the
+two sides that need to be accounted for.  (In the future there may be
+a way to specify a UTF-8 filter rule that gets auto-converted to the
+local side's character set.)
+
 dit(bf(-4, --ipv4) or bf(-6, --ipv6)) Tells rsync to prefer IPv4/IPv6
 when creating sockets.  This only affects sockets that rsync has direct
 control over, such as the outgoing socket when directly contacting an
@@ -2617,6 +2636,8 @@ startdit()
 dit(bf(CVSIGNORE)) The CVSIGNORE environment variable supplements any
 ignore patterns in .cvsignore files. See the bf(--cvs-exclude) option for
 more details.
+dit(bf(RSYNC_ICONV)) Specify a default bf(--iconv) setting using this
+environment variable.
 dit(bf(RSYNC_RSH)) The RSYNC_RSH environment variable allows you to
 override the default shell used as the transport for rsync.  Command line
 options are permitted after the command name, just as in the bf(-e) option.
index 1d2189c..823452f 100644 (file)
--- a/socket.c
+++ b/socket.c
@@ -409,7 +409,7 @@ static int *open_socket_in(int type, int port, const char *bind_addr,
         * unsuccessful, or if the daemon is being run with -vv. */
        for (s = 0; s < ecnt; s++) {
                if (!i || verbose > 1)
-                       rwrite(FLOG, errmsgs[s], strlen(errmsgs[s]));
+                       rwrite(FLOG, errmsgs[s], strlen(errmsgs[s]), 0);
                free(errmsgs[s]);
        }
        free(errmsgs);
index 5c35004..15b3f42 100644 (file)
@@ -56,6 +56,8 @@ filter_outfile() {
        -e '/^done$/d' \
        -e '/ --whole-file$/d' \
        -e '/^total: /d' \
+       -e '/^client charset: /d' \
+       -e '/^server charset: /d' \
        -e '/^$/,$d' \
        <"$outfile" >"$outfile.new"
     mv "$outfile.new" "$outfile"