Matt's transliterate patch.
authorWayne Davison <wayned@samba.org>
Tue, 6 Nov 2007 18:14:36 +0000 (18:14 +0000)
committerWayne Davison <wayned@samba.org>
Tue, 6 Nov 2007 18:14:36 +0000 (18:14 +0000)
transliterate.diff [new file with mode: 0644]

diff --git a/transliterate.diff b/transliterate.diff
new file mode 100644 (file)
index 0000000..89043c7
--- /dev/null
@@ -0,0 +1,167 @@
+This patch adds an option --tr=BAD/GOOD to transliterate filenames.  It
+can be used to remove characters illegal on the destination filesystem.
+Jeff Weber expressed interest in this:
+
+http://lists.samba.org/archive/rsync/2007-October/018996.html
+
+This patch is a COMPLETE HACK that covers the most common cases.  Others
+are welcome to improve it.
+
+To use this patch, run these commands for a successful build:
+
+    patch -p1 <patches/transliterate.diff
+    ./configure                                 (optional if already run)
+    make
+
+--- old/flist.c
++++ new/flist.c
+@@ -80,6 +80,9 @@ extern int filesfrom_convert;
+ extern iconv_t ic_send, ic_recv;
+ #endif
++extern char *tr_opt, *tr_left, *tr_right;
++extern int tr_right_len;
++
+ #define PTR_SIZE (sizeof (struct file_struct *))
+ int io_error;
+@@ -600,6 +603,24 @@ static void send_file_entry(int f, struc
+               stats.total_size += F_LENGTH(file);
+ }
++static void transliterate(char *thisname)
++{
++      char *p1, *p2, *pleft;
++
++      for (p1 = p2 = thisname; *p1; p1++) {
++              /* Look up the current character in the left string. */
++              pleft = strchr(tr_left, *p1);
++              if (!pleft)
++                      /* Not found: no change. */
++                      *p2++ = *p1;
++              else if (pleft - tr_left < tr_right_len)
++                      /* Store replacement from the right string. */
++                      *p2++ = tr_right[pleft - tr_left];
++              /* Otherwise delete. */
++      }
++      *p2 = '\0';
++}
++
+ static struct file_struct *recv_file_entry(struct file_list *flist,
+                                          int xflags, int f)
+ {
+@@ -668,6 +689,9 @@ static struct file_struct *recv_file_ent
+       }
+ #endif
++      if (tr_opt)
++              transliterate(thisname);
++
+       clean_fname(thisname, 0);
+       if (sanitize_paths)
+--- old/options.c
++++ new/options.c
+@@ -181,6 +181,8 @@ int logfile_format_has_i = 0;
+ int logfile_format_has_o_or_i = 0;
+ int always_checksum = 0;
+ int list_only = 0;
++char *tr_opt = NULL, *tr_left = NULL, *tr_right = NULL;
++int tr_right_len = 0;
+ #define MAX_BATCH_NAME_LEN 256        /* Must be less than MAXPATHLEN-13 */
+ char *batch_name = NULL;
+@@ -423,6 +425,7 @@ void usage(enum logcode F)
+ #ifdef ICONV_OPTION
+   rprintf(F,"     --iconv=CONVERT_SPEC    request charset conversion of filenames\n");
+ #endif
++  rprintf(F,"     --tr=BAD/GOOD           transliterate filenames\n");
+   rprintf(F," -4, --ipv4                  prefer IPv4\n");
+   rprintf(F," -6, --ipv6                  prefer IPv6\n");
+   rprintf(F,"     --version               print version number\n");
+@@ -609,6 +612,7 @@ static struct poptOption long_options[] 
+ #ifdef ICONV_OPTION
+   {"iconv",            0,  POPT_ARG_STRING, &iconv_opt, 0, 0, 0 },
+ #endif
++  {"tr",               0,  POPT_ARG_STRING, &tr_opt, 0, 0, 0 },
+   {"ipv4",            '4', POPT_ARG_VAL,    &default_af_hint, AF_INET, 0, 0 },
+   {"ipv6",            '6', POPT_ARG_VAL,    &default_af_hint, AF_INET6, 0, 0 },
+   {"8-bit-output",    '8', POPT_ARG_NONE,   &allow_8bit_chars, 0, 0, 0 },
+@@ -1620,6 +1624,31 @@ int parse_arguments(int *argc_p, const c
+               }
+       }
++      /* Easiest way to get a local server right is to do this on both sides */
++      if (tr_opt) {
++              if (*tr_opt) {
++                      char *p;
++
++                      need_unsorted_flist = 1;
++                      /* Our mutation shouldn't interfere with transmission of the
++                       * original option to the server. */
++                      tr_left = strdup(tr_opt);
++                      p = strchr(tr_left, '/');
++                      if (p != NULL) {
++                              *p = '\0';
++                              p++;
++                              tr_right = p;
++                              tr_right_len = strlen(tr_right);
++                              if (strchr(tr_right, '/') != NULL) {
++                                      snprintf(err_buf, sizeof err_buf,
++                                              "--tr cannot transliterate slashes\n");
++                                      return 0;
++                              }
++                      }
++              } else
++                      tr_opt = NULL;
++      }
++
+       am_starting_up = 0;
+       return 1;
+@@ -1988,6 +2017,12 @@ void server_options(char **args, int *ar
+       else if (remove_source_files)
+               args[ac++] = "--remove-sent-files";
++      if (tr_opt) {
++              if (asprintf(&arg, "--tr=%s", tr_opt) < 0)
++                      goto oom;
++              args[ac++] = arg;
++      }
++
+       *argc_p = ac;
+       return;
+--- old/rsync.yo
++++ new/rsync.yo
+@@ -422,6 +422,7 @@ to the detailed description below for a 
+      --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 filenames
++     --tr=BAD/GOOD           transliterate filenames
+      --checksum-seed=NUM     set block/file checksum seed (advanced)
+  -4, --ipv4                  prefer IPv4
+  -6, --ipv6                  prefer IPv6
+@@ -2013,6 +2014,22 @@ specifying matching rules that can match
+ For instance, you can specify extra include/exclude rules if there are
+ filename differences on the two sides that need to be accounted for.
++dit(bf(--tr=BAD/GOOD)) Transliterates filenames on the receiver, after the
++iconv conversion (if any).  This can be used to remove characters illegal
++on the destination filesystem.  If you use this option, consider saving a
++"find . -ls" listing of the source in the destination to help you determine
++the original filenames in case of need.
++
++The argument consists of a string of characters to remove, optionally
++followed by a slash and a string of corresponding characters with which to
++replace them.  The second string may be shorter, in which case any leftover
++characters in the first string are simply deleted.  For example,
++bf(--tr=':\/!') replaces colons with exclamation marks and deletes backslashes.
++Slashes cannot be transliterated because it would cause havoc.
++
++If the receiver is invoked over a remote shell, use bf(--protect-args) to
++stop the shell from interpreting any nasty characters in the argument.
++
+ 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