Matt's recent improvements, slightly edited.
[rsync/rsync-patches.git] / ignore-case.diff
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 --- old/exclude.c
11 +++ new/exclude.c
12 @@ -32,6 +32,7 @@ extern int io_error;
13  extern int local_server;
14  extern int prune_empty_dirs;
15  extern int ignore_perishable;
16 +extern int ignore_case;
17  extern int delete_mode;
18  extern int delete_excluded;
19  extern int cvs_exclude;
20 @@ -580,16 +581,17 @@ static int rule_matches(char *name, stru
21                 if (litmatch_array(pattern, strings, slash_handling))
22                         return ret_match;
23         } else if (anchored_match) {
24 -               if (strcmp(name, pattern) == 0)
25 +               if ((ignore_case ? strcasecmp(name, pattern)
26 +                                : strcmp(name, pattern)) == 0)
27                         return ret_match;
28         } else {
29                 int l1 = strlen(name);
30                 int l2 = strlen(pattern);
31 -               if (l2 <= l1 &&
32 -                   strcmp(name+(l1-l2),pattern) == 0 &&
33 -                   (l1==l2 || name[l1-(l2+1)] == '/')) {
34 +               if (l2 <= l1
35 +                && (ignore_case ? strcasecmp(name + (l1-l2), pattern)
36 +                                : strcmp(name + (l1-l2), pattern)) == 0
37 +                && (l1 == l2 || name[l1 - (l2+1)] == '/'))
38                         return ret_match;
39 -               }
40         }
41  
42         return !ret_match;
43 --- old/flist.c
44 +++ new/flist.c
45 @@ -34,6 +34,7 @@ extern int inc_recurse;
46  extern int do_progress;
47  extern int always_checksum;
48  extern int module_id;
49 +extern int ignore_case;
50  extern int ignore_errors;
51  extern int numeric_ids;
52  extern int recurse;
53 @@ -2224,7 +2225,7 @@ int f_name_cmp(struct file_struct *f1, s
54         if (type1 != type2)
55                 return type1 == t_PATH ? 1 : -1;
56  
57 -       do {
58 +       while (1) {
59                 if (!*c1) {
60                         switch (state1) {
61                         case s_DIR:
62 @@ -2287,7 +2288,16 @@ int f_name_cmp(struct file_struct *f1, s
63                         if (type1 != type2)
64                                 return type1 == t_PATH ? 1 : -1;
65                 }
66 -       } while ((dif = (int)*c1++ - (int)*c2++) == 0);
67 +               if (ignore_case) {
68 +                       uchar ch1, ch2;
69 +                       ch1 = islower(*c1) ? toupper(*c1) : *c1;
70 +                       ch2 = islower(*c2) ? toupper(*c2) : *c2;
71 +                       c1++, c2++;
72 +                       if ((dif = (int)ch1 - (int)ch2) != 0)
73 +                               break;
74 +               } else if ((dif = (int)*c1++ - (int)*c2++) != 0)
75 +                       break;
76 +       }
77  
78         return dif;
79  }
80 --- old/lib/wildmatch.c
81 +++ new/lib/wildmatch.c
82 @@ -53,6 +53,8 @@
83  #define ISUPPER(c) (ISASCII(c) && isupper(c))
84  #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
85  
86 +extern int ignore_case;
87 +
88  #ifdef WILD_TEST_ITERATIONS
89  int wildmatch_iteration_count;
90  #endif
91 @@ -72,6 +74,8 @@ static int dowild(const uchar *p, const 
92      for ( ; (p_ch = *p) != '\0'; text++, p++) {
93         int matched, special;
94         uchar t_ch, prev_ch;
95 +       if (ignore_case && ISUPPER(p_ch))
96 +           p_ch = tolower(p_ch);
97         while ((t_ch = *text) == '\0') {
98             if (*a == NULL) {
99                 if (p_ch != '*')
100 @@ -237,12 +241,21 @@ static int dowild(const uchar *p, const 
101   * of "text" and any strings in array "a". */
102  static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)
103  {
104 +    uchar s_ch, t_ch;
105      for ( ; *s != '\0'; text++, s++) {
106         while (*text == '\0') {
107             if ((text = *a++) == NULL)
108                 return FALSE;
109         }
110 -       if (*text != *s)
111 +       s_ch = *s;
112 +       t_ch = *text;
113 +       if (ignore_case) {
114 +           if (ISUPPER(s_ch))
115 +               s_ch = tolower(s_ch);
116 +           if (ISUPPER(t_ch))
117 +               t_ch = tolower(t_ch);
118 +       }
119 +       if (t_ch != s_ch)
120             return FALSE;
121      }
122  
123 @@ -288,10 +301,14 @@ static const uchar *trailing_N_elements(
124  int wildmatch(const char *pattern, const char *text)
125  {
126      static const uchar *nomore[1]; /* A NULL pointer. */
127 +    int ret;
128  #ifdef WILD_TEST_ITERATIONS
129      wildmatch_iteration_count = 0;
130  #endif
131 -    return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
132 +    force_lower_case = ignore_case;
133 +    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
134 +    force_lower_case = 0;
135 +    return ret;
136  }
137  
138  /* Match the "pattern" against the forced-to-lower-case "text" string. */
139 --- old/options.c
140 +++ new/options.c
141 @@ -111,6 +111,7 @@ OFF_T max_size = 0;
142  OFF_T min_size = 0;
143  int ignore_errors = 0;
144  int modify_window = 0;
145 +int ignore_case = 0;
146  int blocking_io = -1;
147  int checksum_seed = 0;
148  int inplace = 0;
149 @@ -366,6 +367,7 @@ void usage(enum logcode F)
150    rprintf(F,"     --include-from=FILE     read include patterns from FILE\n");
151    rprintf(F,"     --files-from=FILE       read list of source-file names from FILE\n");
152    rprintf(F," -0, --from0                 all *-from/filter files are delimited by 0s\n");
153 +  rprintf(F,"     --ignore-case           ignore case when comparing filenames\n");
154    rprintf(F,"     --address=ADDRESS       bind address for outgoing socket to daemon\n");
155    rprintf(F,"     --port=PORT             specify double-colon alternate port number\n");
156    rprintf(F,"     --sockopts=OPTIONS      specify custom TCP options\n");
157 @@ -534,6 +536,7 @@ static struct poptOption long_options[] 
158    {"only-write-batch", 0,  POPT_ARG_STRING, &batch_name, OPT_ONLY_WRITE_BATCH, 0, 0 },
159    {"files-from",       0,  POPT_ARG_STRING, &files_from, 0, 0, 0 },
160    {"from0",           '0', POPT_ARG_NONE,   &eol_nulls, 0, 0, 0},
161 +  {"ignore-case",      0,  POPT_ARG_NONE,   &ignore_case, 0, 0, 0 },
162    {"numeric-ids",      0,  POPT_ARG_NONE,   &numeric_ids, 0, 0, 0 },
163    {"timeout",          0,  POPT_ARG_INT,    &io_timeout, 0, 0, 0 },
164    {"rsh",             'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 },
165 @@ -1733,6 +1736,9 @@ void server_options(char **args,int *arg
166                 args[ac++] = arg;
167         }
168  
169 +       if (ignore_case)
170 +               args[ac++] = "--ignore-case";
171 +
172         if (partial_dir && am_sender) {
173                 if (partial_dir != tmp_partialdir) {
174                         args[ac++] = "--partial-dir";
175 --- old/wildtest.c
176 +++ new/wildtest.c
177 @@ -31,6 +31,7 @@ int fnmatch_errors = 0;
178  #endif
179  
180  int wildmatch_errors = 0;
181 +int ignore_case = 0;
182  
183  typedef char bool;
184