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