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