Commit | Line | Data |
---|---|---|
b2b87acf WD |
1 | This patch adds an option --tr=BAD/GOOD to transliterate filenames. It |
2 | can be used to remove characters illegal on the destination filesystem. | |
3 | Jeff Weber expressed interest in this: | |
4 | ||
5 | http://lists.samba.org/archive/rsync/2007-October/018996.html | |
6 | ||
7 | This patch is a COMPLETE HACK that covers the most common cases. Others | |
8 | are welcome to improve it. | |
9 | ||
10 | To use this patch, run these commands for a successful build: | |
11 | ||
12 | patch -p1 <patches/transliterate.diff | |
13 | ./configure (optional if already run) | |
14 | make | |
15 | ||
c1ff70aa | 16 | based-on: a01e3b490eb36ccf9e704840e1b6683dab867550 |
cc3e685d WD |
17 | diff --git a/flist.c b/flist.c |
18 | --- a/flist.c | |
19 | +++ b/flist.c | |
c1ff70aa | 20 | @@ -88,6 +88,9 @@ extern int filesfrom_convert; |
b2b87acf WD |
21 | extern iconv_t ic_send, ic_recv; |
22 | #endif | |
23 | ||
24 | +extern char *tr_opt, *tr_left, *tr_right; | |
25 | +extern int tr_right_len; | |
26 | + | |
72e5645e WD |
27 | #ifdef HAVE_UTIMENSAT |
28 | #ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC | |
29 | #define ST_MTIME_NSEC st_mtim.tv_nsec | |
c1ff70aa | 30 | @@ -675,6 +678,24 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, |
b2b87acf WD |
31 | stats.total_size += F_LENGTH(file); |
32 | } | |
33 | ||
34 | +static void transliterate(char *thisname) | |
35 | +{ | |
36 | + char *p1, *p2, *pleft; | |
37 | + | |
38 | + for (p1 = p2 = thisname; *p1; p1++) { | |
39 | + /* Look up the current character in the left string. */ | |
40 | + pleft = strchr(tr_left, *p1); | |
41 | + if (!pleft) | |
42 | + /* Not found: no change. */ | |
43 | + *p2++ = *p1; | |
44 | + else if (pleft - tr_left < tr_right_len) | |
45 | + /* Store replacement from the right string. */ | |
46 | + *p2++ = tr_right[pleft - tr_left]; | |
47 | + /* Otherwise delete. */ | |
48 | + } | |
49 | + *p2 = '\0'; | |
50 | +} | |
51 | + | |
72e5645e | 52 | static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags) |
b2b87acf | 53 | { |
72e5645e | 54 | static int64 modtime; |
c1ff70aa | 55 | @@ -743,6 +764,9 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x |
b2b87acf WD |
56 | } |
57 | #endif | |
58 | ||
59 | + if (tr_opt) | |
60 | + transliterate(thisname); | |
61 | + | |
65ecbe35 WD |
62 | if (*thisname) |
63 | clean_fname(thisname, 0); | |
b2b87acf | 64 | |
cc3e685d WD |
65 | diff --git a/options.c b/options.c |
66 | --- a/options.c | |
67 | +++ b/options.c | |
fc557362 | 68 | @@ -190,6 +190,8 @@ int logfile_format_has_i = 0; |
b2b87acf WD |
69 | int logfile_format_has_o_or_i = 0; |
70 | int always_checksum = 0; | |
71 | int list_only = 0; | |
72 | +char *tr_opt = NULL, *tr_left = NULL, *tr_right = NULL; | |
73 | +int tr_right_len = 0; | |
74 | ||
75 | #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */ | |
76 | char *batch_name = NULL; | |
72e5645e | 77 | @@ -783,6 +785,7 @@ void usage(enum logcode F) |
b2b87acf WD |
78 | #ifdef ICONV_OPTION |
79 | rprintf(F," --iconv=CONVERT_SPEC request charset conversion of filenames\n"); | |
80 | #endif | |
81 | + rprintf(F," --tr=BAD/GOOD transliterate filenames\n"); | |
82 | rprintf(F," -4, --ipv4 prefer IPv4\n"); | |
83 | rprintf(F," -6, --ipv6 prefer IPv6\n"); | |
84 | rprintf(F," --version print version number\n"); | |
72e5645e | 85 | @@ -996,6 +999,7 @@ static struct poptOption long_options[] = { |
b2b87acf | 86 | {"iconv", 0, POPT_ARG_STRING, &iconv_opt, 0, 0, 0 }, |
85096e5e | 87 | {"no-iconv", 0, POPT_ARG_NONE, 0, OPT_NO_ICONV, 0, 0 }, |
b2b87acf WD |
88 | #endif |
89 | + {"tr", 0, POPT_ARG_STRING, &tr_opt, 0, 0, 0 }, | |
90 | {"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 }, | |
91 | {"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 }, | |
c0c7984e | 92 | {"8-bit-output", '8', POPT_ARG_VAL, &allow_8bit_chars, 1, 0, 0 }, |
c1ff70aa | 93 | @@ -2247,6 +2251,31 @@ int parse_arguments(int *argc_p, const char ***argv_p) |
b2b87acf WD |
94 | } |
95 | } | |
96 | ||
97 | + /* Easiest way to get a local server right is to do this on both sides */ | |
98 | + if (tr_opt) { | |
99 | + if (*tr_opt) { | |
100 | + char *p; | |
101 | + | |
102 | + need_unsorted_flist = 1; | |
103 | + /* Our mutation shouldn't interfere with transmission of the | |
104 | + * original option to the server. */ | |
105 | + tr_left = strdup(tr_opt); | |
106 | + p = strchr(tr_left, '/'); | |
107 | + if (p != NULL) { | |
108 | + *p = '\0'; | |
109 | + p++; | |
110 | + tr_right = p; | |
111 | + tr_right_len = strlen(tr_right); | |
112 | + if (strchr(tr_right, '/') != NULL) { | |
113 | + snprintf(err_buf, sizeof err_buf, | |
114 | + "--tr cannot transliterate slashes\n"); | |
115 | + return 0; | |
116 | + } | |
117 | + } | |
118 | + } else | |
119 | + tr_opt = NULL; | |
120 | + } | |
121 | + | |
122 | am_starting_up = 0; | |
123 | ||
124 | return 1; | |
c1ff70aa | 125 | @@ -2661,6 +2690,12 @@ void server_options(char **args, int *argc_p) |
b2b87acf WD |
126 | else if (remove_source_files) |
127 | args[ac++] = "--remove-sent-files"; | |
128 | ||
129 | + if (tr_opt) { | |
130 | + if (asprintf(&arg, "--tr=%s", tr_opt) < 0) | |
131 | + goto oom; | |
132 | + args[ac++] = arg; | |
133 | + } | |
134 | + | |
ae306a29 WD |
135 | if (ac > MAX_SERVER_ARGS) { /* Not possible... */ |
136 | rprintf(FERROR, "argc overflow in server_options().\n"); | |
137 | exit_cleanup(RERR_MALLOC); | |
cc3e685d WD |
138 | diff --git a/rsync.yo b/rsync.yo |
139 | --- a/rsync.yo | |
140 | +++ b/rsync.yo | |
fc557362 | 141 | @@ -436,6 +436,7 @@ to the detailed description below for a complete description. verb( |
b2b87acf WD |
142 | --read-batch=FILE read a batched update from FILE |
143 | --protocol=NUM force an older protocol version to be used | |
144 | --iconv=CONVERT_SPEC request charset conversion of filenames | |
145 | + --tr=BAD/GOOD transliterate filenames | |
146 | --checksum-seed=NUM set block/file checksum seed (advanced) | |
147 | -4, --ipv4 prefer IPv4 | |
148 | -6, --ipv6 prefer IPv6 | |
c1ff70aa | 149 | @@ -2373,6 +2374,22 @@ daemon uses the charset specified in its "charset" configuration parameter |
85096e5e WD |
150 | regardless of the remote charset you actually pass. Thus, you may feel free to |
151 | specify just the local charset for a daemon transfer (e.g. bf(--iconv=utf8)). | |
b2b87acf WD |
152 | |
153 | +dit(bf(--tr=BAD/GOOD)) Transliterates filenames on the receiver, after the | |
154 | +iconv conversion (if any). This can be used to remove characters illegal | |
155 | +on the destination filesystem. If you use this option, consider saving a | |
156 | +"find . -ls" listing of the source in the destination to help you determine | |
157 | +the original filenames in case of need. | |
158 | + | |
159 | +The argument consists of a string of characters to remove, optionally | |
160 | +followed by a slash and a string of corresponding characters with which to | |
161 | +replace them. The second string may be shorter, in which case any leftover | |
162 | +characters in the first string are simply deleted. For example, | |
163 | +bf(--tr=':\/!') replaces colons with exclamation marks and deletes backslashes. | |
164 | +Slashes cannot be transliterated because it would cause havoc. | |
165 | + | |
166 | +If the receiver is invoked over a remote shell, use bf(--protect-args) to | |
167 | +stop the shell from interpreting any nasty characters in the argument. | |
168 | + | |
169 | dit(bf(-4, --ipv4) or bf(-6, --ipv6)) Tells rsync to prefer IPv4/IPv6 | |
170 | when creating sockets. This only affects sockets that rsync has direct | |
171 | control over, such as the outgoing socket when directly contacting an |