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