- Changed the way that daemon excludes get anchored to the module dir
[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 6This patch adds the ability to merge rules into your excludes/includes
d34d9fad
WD
7using a ". FILE" idiom. If you specify a name with a preceding -p
8option, that filename will be looked for in every subdirectory that
9rsync visits, and the rules found in that subdirectory's file will
40e20a69 10affect that dir and its subdirs.
a55d21aa
WD
11
12For example:
13
40e20a69 14 rsync -av --exclude='. -p .excl' from/ to
a55d21aa
WD
15
16The above will look for a file named ".excl" in every directory of the
524989ae
WD
17hierarchy that rsync visits, and it will exclude (by default) names
18based on the rules found therein. If one of the .excl files contains
19this:
a55d21aa
WD
20
21 + *.c
d34d9fad 22 . -p .excl2
db4f43dd 23 . .excl3
524989ae 24 *.o
a55d21aa 25
d34d9fad 26Then the file ".excl2" will also be read in from the current dir and all
40e20a69
WD
27its subdirs (due to the -p option). The file ".excl3" would just be
28read in from the current dir (because it was not specified with the -p
29option).
a55d21aa
WD
30
31..wayne..
32
40e20a69
WD
33--- orig/clientserver.c 2004-08-02 02:29:16
34+++ clientserver.c 2004-08-09 02:25:28
35@@ -48,9 +48,10 @@ extern int no_detach;
36 extern int default_af_hint;
37 extern char *bind_address;
38 extern struct exclude_list_struct server_exclude_list;
39-extern char *exclude_path_prefix;
40 extern char *config_file;
41 extern char *files_from;
42+extern char dirbuf[MAXPATHLEN];
43+extern unsigned int dirbuf_offset;
44
45 char *auth_user;
46
47@@ -300,26 +301,27 @@ static int rsync_module(int f_in, int f_
48 /* TODO: Perhaps take a list of gids, and make them into the
49 * supplementary groups. */
50
51- exclude_path_prefix = use_chroot? "" : lp_path(i);
52- if (*exclude_path_prefix == '/' && !exclude_path_prefix[1])
53- exclude_path_prefix = "";
54+ if (use_chroot)
55+ set_current_subdir("", 0);
56+ else
57+ set_current_subdir(lp_path(i), strlen(lp_path(i)));
58
59 p = lp_include_from(i);
60 add_exclude_file(&server_exclude_list, p,
61- XFLG_FATAL_ERRORS | XFLG_DEF_INCLUDE);
62+ XFLG_FATAL_ERRORS | XFLG_DEF_INCLUDE | XFLG_ABS_PATH);
63
64 p = lp_include(i);
65 add_exclude(&server_exclude_list, p,
66- XFLG_WORD_SPLIT | XFLG_DEF_INCLUDE);
67+ XFLG_WORD_SPLIT | XFLG_DEF_INCLUDE | XFLG_ABS_PATH);
68
69 p = lp_exclude_from(i);
70 add_exclude_file(&server_exclude_list, p,
71- XFLG_FATAL_ERRORS);
72+ XFLG_FATAL_ERRORS | XFLG_ABS_PATH);
73
74 p = lp_exclude(i);
75- add_exclude(&server_exclude_list, p, XFLG_WORD_SPLIT);
76+ add_exclude(&server_exclude_list, p, XFLG_WORD_SPLIT | XFLG_ABS_PATH);
77
78- exclude_path_prefix = NULL;
79+ set_current_subdir("", 0);
80
81 log_init();
82
d34d9fad 83--- orig/exclude.c 2004-08-05 23:16:37
40e20a69
WD
84+++ exclude.c 2004-08-09 03:19:40
85@@ -27,16 +27,80 @@
045caa90
WD
86 #include "rsync.h"
87
88 extern int verbose;
89+extern int am_sender;
a55d21aa
WD
90 extern int eol_nulls;
91 extern int list_only;
92 extern int recurse;
93+extern int io_error;
56babefa 94+extern int module_id;
045caa90
WD
95+extern int delete_mode;
96+extern int delete_excluded;
56babefa 97+extern int sanitize_paths;
a55d21aa
WD
98
99 extern char curr_dir[];
56babefa 100+extern unsigned int curr_dir_len;
a55d21aa 101
d8af8661 102 struct exclude_list_struct exclude_list = { 0, 0, "" };
524989ae 103-struct exclude_list_struct local_exclude_list = { 0, 0, "per-dir .cvsignore " };
d8af8661 104 struct exclude_list_struct server_exclude_list = { 0, 0, "server " };
40e20a69
WD
105-char *exclude_path_prefix = NULL;
106+
eabda998 107+struct mergelist_save_struct {
d1e7c1c8 108+ struct exclude_list_struct *array;
d8af8661
WD
109+ int count;
110+};
524989ae 111+
db4f43dd
WD
112+/* The dirbuf is set by push_local_excludes() to the current subdirectory
113+ * relative to curr_dir that is being processed. The path always has a
114+ * trailing slash appended, and the variable dirbuf_offset contains the
115+ * length of this path prefix (i.e. it is an offset where you can copy a
116+ * filename to create a pathname that can be opened for reading/writing.
117+ *
118+ * The path is normally relative (e.g. "sub/dir/foo"), but it is set to an
119+ * absolute path when the push code is working on a parent-dir scan of dirs
120+ * that might be higher than the root of the transfer. In that case, the
121+ * path is absolute, and any newly-created per-dir merge files will (at
122+ * least temporarily) get an absolute file name so that we know at what
123+ * point in the hierarchy it first makes an appearance. */
a55d21aa
WD
124+static char dirbuf[MAXPATHLEN];
125+static unsigned int dirbuf_offset = 0;
40e20a69 126+static BOOL parent_dirscan = 0;
db4f43dd
WD
127+
128+/* This array contains a list of all the currently active per-dir merge
129+ * files. This makes it easier to save the appropriate values when we
130+ * "push" down into each subdirectory. */
d34d9fad
WD
131+static struct exclude_struct **mergelist_parents;
132+static int mergelist_cnt = 0;
133+static int mergelist_size = 0;
a55d21aa 134+
d1e7c1c8
WD
135+/* Each exclude_list_struct describes a singly-linked list by keeping track
136+ * of both the head and tail pointers. The list is slightly unusual in that
137+ * a parent-dir's content can be appended to the end of the local list in a
138+ * special way: the last item in the local list has its "next" pointer set
0c7d1fd8 139+ * to point to the inherited list, but the local list's tail pointer points
d1e7c1c8 140+ * at the end of the local list. Thus, if the local list is empty, the head
0c7d1fd8 141+ * will be pointing at the inherited content but the tail will be NULL. To
d1e7c1c8
WD
142+ * help you visualize this, here are the possible list arrangements:
143+ *
144+ * Completely Empty Local Content Only
145+ * ================================== ====================================
146+ * head -> NULL head -> Local1 -> Local2 -> NULL
147+ * tail -> NULL tail -------------^
148+ *
149+ * Inherited Content Only Both Local and Inherited Content
150+ * ================================== ====================================
151+ * head -> Parent1 -> Parent2 -> NULL head -> L1 -> L2 -> P1 -> P2 -> NULL
152+ * tail -> NULL tail ---------^
153+ *
154+ * This means that anyone wanting to traverse the whole list to USE it just
155+ * needs to start at the head and use the "next" pointers until it goes
156+ * NULL. To add new local content, we insert the item after the tail item
157+ * and update the tail (obviously, if "tail" was NULL, we insert it at the
158+ * head). To clear the local list, WE MUST NOT FREE THE INHERITED CONTENT
bc95f62b
WD
159+ * because it is shared between the current list and our parent list(s).
160+ * The easiest way to handle this is to simply truncate the list after the
161+ * tail item and then free the local list from the head. When inheriting
162+ * the list for a new local dir, we just save off the exclude_list_struct
0c7d1fd8 163+ * values (so we can pop back to them later) and set the tail to NULL.
d1e7c1c8 164+ */
40e20a69 165
0c7d1fd8
WD
166 /** Build an exclude structure given an exclude pattern. */
167 static void make_exclude(struct exclude_list_struct *listp, const char *pat,
40e20a69 168@@ -46,23 +110,56 @@ static void make_exclude(struct exclude_
a55d21aa 169 const char *cp;
0c7d1fd8
WD
170 unsigned int ex_len;
171
d34d9fad
WD
172+ if (verbose > 2) {
173+ rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s%sclude)\n",
174+ who_am_i(), (int)pat_len, pat, listp->debug_type,
175+ mflags & MATCHFLG_MERGE_FILE ? "FILE " : "",
176+ mflags & MATCHFLG_INCLUDE ? "in" : "ex");
177+ }
178+
524989ae 179+ if (mflags & MATCHFLG_MERGE_FILE) {
eabda998 180+ int i;
a55d21aa 181+ /* If the local include file was already mentioned, don't
524989ae 182+ * add it again. */
eabda998
WD
183+ for (i = 0; i < mergelist_cnt; i++) {
184+ struct exclude_struct *ex = mergelist_parents[i];
185+ if (strlen(ex->pattern) == pat_len
d8af8661 186+ && memcmp(ex->pattern, pat, pat_len) == 0)
a55d21aa
WD
187+ return;
188+ }
524989ae
WD
189+ if ((pat_len == 10 || (pat_len > 10 && pat[pat_len-11] == '/'))
190+ && strncmp(pat+pat_len-10, ".cvsignore", 10) == 0) {
a55d21aa 191+ mflags |= MATCHFLG_CVSIGNORE;
40e20a69 192+ mflags &= ~MATCHFLG_INCLUDE;
a55d21aa
WD
193+ } else
194+ mflags &= ~MATCHFLG_CVSIGNORE;
195+ }
0c7d1fd8 196+
a55d21aa
WD
197 ret = new(struct exclude_struct);
198 if (!ret)
199 out_of_memory("make_exclude");
40e20a69
WD
200
201 memset(ret, 0, sizeof ret[0]);
202
203- if (exclude_path_prefix)
204- mflags |= MATCHFLG_ABS_PATH;
205- if (exclude_path_prefix && *pat == '/')
206- ex_len = strlen(exclude_path_prefix);
207- else
208+ if (mflags & MATCHFLG_ABS_PATH) {
209+ if (*pat != '/') {
210+ mflags &= ~MATCHFLG_ABS_PATH;
211+ ex_len = 0;
212+ } else {
213+ ex_len = *dirbuf == '/' ? dirbuf_offset - 1
214+ : curr_dir_len + dirbuf_offset - !curr_dir[1];
215+ }
216+ } else
217 ex_len = 0;
218 ret->pattern = new_array(char, ex_len + pat_len + 1);
219 if (!ret->pattern)
220 out_of_memory("make_exclude");
221- if (ex_len)
222- memcpy(ret->pattern, exclude_path_prefix, ex_len);
223+ if (ex_len) {
224+ if (*dirbuf == '/')
225+ strlcpy(ret->pattern, dirbuf, ex_len + 1);
226+ else
227+ pathjoin(ret->pattern, ex_len + 1, curr_dir, dirbuf);
228+ }
229 strlcpy(ret->pattern + ex_len, pat, pat_len + 1);
230 pat_len += ex_len;
231
232@@ -81,14 +178,40 @@ static void make_exclude(struct exclude_
7b22909b 233 mflags |= MATCHFLG_DIRECTORY;
a55d21aa
WD
234 }
235
7b22909b 236- for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++)
bc95f62b 237- ret->slash_cnt++;
524989ae 238+ if (mflags & MATCHFLG_MERGE_FILE) {
d8af8661
WD
239+ struct exclude_list_struct *lp
240+ = new_array(struct exclude_list_struct, 1);
241+ if (!lp)
a55d21aa 242+ out_of_memory("make_exclude");
7cb7ae4e 243+ lp->head = lp->tail = NULL;
d34d9fad
WD
244+ if ((cp = strrchr(ret->pattern, '/')) != NULL)
245+ cp++;
246+ else
247+ cp = ret->pattern;
248+ if (asprintf(&lp->debug_type, "per-dir %s ", cp) < 0)
a55d21aa 249+ out_of_memory("make_exclude");
eabda998
WD
250+ ret->u.mergelist = lp;
251+ if (mergelist_cnt == mergelist_size) {
d34d9fad
WD
252+ mergelist_size += 5;
253+ mergelist_parents = realloc_array(mergelist_parents,
254+ struct exclude_struct *,
255+ mergelist_size);
256+ if (!mergelist_parents)
257+ out_of_memory("make_exclude");
eabda998
WD
258+ }
259+ mergelist_parents[mergelist_cnt++] = ret;
7b22909b
WD
260+ } else {
261+ for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++)
262+ ret->u.slash_cnt++;
abe86b1f 263+ }
7b22909b 264
0c7d1fd8 265 ret->match_flags = mflags;
a55d21aa 266
7b22909b
WD
267- if (!listp->tail)
268+ if (!listp->tail) {
269+ ret->next = listp->head;
270 listp->head = listp->tail = ret;
271- else {
272+ } else {
273+ ret->next = listp->tail->next;
274 listp->tail->next = ret;
275 listp->tail = ret;
276 }
40e20a69 277@@ -96,22 +219,265 @@ static void make_exclude(struct exclude_
d8af8661
WD
278
279 static void free_exclude(struct exclude_struct *ex)
280 {
281+ if (ex->match_flags & MATCHFLG_MERGE_FILE) {
eabda998
WD
282+ free(ex->u.mergelist->debug_type);
283+ free(ex->u.mergelist);
d8af8661
WD
284+ }
285 free(ex->pattern);
a55d21aa
WD
286 free(ex);
287 }
288
d8af8661
WD
289-void clear_exclude_list(struct exclude_list_struct *listp)
290+static void clear_exclude_list(struct exclude_list_struct *listp)
a55d21aa 291 {
7cb7ae4e
WD
292- struct exclude_struct *ent, *next;
293-
294- for (ent = listp->head; ent; ent = next) {
295- next = ent->next;
296- free_exclude(ent);
297+ if (listp->tail) {
298+ struct exclude_struct *ent, *next;
bc95f62b 299+ /* Truncate any inherited items from the local list. */
7cb7ae4e 300+ listp->tail->next = NULL;
dc5cce3c 301+ /* Now free everything that is left. */
7cb7ae4e
WD
302+ for (ent = listp->head; ent; ent = next) {
303+ next = ent->next;
304+ free_exclude(ent);
305+ }
524989ae
WD
306 }
307
7cb7ae4e
WD
308 listp->head = listp->tail = NULL;
309 }
a55d21aa 310
db4f43dd 311+/* This returns an expanded (absolute) filename for the merge-file name if
40e20a69 312+ * the name has any slashes in it OR if the parent_dirscan var is non-zero;
db4f43dd
WD
313+ * otherwise it returns the original merge_file name. If the len_ptr value
314+ * is non-NULL the merge_file name is not null terminated and the length
315+ * value is contained therein (and will be updated with the new length). We
316+ * always return a name that is null terminated. */
41ebea83
WD
317+static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr)
318+{
319+ static char buf[MAXPATHLEN];
320+ char *fn, tmpbuf[MAXPATHLEN];
321+ unsigned int fn_len, cd_len;
322+
db4f43dd 323+ cd_len = *dirbuf == '/' ? 0 : curr_dir_len + 1;
40e20a69 324+ if (!parent_dirscan && *merge_file != '/') {
db4f43dd 325+ /* Return the name unchanged it doesn't have any slashes. */
41ebea83 326+ if (len_ptr) {
db4f43dd
WD
327+ const char *p = merge_file + *len_ptr;
328+ while (--p > merge_file && *p != '/') {}
329+ if (p == merge_file) {
330+ strlcpy(buf, merge_file, *len_ptr + 1);
331+ return buf;
332+ }
333+ } else if (strchr(merge_file, '/') == NULL)
41ebea83
WD
334+ return (char *)merge_file;
335+ }
336+
337+ fn = *merge_file == '/' ? buf : tmpbuf;
338+ if (sanitize_paths) {
339+ /* null-terminate the name if it isn't already */
340+ if (len_ptr && merge_file[*len_ptr]) {
341+ char *to = fn == buf ? tmpbuf : buf;
342+ strlcpy(to, merge_file, *len_ptr + 1);
343+ merge_file = to;
344+ }
41ebea83
WD
345+ if (!sanitize_path(fn, merge_file, dirbuf)) {
346+ rprintf(FERROR, "merge filename overflows: %s\n",
347+ merge_file);
348+ return NULL;
349+ }
350+ } else {
351+ strlcpy(fn, merge_file, len_ptr ? *len_ptr + 1 : MAXPATHLEN);
352+ clean_fname(fn);
353+ }
354+
355+ if (*fn == '/')
356+ return fn;
357+
358+ fn_len = strlen(fn);
359+ if (cd_len + dirbuf_offset + fn_len >= MAXPATHLEN) {
360+ rprintf(FERROR, "merge filename overflows: %s\n", fn);
361+ return NULL;
362+ }
363+ if (cd_len) {
364+ memcpy(buf, curr_dir, curr_dir_len);
365+ buf[curr_dir_len] = '/';
366+ }
367+ if (dirbuf_offset)
368+ memcpy(buf + cd_len, dirbuf, dirbuf_offset);
369+ memcpy(buf + cd_len + dirbuf_offset, fn, fn_len + 1);
370+ fn_len = clean_fname(buf);
371+ if (len_ptr)
372+ *len_ptr = fn_len;
373+
374+ return buf;
375+}
376+
40e20a69
WD
377+/* Sets the dirbuf and dirbuf_offset values */
378+void set_current_subdir(const char *dir, unsigned int dirlen)
379+{
380+ memcpy(dirbuf, dir, dirlen);
381+ dirbuf[dirlen] = '\0';
382+ dirbuf_offset = clean_fname(dirbuf);
383+ if (dirbuf_offset == 1 && *dirbuf == '.')
384+ dirbuf_offset = 0;
385+ else
386+ dirbuf[dirbuf_offset++] = '/';
387+ dirbuf[dirbuf_offset] = '\0';
388+}
389+
db4f43dd
WD
390+/* This routine takes a per-dir merge file entry and finishes its setup.
391+ * If the name has a path portion then we check to see if it refers to a
392+ * parent directory of the first transfer dir. If it does, we scan all the
393+ * dirs from that point through the parent dir of the transfer dir looking
394+ * for the per-dir merge file in each one. */
40e20a69
WD
395+static void setup_merge_file(struct exclude_struct *ex,
396+ struct exclude_list_struct *lp, int flags,
397+ const char *dir, unsigned int dirlen)
1e476835 398+{
d34d9fad 399+ char buf[MAXPATHLEN];
db4f43dd
WD
400+ char *x, *y, *pat = ex->pattern;
401+ unsigned int len;
56babefa 402+
db4f43dd 403+ if (!(x = parse_merge_name(pat, NULL)) || *x != '/')
1e476835
WD
404+ return;
405+
db4f43dd
WD
406+ y = strrchr(x, '/');
407+ *y = '\0';
408+ ex->pattern = strdup(y+1);
40e20a69
WD
409+ if (*dirbuf != '/') {
410+ pathjoin(dirbuf, MAXPATHLEN, sanitize_paths ? "/" : curr_dir,
411+ dir);
412+ }
db4f43dd
WD
413+ if (!*x)
414+ x = "/";
415+ if (*x == '/')
416+ strlcpy(buf, x, MAXPATHLEN);
d34d9fad 417+ else
db4f43dd 418+ pathjoin(buf, MAXPATHLEN, dirbuf, x);
d34d9fad 419+
db4f43dd
WD
420+ len = clean_fname(buf);
421+ if (len != 1 && len < MAXPATHLEN-1) {
422+ buf[len++] = '/';
423+ buf[len] = '\0';
424+ }
51fbb429
WD
425+ x = buf;
426+ if (sanitize_paths)
427+ x += strlen(lp_path(module_id));
db4f43dd 428+ /* This ensures that the specified dir is a parent of the transfer. */
51fbb429 429+ for (y = dirbuf; *x && *x == *y; x++, y++) {}
d34d9fad 430+ if (*x)
db4f43dd 431+ y += strlen(y); /* nope -- skip the scan */
d34d9fad 432+
40e20a69 433+ parent_dirscan = 1;
d34d9fad
WD
434+ while (*y) {
435+ char save[MAXPATHLEN];
41ebea83 436+ strlcpy(save, y, MAXPATHLEN);
db4f43dd 437+ *y = '\0';
d34d9fad 438+ dirbuf_offset = y - dirbuf;
db4f43dd 439+ strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf));
40e20a69
WD
440+ add_exclude_file(lp, buf, flags | XFLG_ABS_PATH);
441+ if (ex->match_flags & MATCHFLG_CVSIGNORE)
442+ lp->head = NULL; /* CVS doesn't inherit rules. */
d34d9fad 443+ lp->tail = NULL;
41ebea83 444+ strlcpy(y, save, MAXPATHLEN);
d34d9fad 445+ while ((*x++ = *y++) != '/') {}
1e476835 446+ }
40e20a69 447+ parent_dirscan = 0;
db4f43dd 448+ free(pat);
40e20a69 449+ set_current_subdir(dir, dirlen);
1e476835
WD
450+}
451+
db4f43dd
WD
452+/* Each time rsync changes to a new directory it call this function to
453+ * handle all the per-dir merge files. The "dir" value is the current path
454+ * relative to curr_dir (with a mandatory trailing slash). We copy it into
455+ * dirbuf so that the routines this calls can append a file name. */
40e20a69 456+void *push_local_excludes(const char *dir, unsigned int dirlen)
a55d21aa 457+{
eabda998
WD
458+ struct mergelist_save_struct *push;
459+ struct exclude_list_struct *ap;
460+ int i;
461+
40e20a69 462+ set_current_subdir(dir, dirlen);
eabda998
WD
463+
464+ if (!(push = new_array(struct mergelist_save_struct, 1)))
465+ out_of_memory("push_local_excludes");
466+
467+ push->count = mergelist_cnt;
468+ push->array = new_array(struct exclude_list_struct, mergelist_cnt);
469+ if (!push->array)
470+ out_of_memory("push_local_excludes");
524989ae 471+
eabda998
WD
472+ for (i = 0, ap = push->array; i < mergelist_cnt; i++) {
473+ memcpy(ap++, mergelist_parents[i]->u.mergelist,
474+ sizeof (struct exclude_list_struct));
475+ }
476+
477+ /* Note: add_exclude_file() might increase mergelist_cnt, so keep
478+ * this loop separate from the above loop. */
479+ for (i = 0; i < mergelist_cnt; i++) {
480+ struct exclude_struct *ex = mergelist_parents[i];
481+ struct exclude_list_struct *lp = ex->u.mergelist;
a55d21aa 482+ int flags;
524989ae
WD
483+
484+ if (verbose > 2) {
485+ rprintf(FINFO, "[%s] pushing %sexclude list\n",
d8af8661 486+ who_am_i(), lp->debug_type);
524989ae 487+ }
d8af8661 488+
40e20a69
WD
489+ if (ex->match_flags & MATCHFLG_CVSIGNORE) {
490+ lp->head = NULL; /* CVS doesn't inherit rules. */
ee1af13c 491+ flags = XFLG_WORD_SPLIT | XFLG_WORDS_ONLY;
40e20a69 492+ } else {
eabda998 493+ flags = ex->match_flags & MATCHFLG_INCLUDE
524989ae 494+ ? XFLG_DEF_INCLUDE : 0;
a55d21aa 495+ }
40e20a69 496+ lp->tail = NULL; /* Switch any local rules to inherited. */
d34d9fad 497+
40e20a69
WD
498+ if (ex->match_flags & MATCHFLG_FINISH_SETUP) {
499+ ex->match_flags &= ~MATCHFLG_FINISH_SETUP;
500+ setup_merge_file(ex, lp, flags, dir, dirlen);
d34d9fad
WD
501+ }
502+
40e20a69 503+ if (strlcpy(dirbuf + dirbuf_offset, ex->pattern,
d8af8661 504+ MAXPATHLEN - dirbuf_offset) < MAXPATHLEN - dirbuf_offset)
40e20a69 505+ add_exclude_file(lp, dirbuf, flags | XFLG_ABS_PATH);
524989ae 506+ else {
a55d21aa
WD
507+ io_error |= IOERR_GENERAL;
508+ rprintf(FINFO,
509+ "cannot add local excludes in long-named directory %s\n",
d8af8661 510+ full_fname(dirbuf));
a55d21aa 511+ }
db4f43dd 512+ dirbuf[dirbuf_offset] = '\0';
a55d21aa
WD
513+ }
514+
524989ae 515+ return (void*)push;
a55d21aa
WD
516+}
517+
eabda998 518+void pop_local_excludes(void *mem)
a55d21aa 519+{
eabda998
WD
520+ struct mergelist_save_struct *pop = (struct mergelist_save_struct*)mem;
521+ struct exclude_list_struct *ap;
522+ int i;
d8af8661 523+
066e3b3e 524+ for (i = mergelist_cnt; i-- > 0; ) {
eabda998
WD
525+ struct exclude_struct *ex = mergelist_parents[i];
526+ struct exclude_list_struct *lp = ex->u.mergelist;
524989ae 527+
524989ae
WD
528+ if (verbose > 2) {
529+ rprintf(FINFO, "[%s] popping %sexclude list\n",
d8af8661 530+ who_am_i(), lp->debug_type);
524989ae 531+ }
d8af8661
WD
532+
533+ clear_exclude_list(lp);
a55d21aa 534+ }
d8af8661 535+
40e20a69 536+ mergelist_cnt = pop->count;
eabda998
WD
537+ for (i = 0, ap = pop->array; i < mergelist_cnt; i++) {
538+ memcpy(mergelist_parents[i]->u.mergelist, ap++,
539+ sizeof (struct exclude_list_struct));
540+ }
d8af8661
WD
541+
542+ free(pop->array);
543+ free(pop);
7cb7ae4e
WD
544+}
545+
a55d21aa 546 static int check_one_exclude(char *name, struct exclude_struct *ex,
7cb7ae4e
WD
547 int name_is_dir)
548 {
40e20a69 549@@ -122,7 +488,7 @@ static int check_one_exclude(char *name,
bc95f62b
WD
550 /* If the pattern does not have any slashes AND it does not have
551 * a "**" (which could match a slash), then we just match the
552 * name portion of the path. */
553- if (!ex->slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
554+ if (!ex->u.slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
555 if ((p = strrchr(name,'/')) != NULL)
556 name = p+1;
557 }
40e20a69 558@@ -133,7 +499,8 @@ static int check_one_exclude(char *name,
045caa90
WD
559 name = full_name;
560 }
561
562- if (!name[0]) return 0;
563+ if (!name[0])
564+ return 0;
565
566 if (ex->match_flags & MATCHFLG_DIRECTORY && !name_is_dir)
567 return 0;
40e20a69 568@@ -148,9 +515,9 @@ static int check_one_exclude(char *name,
bc95f62b
WD
569 if (ex->match_flags & MATCHFLG_WILD) {
570 /* A non-anchored match with an infix slash and no "**"
571 * needs to match the last slash_cnt+1 name elements. */
0c7d1fd8
WD
572- if (!match_start && ex->slash_cnt
573+ if (!match_start && ex->u.slash_cnt
574 && !(ex->match_flags & MATCHFLG_WILD2)) {
bc95f62b
WD
575- int cnt = ex->slash_cnt + 1;
576+ int cnt = ex->u.slash_cnt + 1;
577 for (p = name + strlen(name) - 1; p >= name; p--) {
578 if (*p == '/' && !--cnt)
579 break;
40e20a69 580@@ -221,6 +588,13 @@ int check_exclude(struct exclude_list_st
a55d21aa
WD
581 struct exclude_struct *ent;
582
583 for (ent = listp->head; ent; ent = ent->next) {
524989ae 584+ if (ent->match_flags & MATCHFLG_MERGE_FILE) {
eabda998 585+ int rc = check_exclude(ent->u.mergelist, name,
d8af8661 586+ name_is_dir);
a55d21aa
WD
587+ if (rc)
588+ return rc;
589+ continue;
590+ }
591 if (check_one_exclude(name, ent, name_is_dir)) {
592 report_exclude_result(name, ent, name_is_dir,
593 listp->debug_type);
40e20a69 594@@ -253,11 +627,36 @@ static const char *get_exclude_tok(const
a55d21aa
WD
595 p = (const char *)s;
596 }
597
598- /* Is this a '+' or '-' followed by a space (not whitespace)? */
d34d9fad 599+ /* Check for a +/-/. followed by a space (not whitespace). */
ee1af13c 600 if (!(xflags & XFLG_WORDS_ONLY)
a55d21aa 601- && (*s == '-' || *s == '+') && s[1] == ' ') {
a55d21aa 602+ && (*s == '-' || *s == '+' || *s == '.') && s[1] == ' ') {
0c7d1fd8
WD
603 if (*s == '+')
604 mflags |= MATCHFLG_INCLUDE;
a55d21aa 605+ else if (*s == '.') {
524989ae 606+ mflags |= MATCHFLG_MERGE_FILE;
a55d21aa
WD
607+ if (xflags & XFLG_DEF_INCLUDE)
608+ mflags |= MATCHFLG_INCLUDE;
d34d9fad
WD
609+ while (s[2] == '-') {
610+ s += 2;
611+ do {
612+ switch (*++s) {
d34d9fad 613+ case 'p':
40e20a69
WD
614+ mflags |= MATCHFLG_PERDIR_MERGE
615+ | MATCHFLG_FINISH_SETUP;
d34d9fad
WD
616+ break;
617+ case '-':
618+ if (s[1] == ' ')
619+ goto done;
620+ default:
621+ rprintf(FERROR,
622+ "invalid merge options: %s\n",
623+ p);
624+ exit_cleanup(RERR_SYNTAX);
625+ }
626+ } while (s[1] != ' ');
627+ }
a55d21aa 628+ }
d34d9fad 629+ done:
a55d21aa 630 s += 2;
0c7d1fd8
WD
631 } else if (xflags & XFLG_DEF_INCLUDE)
632 mflags |= MATCHFLG_INCLUDE;
40e20a69
WD
633@@ -273,6 +672,8 @@ static const char *get_exclude_tok(const
634
635 if (*p == '!' && len == 1 && !(xflags & XFLG_WORDS_ONLY))
636 mflags |= MATCHFLG_CLEAR_LIST;
637+ if (xflags & XFLG_ABS_PATH)
638+ mflags |= MATCHFLG_ABS_PATH;
639
640 *len_ptr = len;
641 *flag_ptr = mflags;
642@@ -292,9 +693,15 @@ void add_exclude(struct exclude_list_str
045caa90
WD
643 cp = pattern;
644 pat_len = 0;
645 while (1) {
646+ /* Remember that the returned string is NOT '\0' terminated! */
647 cp = get_exclude_tok(cp + pat_len, &pat_len, &mflags, xflags);
648 if (!pat_len)
649 break;
41ebea83
WD
650+ if (pat_len >= MAXPATHLEN) {
651+ rprintf(FERROR, "discarding over-long exclude: %s\n",
652+ cp);
653+ continue;
654+ }
655
656 if (mflags & MATCHFLG_CLEAR_LIST) {
657 if (verbose > 2) {
40e20a69 658@@ -306,13 +713,23 @@ void add_exclude(struct exclude_list_str
0c7d1fd8
WD
659 continue;
660 }
a55d21aa 661
d34d9fad
WD
662- make_exclude(listp, cp, pat_len, mflags);
663-
664- if (verbose > 2) {
665- rprintf(FINFO, "[%s] add_exclude(%.*s, %s%sclude)\n",
666- who_am_i(), (int)pat_len, cp, listp->debug_type,
667- mflags & MATCHFLG_INCLUDE ? "in" : "ex");
524989ae 668+ if (mflags & MATCHFLG_MERGE_FILE) {
41ebea83 669+ if (mflags & MATCHFLG_PERDIR_MERGE) {
40e20a69 670+ if (parent_dirscan) {
41ebea83
WD
671+ if (!(cp = parse_merge_name(cp, &pat_len)))
672+ continue;
673+ make_exclude(listp, cp, pat_len, mflags);
d34d9fad
WD
674+ continue;
675+ }
41ebea83
WD
676+ } else {
677+ if (!(cp = parse_merge_name(cp, &pat_len)))
d34d9fad 678+ continue;
41ebea83
WD
679+ add_exclude_file(listp, cp, xflags | XFLG_FATAL_ERRORS);
680+ continue;
0c7d1fd8 681+ }
a55d21aa 682 }
d34d9fad
WD
683+
684+ make_exclude(listp, cp, pat_len, mflags);
0c7d1fd8 685 }
d34d9fad
WD
686 }
687
40e20a69 688@@ -321,7 +738,7 @@ void add_exclude_file(struct exclude_lis
41ebea83
WD
689 int xflags)
690 {
691 FILE *fp;
692- char line[MAXPATHLEN+3]; /* Room for "x " prefix and trailing slash. */
693+ char line[MAXPATHLEN+7]; /* Room for prefix chars and trailing slash. */
694 char *eob = line + sizeof line - 1;
695 int word_split = xflags & XFLG_WORD_SPLIT;
696
40e20a69 697@@ -343,6 +760,11 @@ void add_exclude_file(struct exclude_lis
c4e46f36
WD
698 return;
699 }
700
701+ if (verbose > 2) {
1e476835
WD
702+ rprintf(FINFO, "[%s] add_exclude_file(%s,%d)\n",
703+ who_am_i(), fname, xflags);
c4e46f36
WD
704+ }
705+
706 while (1) {
707 char *s = line;
708 int ch, overflow = 0;
40e20a69 709@@ -402,7 +824,21 @@ void send_exclude_list(int f)
0c7d1fd8 710 if (ent->match_flags & MATCHFLG_INCLUDE) {
a55d21aa
WD
711 write_int(f, l + 2);
712 write_buf(f, "+ ", 2);
713- } else if ((*p == '-' || *p == '+') && p[1] == ' ') {
524989ae 714+ } else if (ent->match_flags & MATCHFLG_MERGE_FILE) {
41ebea83
WD
715+ char buf[32], *op = buf;
716+ *op++ = '.';
717+ *op++ = ' ';
718+ if (ent->match_flags & MATCHFLG_PERDIR_MERGE) {
719+ *op++ = '-';
720+ *op++ = 'p';
40e20a69
WD
721+ if (*p == '-')
722+ *op++ = '-';
41ebea83
WD
723+ *op++ = ' ';
724+ }
725+ write_int(f, l + (op - buf));
726+ write_buf(f, buf, op - buf);
a55d21aa
WD
727+ } else if ((*p == '-' || *p == '+' || *p == '.')
728+ && p[1] == ' ') {
729 write_int(f, l + 2);
730 write_buf(f, "- ", 2);
731 } else
40e20a69 732@@ -443,6 +879,7 @@ void add_cvs_excludes(void)
a55d21aa
WD
733 char fname[MAXPATHLEN];
734 char *p;
735
d34d9fad 736+ add_exclude(&exclude_list, ". -p .cvsignore", 0);
a55d21aa 737 add_exclude(&exclude_list, default_cvsignore,
ee1af13c 738 XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
a55d21aa 739
d34d9fad 740--- orig/flist.c 2004-08-05 21:57:29
40e20a69 741+++ flist.c 2004-08-09 02:45:42
d34d9fad 742@@ -39,10 +39,9 @@ extern int module_id;
a55d21aa
WD
743 extern int ignore_errors;
744 extern int numeric_ids;
745
746-extern int cvs_exclude;
747-
748 extern int recurse;
749 extern char curr_dir[MAXPATHLEN];
d34d9fad 750+extern unsigned int curr_dir_len;
a55d21aa 751 extern char *files_from;
d34d9fad
WD
752 extern int filesfrom_fd;
753
9be39c35 754@@ -66,7 +65,6 @@ extern int list_only;
a55d21aa
WD
755
756 extern struct exclude_list_struct exclude_list;
757 extern struct exclude_list_struct server_exclude_list;
758-extern struct exclude_list_struct local_exclude_list;
759
760 int io_error;
761
9be39c35 762@@ -221,8 +219,6 @@ int link_stat(const char *path, STRUCT_S
a55d21aa
WD
763 */
764 static int check_exclude_file(char *fname, int is_dir, int exclude_level)
765 {
766- int rc;
767-
768 #if 0 /* This currently never happens, so avoid a useless compare. */
769 if (exclude_level == NO_EXCLUDES)
770 return 0;
9be39c35 771@@ -244,10 +240,7 @@ static int check_exclude_file(char *fnam
a55d21aa
WD
772 if (exclude_level != ALL_EXCLUDES)
773 return 0;
774 if (exclude_list.head
775- && (rc = check_exclude(&exclude_list, fname, is_dir)) != 0)
776- return rc < 0;
777- if (local_exclude_list.head
778- && check_exclude(&local_exclude_list, fname, is_dir) < 0)
779+ && check_exclude(&exclude_list, fname, is_dir) < 0)
780 return 1;
781 return 0;
782 }
9be39c35 783@@ -954,15 +947,7 @@ void send_file_name(int f, struct file_l
a55d21aa
WD
784
785 if (recursive && S_ISDIR(file->mode)
786 && !(file->flags & FLAG_MOUNT_POINT)) {
787- struct exclude_list_struct last_list = local_exclude_list;
788- local_exclude_list.head = local_exclude_list.tail = NULL;
789 send_directory(f, flist, f_name_to(file, fbuf));
7cb7ae4e
WD
790- if (verbose > 2) {
791- rprintf(FINFO, "[%s] popping %sexclude list\n",
792- who_am_i(), local_exclude_list.debug_type);
793- }
d8af8661 794- clear_exclude_list(&local_exclude_list);
a55d21aa
WD
795- local_exclude_list = last_list;
796 }
797 }
798
9be39c35 799@@ -973,6 +958,7 @@ static void send_directory(int f, struct
a55d21aa
WD
800 struct dirent *di;
801 char fname[MAXPATHLEN];
802 unsigned int offset;
803+ void *save_excludes;
804 char *p;
805
806 d = opendir(dir);
40e20a69 807@@ -996,18 +982,7 @@ static void send_directory(int f, struct
a55d21aa
WD
808 offset++;
809 }
810
811- if (cvs_exclude) {
812- if (strlcpy(p, ".cvsignore", MAXPATHLEN - offset)
813- < MAXPATHLEN - offset) {
814- add_exclude_file(&local_exclude_list, fname,
ee1af13c 815- XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
a55d21aa
WD
816- } else {
817- io_error |= IOERR_GENERAL;
818- rprintf(FINFO,
819- "cannot cvs-exclude in long-named directory %s\n",
820- full_fname(fname));
821- }
045caa90 822- }
045caa90
WD
823+ save_excludes = push_local_excludes(fname, offset);
824
a55d21aa
WD
825 for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
826 char *dname = d_name(di);
40e20a69 827@@ -1028,6 +1003,8 @@ static void send_directory(int f, struct
bc95f62b 828 rsyserr(FERROR, errno, "readdir(%s)", dir);
a55d21aa 829 }
a55d21aa 830
eabda998
WD
831+ pop_local_excludes(save_excludes);
832+
a55d21aa
WD
833 closedir(d);
834 }
eabda998 835
40e20a69 836@@ -1047,6 +1024,7 @@ struct file_list *send_file_list(int f,
d34d9fad
WD
837 char *p, *dir, olddir[sizeof curr_dir];
838 char lastpath[MAXPATHLEN] = "";
839 struct file_list *flist;
40e20a69 840+ BOOL did_first_push = 0;
d34d9fad
WD
841 int64 start_write;
842 int use_ff_fd = 0;
843
40e20a69 844@@ -1067,6 +1045,10 @@ struct file_list *send_file_list(int f,
d34d9fad
WD
845 exit_cleanup(RERR_FILESELECT);
846 }
847 use_ff_fd = 1;
848+ if (curr_dir_len < MAXPATHLEN - 1) {
40e20a69
WD
849+ push_local_excludes(curr_dir, curr_dir_len);
850+ did_first_push = 1;
1e476835 851+ }
d34d9fad
WD
852 }
853 }
854
40e20a69 855@@ -1097,6 +1079,16 @@ struct file_list *send_file_list(int f,
d34d9fad
WD
856 }
857 }
858
40e20a69 859+ if (!did_first_push) {
d34d9fad
WD
860+ if ((p = strrchr(fname, '/')) != NULL) {
861+ dir = fname;
862+ p++;
40e20a69
WD
863+ } else
864+ dir = p = "";
865+ push_local_excludes(dir, p - dir);
866+ did_first_push = 1;
d34d9fad 867+ }
1e476835 868+
d34d9fad
WD
869 if (link_stat(fname, &st, keep_dirlinks) != 0) {
870 if (f != -1) {
871 io_error |= IOERR_GENERAL;
872--- orig/options.c 2004-08-05 21:57:29
40e20a69
WD
873+++ options.c 2004-08-09 03:33:31
874@@ -287,6 +287,7 @@ void usage(enum logcode F)
875 rprintf(F," --include=PATTERN don't exclude files matching PATTERN\n");
876 rprintf(F," --include-from=FILE don't exclude patterns listed in FILE\n");
877 rprintf(F," --files-from=FILE read FILE for list of source-file names\n");
878+ rprintf(F," -E same as --exclude='. -p /.rsync-excludes'\n");
879 rprintf(F," -0, --from0 all *-from file lists are delimited by nulls\n");
880 rprintf(F," --version print version number\n");
881 rprintf(F," --daemon run as an rsync daemon\n");
882@@ -389,6 +390,7 @@ static struct poptOption long_options[]
d34d9fad
WD
883 {"ignore-errors", 0, POPT_ARG_NONE, &ignore_errors, 0, 0, 0 },
884 {"blocking-io", 0, POPT_ARG_VAL, &blocking_io, 1, 0, 0 },
885 {"no-blocking-io", 0, POPT_ARG_VAL, &blocking_io, 0, 0, 0 },
886+ {0, 'E', POPT_ARG_NONE, 0, 'E', 0, 0 },
887 {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 },
888 {"config", 0, POPT_ARG_STRING, &config_file, 0, 0, 0 },
889 {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
40e20a69 890@@ -589,6 +591,11 @@ int parse_arguments(int *argc, const cha
d34d9fad
WD
891 am_sender = 1;
892 break;
893
894+ case 'E':
895+ add_exclude(&exclude_list,
40e20a69 896+ ". -p /.rsync-excludes", 0);
d34d9fad 897+ break;
1e476835 898+
d34d9fad
WD
899 case 'P':
900 do_progress = 1;
901 keep_partial = 1;
045caa90 902--- orig/rsync.h 2004-08-03 15:41:32
40e20a69
WD
903+++ rsync.h 2004-08-08 06:07:01
904@@ -108,6 +108,7 @@
905 #define XFLG_DEF_INCLUDE (1<<1)
906 #define XFLG_WORDS_ONLY (1<<2)
907 #define XFLG_WORD_SPLIT (1<<3)
908+#define XFLG_ABS_PATH (1<<4)
909
910 #define PERMS_REPORT (1<<0)
911 #define PERMS_SKIP_MTIME (1<<1)
912@@ -499,11 +500,18 @@ struct map_struct {
0c7d1fd8
WD
913 #define MATCHFLG_INCLUDE (1<<4) /* this is an include, not an exclude */
914 #define MATCHFLG_DIRECTORY (1<<5) /* this matches only directories */
915 #define MATCHFLG_CLEAR_LIST (1<<6) /* this item is the "!" token */
524989ae 916+#define MATCHFLG_MERGE_FILE (1<<7) /* specifies a file to merge */
a55d21aa 917+#define MATCHFLG_CVSIGNORE (1<<8) /* parse this as a .cvsignore file */
40e20a69
WD
918+#define MATCHFLG_PERDIR_MERGE (1<<9) /* merge-file is searched per-dir */
919+#define MATCHFLG_FINISH_SETUP (1<<10)/* per-dir merge file needs setup */
a55d21aa
WD
920 struct exclude_struct {
921 struct exclude_struct *next;
922 char *pattern;
0c7d1fd8 923 unsigned int match_flags;
bc95f62b 924- int slash_cnt;
bc95f62b
WD
925+ union {
926+ int slash_cnt;
eabda998 927+ struct exclude_list_struct *mergelist;
bc95f62b 928+ } u;
a55d21aa
WD
929 };
930
931 struct exclude_list_struct {
045caa90 932--- orig/rsync.yo 2004-08-03 15:34:32
40e20a69 933+++ rsync.yo 2004-08-09 03:10:29
d34d9fad 934@@ -335,6 +335,7 @@ verb(
1e476835
WD
935 --include=PATTERN don't exclude files matching PATTERN
936 --include-from=FILE don't exclude patterns listed in FILE
1e476835 937 --files-from=FILE read FILE for list of source-file names
40e20a69 938+ -E same as --exclude='. -p /.rsync-excludes'
1e476835
WD
939 -0 --from0 all file lists are delimited by nulls
940 --version print version number
d34d9fad
WD
941 --daemon run as an rsync daemon
942@@ -1086,6 +1087,11 @@ itemize(
1e476835
WD
943 then it is always considered an exclude pattern, even if specified as
944 part of an include option. The prefix is discarded before matching.
945
623f2443
WD
946+ it() if the pattern starts with ". " (a dot followed by a space) then its
947+ pattern is taken to be a merge file that is read in to supplement the
1e476835
WD
948+ current rules. See the section on MERGING EXCLUDE FILES for more
949+ information.
950+
951 it() if the pattern is a single exclamation mark ! then the current
952 include/exclude list is reset, removing all previously defined patterns.
953 )
40e20a69 954@@ -1138,6 +1144,104 @@ itemize(
524989ae
WD
955 it would be excluded by the "*")
956 )
957
958+manpagesection(MERGING EXCLUDE FILES)
959+
d34d9fad
WD
960+You can merge whole files into an exclude file by specifying a rule that
961+starts with a ". " (a dot followed by a space) and putting a filename in
962+place of the pattern. The filename may be preceded by one or more options:
963+
964+startdit()
965+
d34d9fad
WD
966+dit(bf(-p)) Make the file a per-directory merge-file. Rsync will scan
967+every directory that it traverses for the named file, merging its contents
40e20a69
WD
968+when the file exists. (Without this option rsync just merges the rules into
969+the parent file.)
970+
971+Rules are inherited in all subdirectories of the directory where the
972+per-dir merge file was found. Each subdirectory's rules are prefixed to
973+the inherited rules from a parent directory, which gives the newest rules a
974+higher priority than the inherited rules. If you don't want a rule to be
975+inherited, anchor it with a leading slash. Anchored rules in a
976+per-directory merge file are relative to the current directory, so a rule
977+"/foo" would only exclude the file "foo" in the current directory.
978+
979+Note also that you can eliminate all the inherited rules for a directory
980+and its subdirectories by putting the list-clearing token (!) at the start
981+of a per-directory file. This only clears the rules in the current
982+sub-list, not all the rules.
d34d9fad
WD
983+
984+dit(bf(--)) End the scanning of options. Useful if you want to specify a
985+filename that begins with a dash.
986+
987+enddit()
524989ae 988+
bc95f62b 989+Here's an example exclude file (which you'd specify via the normal
d34d9fad 990+--exclude-from=FILE option):
524989ae
WD
991+
992+verb(
993+ . /home/user/.global_excludes
41ebea83 994+ *.gz
40e20a69 995+ . -p .excl
524989ae 996+ + *.[ch]
41ebea83 997+ *.o
524989ae
WD
998+)
999+
1000+This will merge the contents of the /home/user/.global_excludes file at the
1001+start of the list and also turns the ".excl" filename into a per-directory
40e20a69
WD
1002+exclude file. All the merged rules default to being exclude rules because
1003+an exclude statement was used to specify them.
d34d9fad
WD
1004+
1005+Note also that the parsing of any merge-file named ".cvsignore" is always
1006+done in a CVS-compatible manner, even if -C wasn't specified. This means
1007+that its rules are always excludes (even if an include option specified the
1008+file), tokens are split on whitespace, the rules are never inherited, and
41ebea83 1009+no special characters are honored (e.g. no comments, no "!", etc.).
524989ae
WD
1010+
1011+Additionally, you can affect where the --cvs-exclude (-C) option's
41ebea83
WD
1012+inclusion of the per-directory .cvsignore file gets placed into your rules
1013+by adding your own explicit per-directory merge rule for ".cvsignore".
1014+Without this rsync would add its this rule at the end of all your other
1015+rules (giving it a lower priority than your command-line rules). For
1016+example, specifying this:
524989ae
WD
1017+
1018+verb(
d34d9fad 1019+ rsync -avC --exclude='. -p .cvsignore' --exclude-from=foo a/ b
524989ae
WD
1020+)
1021+
1022+will merge all the per-directory .cvsignore rules at the start of your list
1023+rather than at the end. This allows their dir-specific rules to supersede
1024+your rules instead of being subservient to them. (The global rules taken
d34d9fad 1025+from the $HOME/.cvsignore file and from $CVSIGNORE are not repositioned by
524989ae
WD
1026+this.)
1027+
d34d9fad
WD
1028+If a per-directory merge file is specified with a path that is a parent
1029+directory of the first transfer directory, rsync will scan all the parent
1030+dirs from that starting point to the transfer directory for the indicated
1031+per-directory file. For instance, the -E option is an abbreviation for
1032+this command:
1033+
1034+verb(
40e20a69 1035+ --exclude='. -p /.rsync-excludes'
d34d9fad
WD
1036+)
1037+
1038+That exclude tells rsync to scan for the file .rsync-excludes in all
41ebea83
WD
1039+directories from the root up through the source of the transfer. For an
1040+rsync daemon, the root dir is always the module's "path" setting, not the
1041+root of the filesystem (unless the two are the same).
1042+
1043+Some examples of this pre-scanning for per-directory files:
d34d9fad
WD
1044+
1045+verb(
1046+ rsync -avE /src/path/ /dest/dir
41ebea83 1047+ rsync -av --exclude='. -p ../../.rsync-excludes' /src/path/ /dest/dir
40e20a69 1048+ rsync -av --exclude='. -p .rsync-excludes' /src/path/ /dest/dir
d34d9fad 1049+)
1e476835 1050+
41ebea83
WD
1051+The first two commands above will look for .rsync-excludes in "/" and
1052+"/src" before the normal scan begins looking for the file in "/src/path"
1053+and its subdirectories. The last command above just starts scanning
1054+from "/src/path".
524989ae
WD
1055+
1056 manpagesection(BATCH MODE)
1057
1058 bf(Note:) Batch mode should be considered experimental in this version
13bed3dd 1059--- orig/testsuite/exclude.test 2004-05-29 21:25:45
40e20a69 1060+++ testsuite/exclude.test 2004-08-08 06:35:15
c4e46f36 1061@@ -23,19 +23,47 @@ export HOME CVSIGNORE
7d31425d
WD
1062 makepath "$fromdir/foo/down/to/you"
1063 makepath "$fromdir/bar/down/to/foo/too"
1064 makepath "$fromdir/mid/for/foo/and/that/is/who"
c4e46f36 1065+cat >"$fromdir/.excl" <<EOF
7d31425d
WD
1066+.excl
1067+*.bak
1068+*.old
1069+*.junk
c4e46f36 1070+EOF
7d31425d
WD
1071 echo kept >"$fromdir/foo/file1"
1072 echo removed >"$fromdir/foo/file2"
1073 echo cvsout >"$fromdir/foo/file2.old"
c4e46f36 1074+cat >"$fromdir/foo/.excl" <<EOF
7d31425d
WD
1075++ .excl
1076+- file1
c4e46f36
WD
1077+EOF
1078+cat >"$fromdir/bar/.excl" <<EOF
7d31425d 1079+home-cvs-exclude
40e20a69 1080+. -p .excl2
7d31425d 1081++ to
c4e46f36 1082+EOF
7d31425d 1083 echo cvsout >"$fromdir/bar/down/to/home-cvs-exclude"
c4e46f36 1084+cat >"$fromdir/bar/down/to/.excl2" <<EOF
7d31425d 1085+.excl2
c4e46f36 1086+EOF
7d31425d
WD
1087 echo keeper >"$fromdir/bar/down/to/foo/file1"
1088 echo cvsout >"$fromdir/bar/down/to/foo/file1.bak"
1089 echo gone >"$fromdir/bar/down/to/foo/file3"
1090 echo lost >"$fromdir/bar/down/to/foo/file4"
1091 echo cvsout >"$fromdir/bar/down/to/foo/file4.junk"
1092 echo smashed >"$fromdir/bar/down/to/foo/to"
c4e46f36 1093+cat >"$fromdir/bar/down/to/foo/.excl2" <<EOF
7d31425d 1094++ *.junk
c4e46f36 1095+EOF
7d31425d 1096+# This one should be ineffectual
c4e46f36 1097+cat >"$fromdir/mid/.excl2" <<EOF
7d31425d 1098+extra
c4e46f36 1099+EOF
7d31425d
WD
1100 echo cvsout >"$fromdir/mid/one-in-one-out"
1101 echo one-in-one-out >"$fromdir/mid/.cvsignore"
1102 echo cvsin >"$fromdir/mid/one-for-all"
c4e46f36 1103+cat >"$fromdir/mid/.excl" <<EOF
d34d9fad 1104+. -p .cvsignore
c4e46f36 1105+EOF
7d31425d
WD
1106 echo cvsin >"$fromdir/mid/for/one-in-one-out"
1107 echo expunged >"$fromdir/mid/for/foo/extra"
1108 echo retained >"$fromdir/mid/for/foo/keep"
c4e46f36
WD
1109@@ -100,5 +128,24 @@ $RSYNC -av --existing --include='*/' --e
1110 checkit "$RSYNC -avvC --exclude-from=\"$excl\" \
1111 --delete-excluded \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
7d31425d
WD
1112
1113+# Modify the chk dir for our merge-exclude test and then tweak the dir times.
1114+
1115+rm "$chkdir"/.excl
1116+rm "$chkdir"/foo/file1
1117+rm "$chkdir"/bar/.excl
1118+rm "$chkdir"/bar/down/to/.excl2
1119+rm "$chkdir"/bar/down/to/foo/.excl2
1120+rm "$chkdir"/mid/.excl
1121+cp -p "$fromdir"/bar/down/to/foo/*.junk "$chkdir"/bar/down/to/foo
1122+cp -p "$fromdir"/bar/down/to/foo/to "$chkdir"/bar/down/to/foo
1123+
1124+$RSYNC -av --existing --include='*/' --exclude='*' "$fromdir/" "$chkdir/"
1125+
1126+# Now, test if rsync excludes the same files, this time with a merge-exclude
1127+# file.
1128+
40e20a69 1129+checkit "$RSYNC -avv --exclude='. -p .excl' --exclude-from=\"$excl\" \
c4e46f36 1130+ --delete-excluded \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
7d31425d
WD
1131+
1132 # The script would have aborted on error, so getting here means we've won.
1133 exit 0