A bit more fixing, as requested by Matt.
[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 @@ -331,12 +348,14 @@ int wildmatch_array(const char *pattern,
140      if (!text)
141         return FALSE;
142  
143 +    force_lower_case = ignore_case;
144 +
145      if ((matched = dowild(p, text, a)) != TRUE && where < 0
146       && matched != ABORT_ALL) {
147         while (1) {
148             if (*text == '\0') {
149                 if ((text = (uchar*)*a++) == NULL)
150 -                   return FALSE;
151 +                   break;
152                 continue;
153             }
154             if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
155 @@ -344,6 +363,9 @@ int wildmatch_array(const char *pattern,
156                 break;
157         }
158      }
159 +
160 +    force_lower_case = 0;
161 +
162      return matched == TRUE;
163  }
164  
165 --- old/options.c
166 +++ new/options.c
167 @@ -112,6 +112,7 @@ OFF_T max_size = 0;
168  OFF_T min_size = 0;
169  int ignore_errors = 0;
170  int modify_window = 0;
171 +int ignore_case = 0;
172  int blocking_io = -1;
173  int checksum_seed = 0;
174  int inplace = 0;
175 @@ -367,6 +368,7 @@ void usage(enum logcode F)
176    rprintf(F,"     --include-from=FILE     read include patterns from FILE\n");
177    rprintf(F,"     --files-from=FILE       read list of source-file names from FILE\n");
178    rprintf(F," -0, --from0                 all *-from/filter files are delimited by 0s\n");
179 +  rprintf(F,"     --ignore-case           ignore case when comparing filenames\n");
180    rprintf(F,"     --address=ADDRESS       bind address for outgoing socket to daemon\n");
181    rprintf(F,"     --port=PORT             specify double-colon alternate port number\n");
182    rprintf(F,"     --sockopts=OPTIONS      specify custom TCP options\n");
183 @@ -539,6 +541,7 @@ static struct poptOption long_options[] 
184    {"only-write-batch", 0,  POPT_ARG_STRING, &batch_name, OPT_ONLY_WRITE_BATCH, 0, 0 },
185    {"files-from",       0,  POPT_ARG_STRING, &files_from, 0, 0, 0 },
186    {"from0",           '0', POPT_ARG_NONE,   &eol_nulls, 0, 0, 0},
187 +  {"ignore-case",      0,  POPT_ARG_NONE,   &ignore_case, 0, 0, 0 },
188    {"numeric-ids",      0,  POPT_ARG_NONE,   &numeric_ids, 0, 0, 0 },
189    {"timeout",          0,  POPT_ARG_INT,    &io_timeout, 0, 0, 0 },
190    {"rsh",             'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 },
191 @@ -1738,6 +1741,9 @@ void server_options(char **args,int *arg
192                 args[ac++] = arg;
193         }
194  
195 +       if (ignore_case)
196 +               args[ac++] = "--ignore-case";
197 +
198         if (partial_dir && am_sender) {
199                 if (partial_dir != tmp_partialdir) {
200                         args[ac++] = "--partial-dir";
201 --- old/wildtest.c
202 +++ new/wildtest.c
203 @@ -31,6 +31,7 @@ int fnmatch_errors = 0;
204  #endif
205  
206  int wildmatch_errors = 0;
207 +int ignore_case = 0;
208  
209  typedef char bool;
210