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