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