-
Notifications
You must be signed in to change notification settings - Fork 8
/
ignore-case.diff
289 lines (271 loc) · 8.61 KB
/
ignore-case.diff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
This adds the --ignore-case option, which makes rsync compare filenames
in a case-insensitive manner.
To use this patch, run these commands for a successful build:
patch -p1 <patches/ignore-case.diff
./configure (optional if already run)
make
TODO:
- Make this code handle multibyte character encodings, and honor the
--iconv setting when converting case.
based-on: 6c8ca91c731b7bf2b081694bda85b7dadc2b7aff
diff --git a/exclude.c b/exclude.c
--- a/exclude.c
+++ b/exclude.c
@@ -966,16 +966,15 @@ static int rule_matches(const char *fname, filter_rule *ex, int name_flags)
if (litmatch_array(pattern, strings, slash_handling))
return ret_match;
} else if (anchored_match) {
- if (strcmp(name, pattern) == 0)
+ if (ic_strEQ(name, pattern))
return ret_match;
} else {
int l1 = strlen(name);
int l2 = strlen(pattern);
- if (l2 <= l1 &&
- strcmp(name+(l1-l2),pattern) == 0 &&
- (l1==l2 || name[l1-(l2+1)] == '/')) {
+ if (l2 <= l1
+ && ic_strEQ(name + (l1-l2), pattern)
+ && (l1 == l2 || name[l1 - (l2+1)] == '/'))
return ret_match;
- }
}
return !ret_match;
diff --git a/flist.c b/flist.c
--- a/flist.c
+++ b/flist.c
@@ -34,6 +34,7 @@ extern int am_generator;
extern int inc_recurse;
extern int always_checksum;
extern int module_id;
+extern int ignore_case;
extern int ignore_errors;
extern int numeric_ids;
extern int quiet;
@@ -2638,7 +2639,8 @@ struct file_list *recv_file_list(int f, int dir_ndx)
cur_dir++;
if (cur_dir != good_dirname) {
const char *d = dir_ndx >= 0 ? f_name(dir_flist->files[dir_ndx], NULL) : empty_dir;
- if (strcmp(cur_dir, d) != 0) {
+ int dir_differs = ignore_case ? strcasecmp(cur_dir, d) : strcmp(cur_dir, d);
+ if (dir_differs) {
rprintf(FERROR,
"ABORTING due to invalid path from sender: %s/%s\n",
cur_dir, file->basename);
@@ -3205,6 +3207,7 @@ int f_name_cmp(const struct file_struct *f1, const struct file_struct *f2)
{
int dif;
const uchar *c1, *c2;
+ uchar ch1, ch2;
enum fnc_state state1, state2;
enum fnc_type type1, type2;
enum fnc_type t_path = protocol_version >= 29 ? t_PATH : t_ITEM;
@@ -3315,7 +3318,15 @@ int f_name_cmp(const struct file_struct *f1, const struct file_struct *f2)
if (type1 != type2)
return type1 == t_PATH ? 1 : -1;
}
- } while ((dif = (int)*c1++ - (int)*c2++) == 0);
+ ch1 = *c1++;
+ ch2 = *c2++;
+ if (ignore_case) {
+ if (isupper(ch1))
+ ch1 = tolower(ch1);
+ if (isupper(ch2))
+ ch2 = tolower(ch2);
+ }
+ } while ((dif = (int)ch1 - (int)ch2) == 0);
return dif;
}
diff --git a/ifuncs.h b/ifuncs.h
--- a/ifuncs.h
+++ b/ifuncs.h
@@ -110,3 +110,38 @@ static inline char *my_strdup(const char *str, const char *file, int line)
memcpy(buf, str, len);
return buf;
}
+
+static inline int
+strEQ(const char *s1, const char *s2)
+{
+ return strcmp(s1, s2) == 0;
+}
+
+static inline int
+strnEQ(const char *s1, const char *s2, size_t n)
+{
+ return strncmp(s1, s2, n) == 0;
+}
+
+static inline int
+ic_strEQ(const char *s1, const char *s2)
+{
+ extern int ignore_case;
+ if (ignore_case)
+ return strcasecmp(s1, s2) == 0;
+ return strcmp(s1, s2) == 0;
+}
+
+static inline int
+ic_strnEQ(const char *s1, const char *s2, size_t n)
+{
+ extern int ignore_case;
+ if (ignore_case)
+ return strncasecmp(s1, s2, n) == 0;
+ return strncmp(s1, s2, n) == 0;
+}
+
+#define strNE(s1,s2) (!strEQ(s1,s2))
+#define strnNE(s1,s2,n) (!strnEQ(s1,s2,n))
+#define ic_strNE(s1,s2) (!ic_strEQ(s1,s2))
+#define ic_strnNE(s1,s2) (!ic_strnEQ(s1,s2,n))
diff --git a/lib/wildmatch.c b/lib/wildmatch.c
--- a/lib/wildmatch.c
+++ b/lib/wildmatch.c
@@ -53,6 +53,8 @@
#define ISUPPER(c) (ISASCII(c) && isupper(c))
#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
+extern int ignore_case;
+
#ifdef WILD_TEST_ITERATIONS
int wildmatch_iteration_count;
#endif
@@ -72,6 +74,8 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
for ( ; (p_ch = *p) != '\0'; text++, p++) {
int matched, special;
uchar t_ch, prev_ch;
+ if (ignore_case && ISUPPER(p_ch))
+ p_ch = tolower(p_ch);
while ((t_ch = *text) == '\0') {
if (*a == NULL) {
if (p_ch != '*')
@@ -237,12 +241,21 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
* of "text" and any strings in array "a". */
static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)
{
+ uchar s_ch, t_ch;
for ( ; *s != '\0'; text++, s++) {
while (*text == '\0') {
if ((text = *a++) == NULL)
return FALSE;
}
- if (*text != *s)
+ s_ch = *s;
+ t_ch = *text;
+ if (ignore_case) {
+ if (ISUPPER(s_ch))
+ s_ch = tolower(s_ch);
+ if (ISUPPER(t_ch))
+ t_ch = tolower(t_ch);
+ }
+ if (t_ch != s_ch)
return FALSE;
}
@@ -288,10 +301,14 @@ static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)
int wildmatch(const char *pattern, const char *text)
{
static const uchar *nomore[1]; /* A NULL pointer. */
+ int ret;
#ifdef WILD_TEST_ITERATIONS
wildmatch_iteration_count = 0;
#endif
- return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+ force_lower_case = ignore_case;
+ ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+ force_lower_case = 0;
+ return ret;
}
/* Match the "pattern" against the forced-to-lower-case "text" string. */
@@ -331,12 +348,14 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)
if (!text)
return FALSE;
+ force_lower_case = ignore_case;
+
if ((matched = dowild(p, text, a)) != TRUE && where < 0
&& matched != ABORT_ALL) {
while (1) {
if (*text == '\0') {
if ((text = (uchar*)*a++) == NULL)
- return FALSE;
+ break;
continue;
}
if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
@@ -344,6 +363,9 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)
break;
}
}
+
+ force_lower_case = 0;
+
return matched == TRUE;
}
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
@@ -131,6 +131,7 @@ OFF_T max_size = -1;
OFF_T min_size = -1;
int ignore_errors = 0;
int modify_window = 0;
+int ignore_case = 0;
int blocking_io = -1;
int checksum_seed = 0;
int inplace = 0;
@@ -784,6 +785,8 @@ static struct poptOption long_options[] = {
{"read-batch", 0, POPT_ARG_STRING, &batch_name, OPT_READ_BATCH, 0, 0 },
{"write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 },
{"only-write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_ONLY_WRITE_BATCH, 0, 0 },
+ {"ignore-case", 0, POPT_ARG_VAL, &ignore_case, 1, 0, 0 },
+ {"no-ignore-case", 0, POPT_ARG_VAL, &ignore_case, 0, 0, 0 },
{"files-from", 0, POPT_ARG_STRING, &files_from, 0, 0, 0 },
{"from0", '0', POPT_ARG_VAL, &eol_nulls, 1, 0, 0},
{"no-from0", 0, POPT_ARG_VAL, &eol_nulls, 0, 0, 0},
@@ -2865,6 +2868,9 @@ void server_options(char **args, int *argc_p)
args[ac++] = arg;
}
+ if (ignore_case)
+ args[ac++] = "--ignore-case";
+
if (partial_dir && am_sender) {
if (partial_dir != tmp_partialdir) {
args[ac++] = "--partial-dir";
diff --git a/rsync.1.md b/rsync.1.md
--- a/rsync.1.md
+++ b/rsync.1.md
@@ -528,6 +528,7 @@ has its own detailed description later in this manpage.
--secluded-args, -s use the protocol to safely send the args
--trust-sender trust the remote sender's file list
--copy-as=USER[:GROUP] specify user & optional group for the copy
+--ignore-case ignore case when comparing filenames
--address=ADDRESS bind address for outgoing socket to daemon
--port=PORT specify double-colon alternate port number
--sockopts=OPTIONS specify custom TCP options
@@ -2583,6 +2584,12 @@ expand it.
> sudo rsync -aive lsh -M--copy-as=joe src/ lh:dest/
+0. `--ignore-case`
+
+ This option tells rsync to ignore upper-/lower-case differences when
+ comparing filenames. This can avoid problems when sending files to a
+ filesystem that ignores these differences.
+
0. `--temp-dir=DIR`, `-T`
This option instructs rsync to use DIR as a scratch directory when creating
diff --git a/t_stub.c b/t_stub.c
--- a/t_stub.c
+++ b/t_stub.c
@@ -34,6 +34,7 @@ int preserve_perms = 0;
int preserve_executability = 0;
int omit_link_times = 0;
int open_noatime = 0;
+int ignore_case = 0;
size_t max_alloc = 0; /* max_alloc is needed when combined with util2.o */
char *partial_dir;
char *module_dir;
diff --git a/wildtest.c b/wildtest.c
--- a/wildtest.c
+++ b/wildtest.c
@@ -30,6 +30,7 @@
int fnmatch_errors = 0;
#endif
+int ignore_case = 0;
int wildmatch_errors = 0;
typedef char bool;