Commit | Line | Data |
---|---|---|
8a529471 WD |
1 | After applying this patch and running configure, you MUST run this |
2 | command before "make": | |
3 | ||
4 | make proto | |
5 | ||
524989ae WD |
6 | This patch adds the ability to merge rules into your excludes/includes |
7 | using a ". FILE" idiom. If you specify a name without slashes, that | |
8 | filename will be looked for in every subdirectory that rsync visits, | |
9 | and its rules will affect the current directory and its subdirectories. | |
a55d21aa WD |
10 | |
11 | For example: | |
12 | ||
13 | rsync -av --exclude='. .excl' from/ to | |
14 | ||
15 | The above will look for a file named ".excl" in every directory of the | |
524989ae WD |
16 | hierarchy that rsync visits, and it will exclude (by default) names |
17 | based on the rules found therein. If one of the .excl files contains | |
18 | this: | |
a55d21aa WD |
19 | |
20 | + *.c | |
524989ae WD |
21 | . .excl2 |
22 | . ./.excl3 | |
23 | *.o | |
a55d21aa | 24 | |
524989ae WD |
25 | Then the file ".excl2" will also be read in the current dir, and all |
26 | subdirs of the current dir. The file ".excl3" would just be read in | |
27 | for the current dir because its name contained a slash. | |
a55d21aa WD |
28 | |
29 | ..wayne.. | |
30 | ||
bc95f62b WD |
31 | --- exclude.c 15 May 2004 19:31:13 -0000 1.77 |
32 | +++ exclude.c 15 May 2004 20:03:30 -0000 | |
d1e7c1c8 | 33 | @@ -30,32 +30,89 @@ extern int verbose; |
a55d21aa WD |
34 | extern int eol_nulls; |
35 | extern int list_only; | |
36 | extern int recurse; | |
37 | +extern int io_error; | |
38 | +extern int sanitize_paths; | |
39 | ||
40 | extern char curr_dir[]; | |
41 | ||
42 | -struct exclude_list_struct exclude_list = { 0, 0, "" }; | |
524989ae | 43 | -struct exclude_list_struct local_exclude_list = { 0, 0, "per-dir .cvsignore " }; |
a55d21aa | 44 | -struct exclude_list_struct server_exclude_list = { 0, 0, "server " }; |
7cb7ae4e WD |
45 | +struct exclude_list_struct exclude_list = { 0, 0, 0, "" }; |
46 | +struct exclude_list_struct server_exclude_list = { 0, 0, 0, "server " }; | |
a55d21aa WD |
47 | char *exclude_path_prefix = NULL; |
48 | ||
d1e7c1c8 WD |
49 | -/** Build an exclude structure given a exclude pattern */ |
50 | -static void make_exclude(struct exclude_list_struct *listp, const char *pattern, | |
51 | - int pat_len, int include) | |
524989ae | 52 | +struct exclude_list_root { |
d1e7c1c8 | 53 | + struct exclude_list_struct *array; |
524989ae WD |
54 | + int cnt; |
55 | +} local_lists; | |
56 | + | |
a55d21aa WD |
57 | +static char dirbuf[MAXPATHLEN]; |
58 | +static unsigned int dirbuf_offset = 0; | |
a55d21aa | 59 | + |
d1e7c1c8 WD |
60 | +/* Each exclude_list_struct describes a singly-linked list by keeping track |
61 | + * of both the head and tail pointers. The list is slightly unusual in that | |
62 | + * a parent-dir's content can be appended to the end of the local list in a | |
63 | + * special way: the last item in the local list has its "next" pointer set | |
64 | + * to point to the parent's list, but the local list's tail pointer points | |
65 | + * at the end of the local list. Thus, if the local list is empty, the head | |
66 | + * will be pointing at the parent content but the tail will be NULL. To | |
67 | + * help you visualize this, here are the possible list arrangements: | |
68 | + * | |
69 | + * Completely Empty Local Content Only | |
70 | + * ================================== ==================================== | |
71 | + * head -> NULL head -> Local1 -> Local2 -> NULL | |
72 | + * tail -> NULL tail -------------^ | |
73 | + * | |
74 | + * Inherited Content Only Both Local and Inherited Content | |
75 | + * ================================== ==================================== | |
76 | + * head -> Parent1 -> Parent2 -> NULL head -> L1 -> L2 -> P1 -> P2 -> NULL | |
77 | + * tail -> NULL tail ---------^ | |
78 | + * | |
79 | + * This means that anyone wanting to traverse the whole list to USE it just | |
80 | + * needs to start at the head and use the "next" pointers until it goes | |
81 | + * NULL. To add new local content, we insert the item after the tail item | |
82 | + * and update the tail (obviously, if "tail" was NULL, we insert it at the | |
83 | + * head). To clear the local list, WE MUST NOT FREE THE INHERITED CONTENT | |
bc95f62b WD |
84 | + * because it is shared between the current list and our parent list(s). |
85 | + * The easiest way to handle this is to simply truncate the list after the | |
86 | + * tail item and then free the local list from the head. When inheriting | |
87 | + * the list for a new local dir, we just save off the exclude_list_struct | |
88 | + * values (so we can pop back to them) and set the tail to NULL. | |
d1e7c1c8 WD |
89 | + */ |
90 | + | |
91 | +/** Build an exclude structure given an exclude pattern. */ | |
524989ae | 92 | +static void make_exclude(struct exclude_list_struct *listp, const char *pat, |
bc95f62b | 93 | + unsigned int pat_len, unsigned int mflags) |
a55d21aa WD |
94 | { |
95 | struct exclude_struct *ret; | |
96 | const char *cp; | |
97 | - int ex_len; | |
98 | + unsigned int ex_len; | |
99 | + | |
524989ae | 100 | + if (mflags & MATCHFLG_MERGE_FILE) { |
a55d21aa WD |
101 | + struct exclude_struct *ex; |
102 | + /* If the local include file was already mentioned, don't | |
524989ae | 103 | + * add it again. */ |
a55d21aa | 104 | + for (ex = listp->head; ex; ex = ex->next) { |
524989ae | 105 | + if ((ex->match_flags & MATCHFLG_MERGE_FILE) |
a55d21aa | 106 | + && strlen(ex->pattern) == pat_len |
524989ae | 107 | + && strncmp(ex->pattern, pat, pat_len) == 0) |
a55d21aa WD |
108 | + return; |
109 | + } | |
524989ae WD |
110 | + if ((pat_len == 10 || (pat_len > 10 && pat[pat_len-11] == '/')) |
111 | + && strncmp(pat+pat_len-10, ".cvsignore", 10) == 0) { | |
a55d21aa WD |
112 | + mflags |= MATCHFLG_CVSIGNORE; |
113 | + mflags &= ~MATCHFLG_INCLUDE; | |
114 | + } else | |
115 | + mflags &= ~MATCHFLG_CVSIGNORE; | |
116 | + } | |
117 | ||
118 | ret = new(struct exclude_struct); | |
119 | if (!ret) | |
120 | out_of_memory("make_exclude"); | |
121 | ||
122 | memset(ret, 0, sizeof ret[0]); | |
123 | - ret->include = include; | |
124 | ||
125 | if (exclude_path_prefix) | |
126 | - ret->match_flags |= MATCHFLG_ABS_PATH; | |
524989ae | 127 | - if (exclude_path_prefix && *pattern == '/') |
a55d21aa | 128 | + mflags |= MATCHFLG_ABS_PATH; |
524989ae | 129 | + if (exclude_path_prefix && *pat == '/') |
a55d21aa WD |
130 | ex_len = strlen(exclude_path_prefix); |
131 | else | |
524989ae | 132 | ex_len = 0; |
d1e7c1c8 | 133 | @@ -64,33 +121,52 @@ static void make_exclude(struct exclude_ |
524989ae WD |
134 | out_of_memory("make_exclude"); |
135 | if (ex_len) | |
136 | memcpy(ret->pattern, exclude_path_prefix, ex_len); | |
137 | - strlcpy(ret->pattern + ex_len, pattern, pat_len + 1); | |
138 | + strlcpy(ret->pattern + ex_len, pat, pat_len + 1); | |
a55d21aa WD |
139 | pat_len += ex_len; |
140 | ||
141 | if (strpbrk(ret->pattern, "*[?")) { | |
142 | - ret->match_flags |= MATCHFLG_WILD; | |
143 | + mflags |= MATCHFLG_WILD; | |
144 | if ((cp = strstr(ret->pattern, "**")) != NULL) { | |
145 | - ret->match_flags |= MATCHFLG_WILD2; | |
146 | + mflags |= MATCHFLG_WILD2; | |
147 | /* If the pattern starts with **, note that. */ | |
148 | if (cp == ret->pattern) | |
149 | - ret->match_flags |= MATCHFLG_WILD2_PREFIX; | |
150 | + mflags |= MATCHFLG_WILD2_PREFIX; | |
151 | } | |
152 | } | |
153 | ||
154 | if (pat_len > 1 && ret->pattern[pat_len-1] == '/') { | |
155 | ret->pattern[pat_len-1] = 0; | |
156 | - ret->directory = 1; | |
157 | + mflags |= MATCHFLG_DIRECTORY; | |
158 | } | |
159 | ||
160 | for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++) | |
bc95f62b WD |
161 | - ret->slash_cnt++; |
162 | + ret->u.slash_cnt++; | |
a55d21aa | 163 | |
7cb7ae4e WD |
164 | - if (!listp->tail) |
165 | + if (!listp->tail) { | |
166 | + ret->next = listp->head; | |
a55d21aa | 167 | listp->head = listp->tail = ret; |
7cb7ae4e | 168 | - else { |
7cb7ae4e WD |
169 | + } else { |
170 | + ret->next = listp->tail->next; | |
abe86b1f WD |
171 | listp->tail->next = ret; |
172 | listp->tail = ret; | |
173 | } | |
a55d21aa | 174 | + |
524989ae | 175 | + if (mflags & MATCHFLG_MERGE_FILE) { |
a55d21aa | 176 | + struct exclude_list_struct *lp; |
524989ae | 177 | + int ndx = local_lists.cnt++; |
d1e7c1c8 | 178 | + local_lists.array = realloc_array(local_lists.array, |
524989ae | 179 | + struct exclude_list_struct, local_lists.cnt); |
d1e7c1c8 | 180 | + if (!local_lists.array) |
a55d21aa | 181 | + out_of_memory("make_exclude"); |
d1e7c1c8 | 182 | + lp = &local_lists.array[ndx]; |
7cb7ae4e | 183 | + lp->head = lp->tail = NULL; |
bc95f62b | 184 | + lp->parent = ret; |
524989ae | 185 | + if (asprintf(&lp->debug_type, "per-dir %s ", ret->pattern) < 0) |
a55d21aa | 186 | + out_of_memory("make_exclude"); |
bc95f62b | 187 | + ret->u.array_index = ndx; /* Overwrites u.slash_cnt. */ |
abe86b1f | 188 | + } |
a55d21aa WD |
189 | + |
190 | + ret->match_flags = mflags; | |
191 | } | |
192 | ||
193 | static void free_exclude(struct exclude_struct *ex) | |
d1e7c1c8 | 194 | @@ -99,18 +175,90 @@ static void free_exclude(struct exclude_ |
a55d21aa WD |
195 | free(ex); |
196 | } | |
197 | ||
198 | -void free_exclude_list(struct exclude_list_struct *listp) | |
199 | +static void free_exclude_list(struct exclude_list_struct *listp) | |
200 | { | |
7cb7ae4e WD |
201 | - struct exclude_struct *ent, *next; |
202 | - | |
203 | - for (ent = listp->head; ent; ent = next) { | |
204 | - next = ent->next; | |
205 | - free_exclude(ent); | |
206 | + if (listp->tail) { | |
207 | + struct exclude_struct *ent, *next; | |
bc95f62b | 208 | + /* Truncate any inherited items from the local list. */ |
7cb7ae4e WD |
209 | + listp->tail->next = NULL; |
210 | + for (ent = listp->head; ent; ent = next) { | |
211 | + next = ent->next; | |
212 | + free_exclude(ent); | |
213 | + } | |
524989ae WD |
214 | } |
215 | ||
7cb7ae4e WD |
216 | listp->head = listp->tail = NULL; |
217 | } | |
a55d21aa | 218 | |
a55d21aa WD |
219 | +void *push_local_excludes(char *fname, unsigned int offset) |
220 | +{ | |
221 | + int i; | |
524989ae WD |
222 | + struct exclude_list_root *push = new_array(struct exclude_list_root, 1); |
223 | + | |
224 | + if (!push) | |
a55d21aa WD |
225 | + out_of_memory("push_local_excludes"); |
226 | + | |
524989ae | 227 | + push->cnt = local_lists.cnt; |
d1e7c1c8 WD |
228 | + push->array = new_array(struct exclude_list_struct, local_lists.cnt); |
229 | + if (!push->array) | |
524989ae | 230 | + out_of_memory("push_local_excludes"); |
a55d21aa | 231 | + |
d1e7c1c8 | 232 | + memcpy(push->array, local_lists.array, |
524989ae WD |
233 | + sizeof (struct exclude_list_struct) * local_lists.cnt); |
234 | + | |
d1e7c1c8 WD |
235 | + /* Make it easy to construct the full path for a merge-file that was |
236 | + * specified with a relative path by saving off the current dir. */ | |
a55d21aa WD |
237 | + memcpy(dirbuf, fname, offset); |
238 | + dirbuf_offset = offset; | |
239 | + | |
524989ae | 240 | + for (i = 0; i < local_lists.cnt; i++) { |
d1e7c1c8 | 241 | + struct exclude_list_struct *listp = &local_lists.array[i]; |
a55d21aa WD |
242 | + char *file = listp->parent->pattern; |
243 | + int flags; | |
524989ae WD |
244 | + |
245 | + if (verbose > 2) { | |
246 | + rprintf(FINFO, "[%s] pushing %sexclude list\n", | |
247 | + who_am_i(), listp->debug_type); | |
248 | + } | |
a55d21aa | 249 | + if (listp->parent->match_flags & MATCHFLG_CVSIGNORE) { |
bc95f62b | 250 | + listp->head = NULL; /* Subdirs don't inherit rules. */ |
ee1af13c | 251 | + flags = XFLG_WORD_SPLIT | XFLG_WORDS_ONLY; |
a55d21aa | 252 | + } else { |
524989ae WD |
253 | + flags = listp->parent->match_flags & MATCHFLG_INCLUDE |
254 | + ? XFLG_DEF_INCLUDE : 0; | |
a55d21aa | 255 | + } |
bc95f62b | 256 | + listp->tail = NULL; /* Signals no local content. */ |
a55d21aa | 257 | + if (strlcpy(fname + offset, file, MAXPATHLEN - offset) |
524989ae | 258 | + < MAXPATHLEN - offset) |
a55d21aa | 259 | + add_exclude_file(listp, fname, flags); |
524989ae | 260 | + else { |
a55d21aa WD |
261 | + io_error |= IOERR_GENERAL; |
262 | + rprintf(FINFO, | |
263 | + "cannot add local excludes in long-named directory %s\n", | |
264 | + full_fname(fname)); | |
265 | + } | |
266 | + } | |
267 | + | |
524989ae | 268 | + return (void*)push; |
a55d21aa WD |
269 | +} |
270 | + | |
271 | +void pop_local_excludes(void *mem) | |
272 | +{ | |
273 | + int i; | |
524989ae WD |
274 | + |
275 | + for (i = 0; i < local_lists.cnt; i++) { | |
d1e7c1c8 | 276 | + struct exclude_list_struct *listp = &local_lists.array[i]; |
524989ae WD |
277 | + if (verbose > 2) { |
278 | + rprintf(FINFO, "[%s] popping %sexclude list\n", | |
279 | + who_am_i(), listp->debug_type); | |
280 | + } | |
a55d21aa WD |
281 | + free_exclude_list(listp); |
282 | + } | |
d1e7c1c8 | 283 | + free(local_lists.array); |
524989ae | 284 | + local_lists = *(struct exclude_list_root*)mem; |
a55d21aa | 285 | + free(mem); |
7cb7ae4e WD |
286 | +} |
287 | + | |
a55d21aa | 288 | static int check_one_exclude(char *name, struct exclude_struct *ex, |
7cb7ae4e WD |
289 | int name_is_dir) |
290 | { | |
bc95f62b WD |
291 | @@ -121,7 +269,7 @@ static int check_one_exclude(char *name, |
292 | /* If the pattern does not have any slashes AND it does not have | |
293 | * a "**" (which could match a slash), then we just match the | |
294 | * name portion of the path. */ | |
295 | - if (!ex->slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) { | |
296 | + if (!ex->u.slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) { | |
297 | if ((p = strrchr(name,'/')) != NULL) | |
298 | name = p+1; | |
299 | } | |
d1e7c1c8 | 300 | @@ -134,7 +282,8 @@ static int check_one_exclude(char *name, |
a55d21aa WD |
301 | |
302 | if (!name[0]) return 0; | |
303 | ||
304 | - if (ex->directory && !name_is_dir) return 0; | |
305 | + if ((ex->match_flags & MATCHFLG_DIRECTORY) && !name_is_dir) | |
306 | + return 0; | |
307 | ||
308 | if (*pattern == '/') { | |
309 | match_start = 1; | |
bc95f62b WD |
310 | @@ -146,9 +295,9 @@ static int check_one_exclude(char *name, |
311 | if (ex->match_flags & MATCHFLG_WILD) { | |
312 | /* A non-anchored match with an infix slash and no "**" | |
313 | * needs to match the last slash_cnt+1 name elements. */ | |
314 | - if (!match_start && ex->slash_cnt && | |
315 | + if (!match_start && ex->u.slash_cnt && | |
316 | !(ex->match_flags & MATCHFLG_WILD2)) { | |
317 | - int cnt = ex->slash_cnt + 1; | |
318 | + int cnt = ex->u.slash_cnt + 1; | |
319 | for (p = name + strlen(name) - 1; p >= name; p--) { | |
320 | if (*p == '/' && !--cnt) | |
321 | break; | |
d1e7c1c8 | 322 | @@ -201,9 +350,11 @@ static void report_exclude_result(char c |
a55d21aa WD |
323 | |
324 | if (verbose >= 2) { | |
325 | rprintf(FINFO, "[%s] %scluding %s %s because of %spattern %s%s\n", | |
326 | - who_am_i(), ent->include ? "in" : "ex", | |
327 | + who_am_i(), | |
328 | + ent->match_flags & MATCHFLG_INCLUDE ? "in" : "ex", | |
329 | name_is_dir ? "directory" : "file", name, type, | |
330 | - ent->pattern, ent->directory ? "/" : ""); | |
331 | + ent->pattern, | |
332 | + ent->match_flags & MATCHFLG_DIRECTORY ? "/" : ""); | |
333 | } | |
334 | } | |
335 | ||
d1e7c1c8 | 336 | @@ -217,10 +368,18 @@ int check_exclude(struct exclude_list_st |
a55d21aa WD |
337 | struct exclude_struct *ent; |
338 | ||
339 | for (ent = listp->head; ent; ent = ent->next) { | |
524989ae | 340 | + if (ent->match_flags & MATCHFLG_MERGE_FILE) { |
a55d21aa | 341 | + struct exclude_list_struct *lp |
bc95f62b | 342 | + = &local_lists.array[ent->u.array_index]; |
a55d21aa WD |
343 | + int rc = check_exclude(lp, name, name_is_dir); |
344 | + if (rc) | |
345 | + return rc; | |
346 | + continue; | |
347 | + } | |
348 | if (check_one_exclude(name, ent, name_is_dir)) { | |
349 | report_exclude_result(name, ent, name_is_dir, | |
350 | listp->debug_type); | |
351 | - return ent->include ? 1 : -1; | |
352 | + return (ent->match_flags & MATCHFLG_INCLUDE) ? 1 : -1; | |
353 | } | |
354 | } | |
355 | ||
d1e7c1c8 | 356 | @@ -236,11 +395,11 @@ int check_exclude(struct exclude_list_st |
a55d21aa WD |
357 | * *incl_ptr value will be 1 for an include, 0 for an exclude, and -1 for |
358 | * the list-clearing "!" token. | |
359 | */ | |
360 | -static const char *get_exclude_tok(const char *p, int *len_ptr, int *incl_ptr, | |
bc95f62b WD |
361 | - int xflags) |
362 | +static const char *get_exclude_tok(const char *p, unsigned int *len_ptr, | |
363 | + unsigned int *flag_ptr, int xflags) | |
a55d21aa WD |
364 | { |
365 | const unsigned char *s = (const unsigned char *)p; | |
366 | - int len; | |
bc95f62b | 367 | + unsigned int len, mflags = 0; |
a55d21aa WD |
368 | |
369 | if (xflags & XFLG_WORD_SPLIT) { | |
370 | /* Skip over any initial whitespace. */ | |
d1e7c1c8 | 371 | @@ -250,13 +409,19 @@ static const char *get_exclude_tok(const |
a55d21aa WD |
372 | p = (const char *)s; |
373 | } | |
374 | ||
375 | - /* Is this a '+' or '-' followed by a space (not whitespace)? */ | |
376 | + /* Is this a +/-/. followed by a space (not whitespace)? */ | |
ee1af13c | 377 | if (!(xflags & XFLG_WORDS_ONLY) |
a55d21aa WD |
378 | - && (*s == '-' || *s == '+') && s[1] == ' ') { |
379 | - *incl_ptr = *s == '+'; | |
380 | + && (*s == '-' || *s == '+' || *s == '.') && s[1] == ' ') { | |
381 | + if (*s == '+') | |
382 | + mflags |= MATCHFLG_INCLUDE; | |
383 | + else if (*s == '.') { | |
524989ae | 384 | + mflags |= MATCHFLG_MERGE_FILE; |
a55d21aa WD |
385 | + if (xflags & XFLG_DEF_INCLUDE) |
386 | + mflags |= MATCHFLG_INCLUDE; | |
387 | + } | |
388 | s += 2; | |
389 | - } else | |
390 | - *incl_ptr = xflags & XFLG_DEF_INCLUDE; | |
391 | + } else if (xflags & XFLG_DEF_INCLUDE) | |
392 | + mflags |= MATCHFLG_INCLUDE; | |
393 | ||
394 | if (xflags & XFLG_WORD_SPLIT) { | |
395 | const unsigned char *cp = s; | |
d1e7c1c8 | 396 | @@ -268,9 +433,10 @@ static const char *get_exclude_tok(const |
a55d21aa WD |
397 | len = strlen(s); |
398 | ||
ee1af13c | 399 | if (*p == '!' && len == 1 && !(xflags & XFLG_WORDS_ONLY)) |
a55d21aa WD |
400 | - *incl_ptr = -1; |
401 | + mflags |= MATCHFLG_CLEAR_LIST; | |
402 | ||
403 | *len_ptr = len; | |
404 | + *flag_ptr = mflags; | |
405 | return (const char *)s; | |
406 | } | |
407 | ||
d1e7c1c8 | 408 | @@ -278,7 +444,7 @@ static const char *get_exclude_tok(const |
a55d21aa WD |
409 | void add_exclude(struct exclude_list_struct *listp, const char *pattern, |
410 | int xflags) | |
411 | { | |
412 | - int pat_len, incl; | |
bc95f62b | 413 | + unsigned int pat_len, mflags; |
a55d21aa WD |
414 | const char *cp; |
415 | ||
416 | if (!pattern) | |
bc95f62b | 417 | @@ -287,27 +453,56 @@ void add_exclude(struct exclude_list_str |
a55d21aa WD |
418 | cp = pattern; |
419 | pat_len = 0; | |
420 | while (1) { | |
421 | - cp = get_exclude_tok(cp + pat_len, &pat_len, &incl, xflags); | |
422 | + cp = get_exclude_tok(cp + pat_len, &pat_len, &mflags, xflags); | |
423 | if (!pat_len) | |
424 | break; | |
524989ae | 425 | - /* If we got the special "!" token, clear the list. */ |
7cb7ae4e | 426 | - if (incl < 0) { |
524989ae WD |
427 | + if (mflags & MATCHFLG_CLEAR_LIST) { |
428 | if (verbose > 2) { | |
7cb7ae4e WD |
429 | rprintf(FINFO, |
430 | "[%s] clearing %sexclude list\n", | |
431 | who_am_i(), listp->debug_type); | |
432 | } | |
433 | free_exclude_list(listp); | |
434 | - } else { | |
435 | - make_exclude(listp, cp, pat_len, incl); | |
436 | - | |
437 | - if (verbose > 2) { | |
a55d21aa WD |
438 | - rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s)\n", |
439 | - who_am_i(), pat_len, cp, | |
440 | - listp->debug_type, | |
441 | - incl ? "include" : "exclude"); | |
442 | + continue; | |
443 | + } | |
524989ae | 444 | + if (mflags & MATCHFLG_MERGE_FILE) { |
a55d21aa | 445 | + char name[MAXPATHLEN]; |
bc95f62b WD |
446 | + if (pat_len >= sizeof name) { |
447 | + rprintf(FERROR, | |
448 | + "merge filename too long: %s\n", cp); | |
449 | + continue; | |
450 | + } | |
a55d21aa | 451 | + strlcpy(name, cp, pat_len+1); |
524989ae | 452 | + if (strchr(name, '/') != NULL) { |
a55d21aa WD |
453 | + if (sanitize_paths) |
454 | + sanitize_path(name, curr_dir); | |
455 | + if (*name == '/') | |
456 | + cp = name; | |
457 | + else { | |
458 | + if (strlcpy(dirbuf + dirbuf_offset, | |
459 | + name, MAXPATHLEN - dirbuf_offset) | |
bc95f62b WD |
460 | + >= MAXPATHLEN - dirbuf_offset) { |
461 | + rprintf(FERROR, | |
462 | + "merge filename too long: %s...\n", | |
463 | + dirbuf); | |
464 | + continue; | |
465 | + } | |
a55d21aa WD |
466 | + cp = dirbuf; |
467 | + } | |
468 | + add_exclude_file(listp, cp, | |
469 | + xflags | XFLG_FATAL_ERRORS); | |
470 | + continue; | |
471 | } | |
472 | } | |
524989ae | 473 | + |
a55d21aa WD |
474 | + make_exclude(listp, cp, pat_len, mflags); |
475 | + | |
476 | + if (verbose > 2) { | |
477 | + rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s%sclude)\n", | |
478 | + who_am_i(), pat_len, cp, listp->debug_type, | |
524989ae | 479 | + mflags & MATCHFLG_MERGE_FILE ? "FILE " : "", |
a55d21aa WD |
480 | + mflags & MATCHFLG_INCLUDE ? "in" : "ex"); |
481 | + } | |
482 | } | |
483 | } | |
484 | ||
bc95f62b | 485 | @@ -389,15 +584,19 @@ void send_exclude_list(int f) |
a55d21aa WD |
486 | l = strlcpy(p, ent->pattern, sizeof p); |
487 | if (l == 0 || l >= MAXPATHLEN) | |
488 | continue; | |
489 | - if (ent->directory) { | |
490 | + if (ent->match_flags & MATCHFLG_DIRECTORY) { | |
491 | p[l++] = '/'; | |
492 | p[l] = '\0'; | |
493 | } | |
494 | ||
495 | - if (ent->include) { | |
496 | + if (ent->match_flags & MATCHFLG_INCLUDE) { | |
497 | write_int(f, l + 2); | |
498 | write_buf(f, "+ ", 2); | |
499 | - } else if ((*p == '-' || *p == '+') && p[1] == ' ') { | |
524989ae | 500 | + } else if (ent->match_flags & MATCHFLG_MERGE_FILE) { |
a55d21aa WD |
501 | + write_int(f, l + 2); |
502 | + write_buf(f, ". ", 2); | |
503 | + } else if ((*p == '-' || *p == '+' || *p == '.') | |
504 | + && p[1] == ' ') { | |
505 | write_int(f, l + 2); | |
506 | write_buf(f, "- ", 2); | |
507 | } else | |
bc95f62b | 508 | @@ -438,6 +637,7 @@ void add_cvs_excludes(void) |
a55d21aa WD |
509 | char fname[MAXPATHLEN]; |
510 | char *p; | |
511 | ||
512 | + add_exclude(&exclude_list, ". .cvsignore", 0); | |
513 | add_exclude(&exclude_list, default_cvsignore, | |
ee1af13c | 514 | XFLG_WORD_SPLIT | XFLG_WORDS_ONLY); |
a55d21aa | 515 | |
bc95f62b WD |
516 | --- flist.c 15 May 2004 19:31:10 -0000 1.223 |
517 | +++ flist.c 15 May 2004 20:03:31 -0000 | |
a55d21aa WD |
518 | @@ -39,8 +39,6 @@ extern int module_id; |
519 | extern int ignore_errors; | |
520 | extern int numeric_ids; | |
521 | ||
522 | -extern int cvs_exclude; | |
523 | - | |
524 | extern int recurse; | |
525 | extern char curr_dir[MAXPATHLEN]; | |
526 | extern char *files_from; | |
7cb7ae4e | 527 | @@ -65,7 +63,6 @@ extern int write_batch; |
a55d21aa WD |
528 | |
529 | extern struct exclude_list_struct exclude_list; | |
530 | extern struct exclude_list_struct server_exclude_list; | |
531 | -extern struct exclude_list_struct local_exclude_list; | |
532 | ||
533 | int io_error; | |
534 | ||
7cb7ae4e | 535 | @@ -210,8 +207,6 @@ int link_stat(const char *path, STRUCT_S |
a55d21aa WD |
536 | */ |
537 | static int check_exclude_file(char *fname, int is_dir, int exclude_level) | |
538 | { | |
539 | - int rc; | |
540 | - | |
541 | #if 0 /* This currently never happens, so avoid a useless compare. */ | |
542 | if (exclude_level == NO_EXCLUDES) | |
543 | return 0; | |
7cb7ae4e | 544 | @@ -233,10 +228,7 @@ static int check_exclude_file(char *fnam |
a55d21aa WD |
545 | if (exclude_level != ALL_EXCLUDES) |
546 | return 0; | |
547 | if (exclude_list.head | |
548 | - && (rc = check_exclude(&exclude_list, fname, is_dir)) != 0) | |
549 | - return rc < 0; | |
550 | - if (local_exclude_list.head | |
551 | - && check_exclude(&local_exclude_list, fname, is_dir) < 0) | |
552 | + && check_exclude(&exclude_list, fname, is_dir) < 0) | |
553 | return 1; | |
554 | return 0; | |
555 | } | |
7cb7ae4e | 556 | @@ -946,15 +938,7 @@ void send_file_name(int f, struct file_l |
a55d21aa WD |
557 | |
558 | if (recursive && S_ISDIR(file->mode) | |
559 | && !(file->flags & FLAG_MOUNT_POINT)) { | |
560 | - struct exclude_list_struct last_list = local_exclude_list; | |
561 | - local_exclude_list.head = local_exclude_list.tail = NULL; | |
562 | send_directory(f, flist, f_name_to(file, fbuf)); | |
7cb7ae4e WD |
563 | - if (verbose > 2) { |
564 | - rprintf(FINFO, "[%s] popping %sexclude list\n", | |
565 | - who_am_i(), local_exclude_list.debug_type); | |
566 | - } | |
a55d21aa WD |
567 | - free_exclude_list(&local_exclude_list); |
568 | - local_exclude_list = last_list; | |
569 | } | |
570 | } | |
571 | ||
7cb7ae4e | 572 | @@ -965,6 +949,7 @@ static void send_directory(int f, struct |
a55d21aa WD |
573 | struct dirent *di; |
574 | char fname[MAXPATHLEN]; | |
575 | unsigned int offset; | |
576 | + void *save_excludes; | |
577 | char *p; | |
578 | ||
579 | d = opendir(dir); | |
bc95f62b | 580 | @@ -988,18 +973,7 @@ static void send_directory(int f, struct |
a55d21aa WD |
581 | offset++; |
582 | } | |
583 | ||
584 | - if (cvs_exclude) { | |
585 | - if (strlcpy(p, ".cvsignore", MAXPATHLEN - offset) | |
586 | - < MAXPATHLEN - offset) { | |
587 | - add_exclude_file(&local_exclude_list, fname, | |
ee1af13c | 588 | - XFLG_WORD_SPLIT | XFLG_WORDS_ONLY); |
a55d21aa WD |
589 | - } else { |
590 | - io_error |= IOERR_GENERAL; | |
591 | - rprintf(FINFO, | |
592 | - "cannot cvs-exclude in long-named directory %s\n", | |
593 | - full_fname(fname)); | |
594 | - } | |
595 | - } | |
596 | + save_excludes = push_local_excludes(fname, offset); | |
597 | ||
598 | for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) { | |
599 | char *dname = d_name(di); | |
bc95f62b WD |
600 | @@ -1019,6 +993,8 @@ static void send_directory(int f, struct |
601 | io_error |= IOERR_GENERAL; | |
602 | rsyserr(FERROR, errno, "readdir(%s)", dir); | |
a55d21aa WD |
603 | } |
604 | + | |
605 | + pop_local_excludes(save_excludes); | |
606 | ||
607 | closedir(d); | |
608 | } | |
7cb7ae4e | 609 | --- rsync.h 13 May 2004 18:51:22 -0000 1.203 |
bc95f62b WD |
610 | +++ rsync.h 15 May 2004 20:03:31 -0000 |
611 | @@ -493,18 +493,24 @@ struct map_struct { | |
a55d21aa WD |
612 | #define MATCHFLG_WILD2 (1<<1) /* pattern has '**' */ |
613 | #define MATCHFLG_WILD2_PREFIX (1<<2) /* pattern starts with '**' */ | |
614 | #define MATCHFLG_ABS_PATH (1<<3) /* path-match on absolute path */ | |
615 | +#define MATCHFLG_INCLUDE (1<<4) /* this is an include, not an exclude */ | |
616 | +#define MATCHFLG_CLEAR_LIST (1<<5) /* this item is the "!" token */ | |
617 | +#define MATCHFLG_DIRECTORY (1<<6) /* this matches only directories */ | |
524989ae | 618 | +#define MATCHFLG_MERGE_FILE (1<<7) /* specifies a file to merge */ |
a55d21aa WD |
619 | +#define MATCHFLG_CVSIGNORE (1<<8) /* parse this as a .cvsignore file */ |
620 | struct exclude_struct { | |
621 | struct exclude_struct *next; | |
622 | char *pattern; | |
bc95f62b | 623 | - int match_flags; |
a55d21aa WD |
624 | - int include; |
625 | - int directory; | |
bc95f62b WD |
626 | - int slash_cnt; |
627 | + unsigned int match_flags; | |
628 | + union { | |
629 | + int slash_cnt; | |
630 | + int array_index; | |
631 | + } u; | |
a55d21aa WD |
632 | }; |
633 | ||
634 | struct exclude_list_struct { | |
635 | - struct exclude_struct *head; | |
636 | - struct exclude_struct *tail; | |
637 | + struct exclude_struct *head, *tail; | |
7cb7ae4e | 638 | + struct exclude_struct *parent; |
a55d21aa WD |
639 | char *debug_type; |
640 | }; | |
641 | ||
ea238f1c | 642 | --- rsync.yo 7 May 2004 00:18:37 -0000 1.169 |
bc95f62b | 643 | +++ rsync.yo 15 May 2004 20:03:32 -0000 |
ea238f1c | 644 | @@ -1075,6 +1075,72 @@ itemize( |
524989ae WD |
645 | it would be excluded by the "*") |
646 | ) | |
647 | ||
648 | +manpagesection(MERGING EXCLUDE FILES) | |
649 | + | |
650 | +You can merge whole files into an exclude file using a rule that starts | |
651 | +with a ". " (a dot followed by a space) and has a filename in place of the | |
652 | +pattern. There are two types of merge rules, single-instance and | |
653 | +per-directory: | |
654 | + | |
655 | +itemize( | |
656 | + it() If the filename has no slashes in it, it is a per-directory merge; | |
bc95f62b WD |
657 | + rsync scans every directory that it traverses for the named file, merging |
658 | + its contents (when it exists) file at the start of this per-directory | |
659 | + sub-list (subdirectories inherit the contents of their parent directories | |
660 | + by default, and each subdirectory's rules have precedence over the parent | |
661 | + directory's rules). | |
524989ae WD |
662 | + |
663 | + it() If a filename has a slash in it, it is a single-instance merge; the | |
664 | + named file's contents will be merged into the current exclude file, | |
665 | + replacing the merge rule. Thus, you should use the name ./foo instead of | |
666 | + foo if you don't want to scan for "foo" in all the subdirectories of the | |
667 | + current directory. | |
668 | +) | |
669 | + | |
670 | +Note also that you can eliminate all the inherited rules for the current | |
671 | +per-directory ruleset by putting the list-clearing token (!) in the file. | |
bc95f62b WD |
672 | +This only clears the rules for the current per-directory sub-list (up |
673 | +through the ! token) and only for the current directory and its | |
524989ae WD |
674 | +subdirectories. |
675 | + | |
bc95f62b WD |
676 | +Here's an example exclude file (which you'd specify via the normal |
677 | +--exclude-from option): | |
524989ae WD |
678 | + |
679 | +verb( | |
680 | + . /home/user/.global_excludes | |
681 | + - *.gz | |
682 | + . .excl | |
683 | + + *.[ch] | |
684 | + - *.o | |
685 | +) | |
686 | + | |
687 | +This will merge the contents of the /home/user/.global_excludes file at the | |
688 | +start of the list and also turns the ".excl" filename into a per-directory | |
689 | +exclude file whose local contents will be merged into the list in place of | |
690 | +the .excl line. | |
691 | + | |
692 | +Additionally, you can affect where the --cvs-exclude (-C) option's | |
693 | +inclusion of a per-directory .cvsignore file gets placed into your rules by | |
bc95f62b | 694 | +adding an explicit merge rule for ".cvsignore". For instance, specifying |
524989ae WD |
695 | +this: |
696 | + | |
697 | +verb( | |
698 | + rsync -avC --exclude='. .cvsignore' --exclude-from=foo a/ b | |
699 | +) | |
700 | + | |
701 | +will merge all the per-directory .cvsignore rules at the start of your list | |
702 | +rather than at the end. This allows their dir-specific rules to supersede | |
703 | +your rules instead of being subservient to them. (The global rules taken | |
704 | +from the $HOME/.cvsignore file and from $CVSIGNORE are not affected by | |
705 | +this.) | |
706 | + | |
707 | +Note also that the parsing of any merge-file named ".cvsignore" is always | |
708 | +done in a CVS-compatible manner (even if -C wasn't specified) -- i.e. the | |
709 | +rules are always exclude rules (even when specified by an include option), | |
710 | +they are split on whitespace, no special prefixes or list-clearing tokens | |
711 | +are honored, and (for per-directory files) subdirectories don't inherit the | |
712 | +parent directory's rules. | |
713 | + | |
714 | manpagesection(BATCH MODE) | |
715 | ||
716 | bf(Note:) Batch mode should be considered experimental in this version |