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