- Fixed the handling of the "!" list-clearing rules.
[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
147a9095 6This patch adds the --filter option, which implements an improved set of
ee35f7fb 7include/exclude rules:
147a9095
WD
8
9 . SINGLE-INSTANCE_MERGE_FILE
10 : PER-DIRECTORY_MERGE_FILE
11 - exclude-pattern
12 + include-pattern
13
14Note that the prefix for a filter rule is NOT optional, and that the
ee35f7fb
WD
15separating space can be replaced by an equal-sign (=) or an underscore (_),
16if desired. There are also optional modifiers that can be specified for
17the merge-file rules.
147a9095
WD
18
19A per-directory merge file is one that will be looked for in every
20sub-directory that rsync visits, and the rules found in that sub-
21directory's file will affect that dir and (if desired) its subdirs.
a55d21aa
WD
22
23For example:
24
ee35f7fb 25 rsync -av --filter :_.rules from/ to
a55d21aa 26
ee35f7fb 27The above will look for a file named ".rules" in every directory of the
147a9095 28hierarchy that rsync visits, and it will filter names based on the rules
ee35f7fb 29found therein. If one of the .rules files contains this:
a55d21aa
WD
30
31 + *.c
ee35f7fb
WD
32 : .rules2
33 . .rules3
147a9095
WD
34 - *.o
35 - /foobar
a55d21aa 36
ee35f7fb
WD
37Then the file ".rules2" will also be read in from the current dir and all
38its subdirs. The file ".rules3" would just be read in from the current dir
39only. The exclusion of "foobar" will only happen in that .rules file's
147a9095 40directory because the rule is anchored, which is one way to make a rule
4f92d035 41local instead of inherited (see also the 'n' modifier).
a55d21aa
WD
42
43..wayne..
44
37da98ae 45--- orig/clientserver.c 2005-01-01 21:11:00
147a9095 46+++ clientserver.c 2005-01-16 23:33:02
37da98ae 47@@ -49,12 +49,14 @@ extern int no_detach;
40e20a69
WD
48 extern int default_af_hint;
49 extern char *bind_address;
50 extern struct exclude_list_struct server_exclude_list;
51-extern char *exclude_path_prefix;
52 extern char *config_file;
53 extern char *files_from;
40e20a69
WD
54
55 char *auth_user;
56
3d3aaf9f 57+/* Length of lp_path() string when in daemon mode & not chrooted, else 0. */
45795ec6
WD
58+unsigned int module_dirlen = 0;
59+
60 /**
61 * Run a client connected to an rsyncd. The alternative to this
62 * function for remote-shell connections is do_cmd().
147a9095 63@@ -304,26 +306,33 @@ static int rsync_module(int f_in, int f_
40e20a69
WD
64 /* TODO: Perhaps take a list of gids, and make them into the
65 * supplementary groups. */
66
67- exclude_path_prefix = use_chroot? "" : lp_path(i);
68- if (*exclude_path_prefix == '/' && !exclude_path_prefix[1])
69- exclude_path_prefix = "";
45795ec6
WD
70+ if (use_chroot) {
71+ module_dirlen = 0;
72+ set_excludes_dir("/", 1);
73+ } else {
74+ module_dirlen = strlen(lp_path(i));
75+ set_excludes_dir(lp_path(i), module_dirlen);
76+ }
147a9095
WD
77+
78+ p = lp_filter(i);
79+ add_exclude(&server_exclude_list, p,
80+ XFLG_WORD_SPLIT | XFLG_ABS_PATH);
40e20a69
WD
81
82 p = lp_include_from(i);
83 add_exclude_file(&server_exclude_list, p,
84- XFLG_FATAL_ERRORS | XFLG_DEF_INCLUDE);
147a9095 85+ XFLG_FATAL_ERRORS | XFLG_ABS_PATH | XFLG_DEF_INCLUDE);
40e20a69
WD
86
87 p = lp_include(i);
88 add_exclude(&server_exclude_list, p,
89- XFLG_WORD_SPLIT | XFLG_DEF_INCLUDE);
147a9095 90+ XFLG_WORD_SPLIT | XFLG_ABS_PATH | XFLG_DEF_INCLUDE);
40e20a69
WD
91
92 p = lp_exclude_from(i);
93 add_exclude_file(&server_exclude_list, p,
94- XFLG_FATAL_ERRORS);
147a9095 95+ XFLG_FATAL_ERRORS | XFLG_ABS_PATH | XFLG_DEF_EXCLUDE);
40e20a69
WD
96
97 p = lp_exclude(i);
98- add_exclude(&server_exclude_list, p, XFLG_WORD_SPLIT);
45795ec6 99-
40e20a69 100- exclude_path_prefix = NULL;
147a9095
WD
101+ add_exclude(&server_exclude_list, p,
102+ XFLG_WORD_SPLIT | XFLG_ABS_PATH | XFLG_DEF_EXCLUDE);
40e20a69
WD
103
104 log_init();
105
2e064775 106--- orig/exclude.c 2005-01-13 23:15:56
ee35f7fb
WD
107+++ exclude.c 2005-01-17 05:55:59
108@@ -30,15 +30,68 @@ extern int verbose;
a55d21aa
WD
109 extern int eol_nulls;
110 extern int list_only;
111 extern int recurse;
112+extern int io_error;
56babefa 113+extern int sanitize_paths;
147a9095 114+extern int protocol_version;
a55d21aa
WD
115
116 extern char curr_dir[];
56babefa 117+extern unsigned int curr_dir_len;
45795ec6 118+extern unsigned int module_dirlen;
a55d21aa 119
d8af8661 120 struct exclude_list_struct exclude_list = { 0, 0, "" };
524989ae 121-struct exclude_list_struct local_exclude_list = { 0, 0, "per-dir .cvsignore " };
d8af8661 122 struct exclude_list_struct server_exclude_list = { 0, 0, "server " };
40e20a69 123-char *exclude_path_prefix = NULL;
147a9095
WD
124
125-/** Build an exclude structure given an exclude pattern. */
db4f43dd
WD
126+/* The dirbuf is set by push_local_excludes() to the current subdirectory
127+ * relative to curr_dir that is being processed. The path always has a
45795ec6
WD
128+ * trailing slash appended, and the variable dirbuf_len contains the length
129+ * of this path prefix. The path is always absolute. */
130+static char dirbuf[MAXPATHLEN+1];
131+static unsigned int dirbuf_len = 0;
b338da5b 132+static int dirbuf_depth;
45795ec6
WD
133+
134+/* This is True when we're scanning parent dirs for per-dir merge-files. */
135+static BOOL parent_dirscan = False;
db4f43dd
WD
136+
137+/* This array contains a list of all the currently active per-dir merge
138+ * files. This makes it easier to save the appropriate values when we
139+ * "push" down into each subdirectory. */
d34d9fad
WD
140+static struct exclude_struct **mergelist_parents;
141+static int mergelist_cnt = 0;
142+static int mergelist_size = 0;
a55d21aa 143+
d1e7c1c8
WD
144+/* Each exclude_list_struct describes a singly-linked list by keeping track
145+ * of both the head and tail pointers. The list is slightly unusual in that
146+ * a parent-dir's content can be appended to the end of the local list in a
147+ * special way: the last item in the local list has its "next" pointer set
0c7d1fd8 148+ * to point to the inherited list, but the local list's tail pointer points
d1e7c1c8 149+ * at the end of the local list. Thus, if the local list is empty, the head
0c7d1fd8 150+ * will be pointing at the inherited content but the tail will be NULL. To
d1e7c1c8
WD
151+ * help you visualize this, here are the possible list arrangements:
152+ *
153+ * Completely Empty Local Content Only
154+ * ================================== ====================================
155+ * head -> NULL head -> Local1 -> Local2 -> NULL
156+ * tail -> NULL tail -------------^
157+ *
158+ * Inherited Content Only Both Local and Inherited Content
159+ * ================================== ====================================
160+ * head -> Parent1 -> Parent2 -> NULL head -> L1 -> L2 -> P1 -> P2 -> NULL
161+ * tail -> NULL tail ---------^
162+ *
2e064775 163+ * This means that anyone wanting to traverse the whole list to use it just
d1e7c1c8
WD
164+ * needs to start at the head and use the "next" pointers until it goes
165+ * NULL. To add new local content, we insert the item after the tail item
166+ * and update the tail (obviously, if "tail" was NULL, we insert it at the
167+ * head). To clear the local list, WE MUST NOT FREE THE INHERITED CONTENT
bc95f62b
WD
168+ * because it is shared between the current list and our parent list(s).
169+ * The easiest way to handle this is to simply truncate the list after the
170+ * tail item and then free the local list from the head. When inheriting
171+ * the list for a new local dir, we just save off the exclude_list_struct
0c7d1fd8 172+ * values (so we can pop back to them later) and set the tail to NULL.
d1e7c1c8 173+ */
147a9095
WD
174+
175+/* Build an exclude structure given an exclude pattern. The value in "pat"
176+ * is not null-terminated. */
0c7d1fd8 177 static void make_exclude(struct exclude_list_struct *listp, const char *pat,
147a9095
WD
178 unsigned int pat_len, unsigned int mflags)
179 {
ee35f7fb 180@@ -46,23 +99,45 @@ static void make_exclude(struct exclude_
a55d21aa 181 const char *cp;
0c7d1fd8
WD
182 unsigned int ex_len;
183
d34d9fad 184+ if (verbose > 2) {
147a9095 185+ rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s)\n",
ee35f7fb
WD
186+ who_am_i(), (int)pat_len, pat,
187+ mflags & MATCHFLG_MERGE_FILE ? "per-dir-merge"
188+ : mflags & MATCHFLG_INCLUDE ? "include" : "exclude",
189+ listp->debug_type);
d34d9fad
WD
190+ }
191+
524989ae 192+ if (mflags & MATCHFLG_MERGE_FILE) {
eabda998 193+ int i;
2e064775 194+ /* If the local merge file was already mentioned, don't
524989ae 195+ * add it again. */
eabda998
WD
196+ for (i = 0; i < mergelist_cnt; i++) {
197+ struct exclude_struct *ex = mergelist_parents[i];
198+ if (strlen(ex->pattern) == pat_len
d8af8661 199+ && memcmp(ex->pattern, pat, pat_len) == 0)
a55d21aa
WD
200+ return;
201+ }
a55d21aa 202+ }
0c7d1fd8 203+
a55d21aa
WD
204 ret = new(struct exclude_struct);
205 if (!ret)
206 out_of_memory("make_exclude");
40e20a69
WD
207
208 memset(ret, 0, sizeof ret[0]);
209
210- if (exclude_path_prefix)
211- mflags |= MATCHFLG_ABS_PATH;
212- if (exclude_path_prefix && *pat == '/')
213- ex_len = strlen(exclude_path_prefix);
214- else
215+ if (mflags & MATCHFLG_ABS_PATH) {
216+ if (*pat != '/') {
217+ mflags &= ~MATCHFLG_ABS_PATH;
218+ ex_len = 0;
45795ec6
WD
219+ } else
220+ ex_len = dirbuf_len - module_dirlen - 1;
40e20a69
WD
221+ } else
222 ex_len = 0;
223 ret->pattern = new_array(char, ex_len + pat_len + 1);
224 if (!ret->pattern)
225 out_of_memory("make_exclude");
45795ec6 226 if (ex_len)
40e20a69 227- memcpy(ret->pattern, exclude_path_prefix, ex_len);
5f98a4b1 228+ memcpy(ret->pattern, dirbuf + module_dirlen, ex_len);
40e20a69
WD
229 strlcpy(ret->pattern + ex_len, pat, pat_len + 1);
230 pat_len += ex_len;
231
ee35f7fb 232@@ -81,14 +156,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;
ee35f7fb 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 }
ee35f7fb 277@@ -96,22 +197,263 @@ 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);
ee35f7fb 284+ mergelist_cnt--;
d8af8661
WD
285+ }
286 free(ex->pattern);
a55d21aa
WD
287 free(ex);
288 }
289
d8af8661
WD
290-void clear_exclude_list(struct exclude_list_struct *listp)
291+static void clear_exclude_list(struct exclude_list_struct *listp)
a55d21aa 292 {
7cb7ae4e
WD
293- struct exclude_struct *ent, *next;
294-
295- for (ent = listp->head; ent; ent = next) {
296- next = ent->next;
297- free_exclude(ent);
298+ if (listp->tail) {
299+ struct exclude_struct *ent, *next;
bc95f62b 300+ /* Truncate any inherited items from the local list. */
7cb7ae4e 301+ listp->tail->next = NULL;
dc5cce3c 302+ /* Now free everything that is left. */
7cb7ae4e
WD
303+ for (ent = listp->head; ent; ent = next) {
304+ next = ent->next;
305+ free_exclude(ent);
306+ }
524989ae
WD
307 }
308
7cb7ae4e
WD
309 listp->head = listp->tail = NULL;
310 }
a55d21aa 311
db4f43dd 312+/* This returns an expanded (absolute) filename for the merge-file name if
45795ec6 313+ * the name has any slashes in it OR if the parent_dirscan var is True;
db4f43dd 314+ * otherwise it returns the original merge_file name. If the len_ptr value
45795ec6
WD
315+ * is non-NULL the merge_file name is limited by the referenced length
316+ * value and will be updated with the length of the resulting name. We
317+ * always return a name that is null terminated, even if the merge_file
318+ * name was not. */
319+static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr,
320+ unsigned int prefix_skip)
41ebea83
WD
321+{
322+ static char buf[MAXPATHLEN];
323+ char *fn, tmpbuf[MAXPATHLEN];
45795ec6 324+ unsigned int fn_len;
41ebea83 325+
40e20a69 326+ if (!parent_dirscan && *merge_file != '/') {
db4f43dd 327+ /* Return the name unchanged it doesn't have any slashes. */
41ebea83 328+ if (len_ptr) {
db4f43dd
WD
329+ const char *p = merge_file + *len_ptr;
330+ while (--p > merge_file && *p != '/') {}
331+ if (p == merge_file) {
332+ strlcpy(buf, merge_file, *len_ptr + 1);
333+ return buf;
334+ }
335+ } else if (strchr(merge_file, '/') == NULL)
41ebea83
WD
336+ return (char *)merge_file;
337+ }
338+
339+ fn = *merge_file == '/' ? buf : tmpbuf;
340+ if (sanitize_paths) {
45795ec6 341+ const char *r = prefix_skip ? "/" : NULL;
41ebea83
WD
342+ /* null-terminate the name if it isn't already */
343+ if (len_ptr && merge_file[*len_ptr]) {
344+ char *to = fn == buf ? tmpbuf : buf;
345+ strlcpy(to, merge_file, *len_ptr + 1);
346+ merge_file = to;
347+ }
b338da5b 348+ if (!sanitize_path(fn, merge_file, r, dirbuf_depth)) {
45795ec6 349+ rprintf(FERROR, "merge-file name overflows: %s\n",
41ebea83
WD
350+ merge_file);
351+ return NULL;
352+ }
353+ } else {
354+ strlcpy(fn, merge_file, len_ptr ? *len_ptr + 1 : MAXPATHLEN);
20f8d513 355+ clean_fname(fn, 1);
41ebea83
WD
356+ }
357+
41ebea83 358+ fn_len = strlen(fn);
45795ec6
WD
359+ if (fn == buf)
360+ goto done;
361+
362+ if (dirbuf_len + fn_len >= MAXPATHLEN) {
363+ rprintf(FERROR, "merge-file name overflows: %s\n", fn);
41ebea83
WD
364+ return NULL;
365+ }
45795ec6
WD
366+ memcpy(buf, dirbuf + prefix_skip, dirbuf_len - prefix_skip);
367+ memcpy(buf + dirbuf_len - prefix_skip, fn, fn_len + 1);
20f8d513 368+ fn_len = clean_fname(buf, 1);
45795ec6
WD
369+
370+ done:
41ebea83
WD
371+ if (len_ptr)
372+ *len_ptr = fn_len;
41ebea83
WD
373+ return buf;
374+}
375+
45795ec6
WD
376+/* Sets the dirbuf and dirbuf_len values. */
377+void set_excludes_dir(const char *dir, unsigned int dirlen)
40e20a69 378+{
45795ec6
WD
379+ unsigned int len;
380+ if (*dir != '/') {
381+ memcpy(dirbuf, curr_dir, curr_dir_len);
382+ dirbuf[curr_dir_len] = '/';
383+ len = curr_dir_len + 1;
470aa8d5
WD
384+ if (len + dirlen >= MAXPATHLEN)
385+ dirlen = 0;
45795ec6
WD
386+ } else
387+ len = 0;
388+ memcpy(dirbuf + len, dir, dirlen);
389+ dirbuf[dirlen + len] = '\0';
20f8d513 390+ dirbuf_len = clean_fname(dirbuf, 1);
45795ec6
WD
391+ if (dirbuf_len > 1 && dirbuf[dirbuf_len-1] == '.'
392+ && dirbuf[dirbuf_len-2] == '/')
393+ dirbuf_len -= 2;
394+ dirbuf[dirbuf_len++] = '/';
395+ dirbuf[dirbuf_len] = '\0';
b338da5b
WD
396+ if (sanitize_paths)
397+ dirbuf_depth = count_dir_elements(dirbuf + module_dirlen);
40e20a69
WD
398+}
399+
45795ec6 400+/* This routine takes a per-dir merge-file entry and finishes its setup.
db4f43dd
WD
401+ * If the name has a path portion then we check to see if it refers to a
402+ * parent directory of the first transfer dir. If it does, we scan all the
403+ * dirs from that point through the parent dir of the transfer dir looking
45795ec6
WD
404+ * for the per-dir merge-file in each one. */
405+static BOOL setup_merge_file(struct exclude_struct *ex,
406+ struct exclude_list_struct *lp, int flags)
1e476835 407+{
d34d9fad 408+ char buf[MAXPATHLEN];
db4f43dd
WD
409+ char *x, *y, *pat = ex->pattern;
410+ unsigned int len;
56babefa 411+
45795ec6
WD
412+ if (!(x = parse_merge_name(pat, NULL, 0)) || *x != '/')
413+ return 0;
1e476835 414+
db4f43dd
WD
415+ y = strrchr(x, '/');
416+ *y = '\0';
417+ ex->pattern = strdup(y+1);
db4f43dd
WD
418+ if (!*x)
419+ x = "/";
420+ if (*x == '/')
421+ strlcpy(buf, x, MAXPATHLEN);
d34d9fad 422+ else
db4f43dd 423+ pathjoin(buf, MAXPATHLEN, dirbuf, x);
d34d9fad 424+
20f8d513 425+ len = clean_fname(buf, 1);
db4f43dd
WD
426+ if (len != 1 && len < MAXPATHLEN-1) {
427+ buf[len++] = '/';
428+ buf[len] = '\0';
429+ }
db4f43dd 430+ /* This ensures that the specified dir is a parent of the transfer. */
45795ec6 431+ for (x = buf, y = dirbuf; *x && *x == *y; x++, y++) {}
d34d9fad 432+ if (*x)
db4f43dd 433+ y += strlen(y); /* nope -- skip the scan */
d34d9fad 434+
45795ec6 435+ parent_dirscan = True;
d34d9fad
WD
436+ while (*y) {
437+ char save[MAXPATHLEN];
41ebea83 438+ strlcpy(save, y, MAXPATHLEN);
db4f43dd 439+ *y = '\0';
45795ec6 440+ dirbuf_len = y - dirbuf;
db4f43dd 441+ strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf));
40e20a69 442+ add_exclude_file(lp, buf, flags | XFLG_ABS_PATH);
147a9095
WD
443+ if (ex->match_flags & MATCHFLG_NO_INHERIT)
444+ lp->head = NULL;
d34d9fad 445+ lp->tail = NULL;
41ebea83 446+ strlcpy(y, save, MAXPATHLEN);
d34d9fad 447+ while ((*x++ = *y++) != '/') {}
1e476835 448+ }
45795ec6 449+ parent_dirscan = False;
db4f43dd 450+ free(pat);
45795ec6 451+ return 1;
1e476835
WD
452+}
453+
db4f43dd 454+/* Each time rsync changes to a new directory it call this function to
45795ec6
WD
455+ * handle all the per-dir merge-files. The "dir" value is the current path
456+ * relative to curr_dir (which might not be null-terminated). We copy it
457+ * into dirbuf so that we can easily append a file name on the end. */
40e20a69 458+void *push_local_excludes(const char *dir, unsigned int dirlen)
a55d21aa 459+{
ee35f7fb 460+ struct exclude_list_struct *ap, *push;
eabda998
WD
461+ int i;
462+
45795ec6 463+ set_excludes_dir(dir, dirlen);
eabda998 464+
ee35f7fb
WD
465+ push = new_array(struct exclude_list_struct, mergelist_cnt);
466+ if (!push)
eabda998 467+ out_of_memory("push_local_excludes");
524989ae 468+
ee35f7fb 469+ for (i = 0, ap = push; i < mergelist_cnt; i++) {
eabda998
WD
470+ memcpy(ap++, mergelist_parents[i]->u.mergelist,
471+ sizeof (struct exclude_list_struct));
472+ }
473+
474+ /* Note: add_exclude_file() might increase mergelist_cnt, so keep
475+ * this loop separate from the above loop. */
476+ for (i = 0; i < mergelist_cnt; i++) {
477+ struct exclude_struct *ex = mergelist_parents[i];
478+ struct exclude_list_struct *lp = ex->u.mergelist;
147a9095 479+ int flags = 0;
524989ae
WD
480+
481+ if (verbose > 2) {
ee35f7fb 482+ rprintf(FINFO, "[%s] pushing exclude list%s\n",
d8af8661 483+ who_am_i(), lp->debug_type);
524989ae 484+ }
d8af8661 485+
40e20a69 486+ lp->tail = NULL; /* Switch any local rules to inherited. */
147a9095
WD
487+ if (ex->match_flags & MATCHFLG_NO_INHERIT)
488+ lp->head = NULL;
489+ if (ex->match_flags & MATCHFLG_WORD_SPLIT)
490+ flags |= XFLG_WORD_SPLIT;
491+ if (ex->match_flags & MATCHFLG_NO_PREFIXES)
492+ flags |= XFLG_NO_PREFIXES;
493+ if (ex->match_flags & MATCHFLG_INCLUDE)
494+ flags |= XFLG_DEF_INCLUDE;
495+ else if (ex->match_flags & MATCHFLG_NO_PREFIXES)
496+ flags |= XFLG_DEF_EXCLUDE;
d34d9fad 497+
40e20a69
WD
498+ if (ex->match_flags & MATCHFLG_FINISH_SETUP) {
499+ ex->match_flags &= ~MATCHFLG_FINISH_SETUP;
45795ec6
WD
500+ if (setup_merge_file(ex, lp, flags))
501+ set_excludes_dir(dir, dirlen);
d34d9fad
WD
502+ }
503+
45795ec6
WD
504+ if (strlcpy(dirbuf + dirbuf_len, ex->pattern,
505+ MAXPATHLEN - dirbuf_len) < MAXPATHLEN - dirbuf_len)
40e20a69 506+ add_exclude_file(lp, dirbuf, flags | XFLG_ABS_PATH);
524989ae 507+ else {
a55d21aa
WD
508+ io_error |= IOERR_GENERAL;
509+ rprintf(FINFO,
510+ "cannot add local excludes in long-named directory %s\n",
d8af8661 511+ full_fname(dirbuf));
a55d21aa 512+ }
45795ec6 513+ dirbuf[dirbuf_len] = '\0';
a55d21aa
WD
514+ }
515+
524989ae 516+ return (void*)push;
a55d21aa
WD
517+}
518+
eabda998 519+void pop_local_excludes(void *mem)
a55d21aa 520+{
ee35f7fb 521+ struct exclude_list_struct *ap, *pop = (struct exclude_list_struct*)mem;
eabda998 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 528+ if (verbose > 2) {
ee35f7fb 529+ rprintf(FINFO, "[%s] popping exclude list%s\n",
d8af8661 530+ who_am_i(), lp->debug_type);
524989ae 531+ }
d8af8661
WD
532+
533+ clear_exclude_list(lp);
a55d21aa 534+ }
d8af8661 535+
ee35f7fb 536+ for (i = 0, ap = pop; i < mergelist_cnt; i++) {
eabda998
WD
537+ memcpy(mergelist_parents[i]->u.mergelist, ap++,
538+ sizeof (struct exclude_list_struct));
539+ }
d8af8661 540+
d8af8661 541+ free(pop);
7cb7ae4e
WD
542+}
543+
a55d21aa 544 static int check_one_exclude(char *name, struct exclude_struct *ex,
7cb7ae4e
WD
545 int name_is_dir)
546 {
ee35f7fb 547@@ -125,13 +467,14 @@ static int check_one_exclude(char *name,
bc95f62b
WD
548 /* If the pattern does not have any slashes AND it does not have
549 * a "**" (which could match a slash), then we just match the
550 * name portion of the path. */
551- if (!ex->slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
552+ if (!ex->u.slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
553 if ((p = strrchr(name,'/')) != NULL)
554 name = p+1;
555 }
3d3aaf9f
WD
556 else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/'
557- && curr_dir[1]) {
558- pathjoin(full_name, sizeof full_name, curr_dir + 1, name);
559+ && curr_dir_len > module_dirlen + 1) {
560+ pathjoin(full_name, sizeof full_name,
561+ curr_dir + module_dirlen + 1, name);
045caa90
WD
562 name = full_name;
563 }
564
ee35f7fb 565@@ -148,9 +491,9 @@ static int check_one_exclude(char *name,
bc95f62b
WD
566 if (ex->match_flags & MATCHFLG_WILD) {
567 /* A non-anchored match with an infix slash and no "**"
568 * needs to match the last slash_cnt+1 name elements. */
0c7d1fd8
WD
569- if (!match_start && ex->slash_cnt
570+ if (!match_start && ex->u.slash_cnt
571 && !(ex->match_flags & MATCHFLG_WILD2)) {
bc95f62b
WD
572- int cnt = ex->slash_cnt + 1;
573+ int cnt = ex->u.slash_cnt + 1;
574 for (p = name + strlen(name) - 1; p >= name; p--) {
575 if (*p == '/' && !--cnt)
576 break;
ee35f7fb
WD
577@@ -202,12 +545,11 @@ static void report_exclude_result(char c
578 * case we add it back in here. */
579
580 if (verbose >= 2) {
581- rprintf(FINFO, "[%s] %scluding %s %s because of %spattern %s%s\n",
582+ rprintf(FINFO, "[%s] %scluding %s %s because of pattern %s%s%s\n",
583 who_am_i(),
584 ent->match_flags & MATCHFLG_INCLUDE ? "in" : "ex",
585- name_is_dir ? "directory" : "file", name, type,
586- ent->pattern,
587- ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "");
588+ name_is_dir ? "directory" : "file", name, ent->pattern,
589+ ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "", type);
590 }
591 }
592
593@@ -221,6 +563,13 @@ int check_exclude(struct exclude_list_st
a55d21aa
WD
594 struct exclude_struct *ent;
595
596 for (ent = listp->head; ent; ent = ent->next) {
524989ae 597+ if (ent->match_flags & MATCHFLG_MERGE_FILE) {
eabda998 598+ int rc = check_exclude(ent->u.mergelist, name,
d8af8661 599+ name_is_dir);
a55d21aa
WD
600+ if (rc)
601+ return rc;
602+ continue;
603+ }
604 if (check_one_exclude(name, ent, name_is_dir)) {
605 report_exclude_result(name, ent, name_is_dir,
606 listp->debug_type);
ee35f7fb 607@@ -236,32 +585,102 @@ int check_exclude(struct exclude_list_st
147a9095
WD
608 * be '\0' terminated, so use the returned length to limit the string.
609 * Also, be sure to add this length to the returned pointer before passing
610 * it back to ask for the next token. This routine parses the "!" (list-
611- * clearing) token and (if xflags does NOT contain XFLG_WORDS_ONLY) the
612+ * clearing) token and (if xflags does NOT contain XFLG_NO_PREFIXES) the
613 * +/- prefixes for overriding the include/exclude mode. The *flag_ptr
614 * value will also be set to the MATCHFLG_* bits for the current token.
615 */
ee35f7fb
WD
616-static const char *get_exclude_tok(const char *p, unsigned int *len_ptr,
617- unsigned int *flag_ptr, int xflags)
618+static const char *get_exclude_tok(const char *p, int xflags,
619+ unsigned int *len_ptr, unsigned int *flag_ptr)
147a9095
WD
620 {
621 const unsigned char *s = (const unsigned char *)p;
622 unsigned int len, mflags = 0;
ee35f7fb 623+ int empty_pat_is_OK = 0;
147a9095
WD
624
625 if (xflags & XFLG_WORD_SPLIT) {
626 /* Skip over any initial whitespace. */
ee35f7fb
WD
627 while (isspace(*s))
628 s++;
629- /* Update for "!" check. */
630+ /* Update to point to real start of rule. */
a55d21aa
WD
631 p = (const char *)s;
632 }
ee35f7fb
WD
633+ if (!*s)
634+ return NULL;
a55d21aa
WD
635
636- /* Is this a '+' or '-' followed by a space (not whitespace)? */
2e064775 637- if (!(xflags & XFLG_WORDS_ONLY)
ee35f7fb 638+ /* Figure out what kind of a filter rule "s" is pointing at. */
147a9095
WD
639+ if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE))) {
640+ char *mods = "";
641+ switch (*s) {
642+ case ':':
643+ mflags |= MATCHFLG_PERDIR_MERGE
644+ | MATCHFLG_FINISH_SETUP;
645+ /* FALL THROUGH */
646+ case '.':
647+ mflags |= MATCHFLG_MERGE_FILE;
648+ mods = "-+Cens";
649+ break;
650+ case '+':
651+ mflags |= MATCHFLG_INCLUDE;
652+ break;
653+ case '-':
654+ break;
655+ case '!':
ee35f7fb
WD
656+ mflags |= MATCHFLG_CLEAR_LIST;
657+ mods = NULL;
147a9095 658+ break;
147a9095 659+ default:
ee35f7fb 660+ rprintf(FERROR, "Unknown filter rule: %s\n", p);
6e313719
WD
661+ exit_cleanup(RERR_SYNTAX);
662+ }
ee35f7fb 663+ while (mods && *++s && *s != ' ' && *s != '=' && *s != '_') {
147a9095 664+ if (strchr(mods, *s) == NULL) {
ee35f7fb
WD
665+ if (xflags & XFLG_WORD_SPLIT && isspace(*s)) {
666+ s--;
667+ break;
668+ }
147a9095 669+ rprintf(FERROR,
ee35f7fb 670+ "unknown option '%c' in filter rule: %s\n",
147a9095
WD
671+ *s, p);
672+ exit_cleanup(RERR_SYNTAX);
673+ }
2e064775 674+ switch (*s) {
147a9095
WD
675+ case '-':
676+ mflags |= MATCHFLG_NO_PREFIXES;
677+ break;
678+ case '+':
679+ mflags |= MATCHFLG_NO_PREFIXES
680+ | MATCHFLG_INCLUDE;
681+ break;
6e313719 682+ case 'C':
ee35f7fb 683+ empty_pat_is_OK = 1;
147a9095
WD
684+ mflags |= MATCHFLG_NO_PREFIXES
685+ | MATCHFLG_WORD_SPLIT
686+ | MATCHFLG_NO_INHERIT;
6e313719 687+ break;
147a9095 688+ case 'e':
6e313719
WD
689+ mflags |= MATCHFLG_EXCLUDE_SELF;
690+ break;
147a9095
WD
691+ case 'n':
692+ mflags |= MATCHFLG_NO_INHERIT;
2e064775 693+ break;
147a9095
WD
694+ case 's':
695+ mflags |= MATCHFLG_WORD_SPLIT;
2e064775 696+ break;
d34d9fad 697+ }
a55d21aa 698+ }
6e313719
WD
699+ if (*s)
700+ s++;
147a9095
WD
701+ } else if (!(xflags & XFLG_NO_PREFIXES)
702 && (*s == '-' || *s == '+') && s[1] == ' ') {
703 if (*s == '+')
704 mflags |= MATCHFLG_INCLUDE;
705 s += 2;
ee35f7fb
WD
706- } else if (xflags & XFLG_DEF_INCLUDE)
707- mflags |= MATCHFLG_INCLUDE;
708+ } else {
709+ if (xflags & XFLG_DEF_INCLUDE)
710+ mflags |= MATCHFLG_INCLUDE;
711+ if (*s == '!')
712+ mflags |= MATCHFLG_CLEAR_LIST; /* Tentative! */
713+ }
147a9095 714+
2e064775 715 if (xflags & XFLG_DIRECTORY)
147a9095
WD
716 mflags |= MATCHFLG_DIRECTORY;
717
ee35f7fb 718@@ -274,8 +693,21 @@ static const char *get_exclude_tok(const
6e313719
WD
719 } else
720 len = strlen(s);
40e20a69 721
ee35f7fb
WD
722- if (*p == '!' && len == 1)
723- mflags |= MATCHFLG_CLEAR_LIST;
724+ if (mflags & MATCHFLG_CLEAR_LIST) {
725+ if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE)) && len) {
726+ rprintf(FERROR,
727+ "'!' rule has trailing characters: %s\n", p);
728+ exit_cleanup(RERR_SYNTAX);
729+ }
730+ if (len > 1)
731+ mflags &= ~MATCHFLG_CLEAR_LIST;
732+ } else if (!len && !empty_pat_is_OK) {
733+ rprintf(FERROR, "unexpected end of filter rule: %s\n", p);
147a9095 734+ exit_cleanup(RERR_SYNTAX);
6e313719
WD
735+ }
736+
40e20a69
WD
737+ if (xflags & XFLG_ABS_PATH)
738+ mflags |= MATCHFLG_ABS_PATH;
739
740 *len_ptr = len;
741 *flag_ptr = mflags;
ee35f7fb 742@@ -287,35 +719,71 @@ void add_exclude(struct exclude_list_str
45795ec6
WD
743 int xflags)
744 {
745 unsigned int pat_len, mflags;
746- const char *cp;
747+ const char *cp, *p;
748
749 if (!pattern)
750 return;
ee35f7fb
WD
751
752- cp = pattern;
753- pat_len = 0;
045caa90 754 while (1) {
ee35f7fb
WD
755- cp = get_exclude_tok(cp + pat_len, &pat_len, &mflags, xflags);
756- if (!pat_len)
045caa90 757+ /* Remember that the returned string is NOT '\0' terminated! */
ee35f7fb
WD
758+ cp = get_exclude_tok(pattern, xflags, &pat_len, &mflags);
759+ if (!cp)
045caa90 760 break;
41ebea83
WD
761+ if (pat_len >= MAXPATHLEN) {
762+ rprintf(FERROR, "discarding over-long exclude: %s\n",
763+ cp);
764+ continue;
765+ }
ee35f7fb 766+ pattern = cp + pat_len;
41ebea83
WD
767
768 if (mflags & MATCHFLG_CLEAR_LIST) {
769 if (verbose > 2) {
ee35f7fb
WD
770 rprintf(FINFO,
771- "[%s] clearing %sexclude list\n",
772+ "[%s] clearing exclude list%s\n",
773 who_am_i(), listp->debug_type);
774 }
775 clear_exclude_list(listp);
0c7d1fd8
WD
776 continue;
777 }
a55d21aa 778
d34d9fad
WD
779- make_exclude(listp, cp, pat_len, mflags);
780-
781- if (verbose > 2) {
782- rprintf(FINFO, "[%s] add_exclude(%.*s, %s%sclude)\n",
783- who_am_i(), (int)pat_len, cp, listp->debug_type,
784- mflags & MATCHFLG_INCLUDE ? "in" : "ex");
ee35f7fb
WD
785+ if (!pat_len) {
786+ cp = ".cvsignore";
787+ pat_len = 10;
788+ }
789+
524989ae 790+ if (mflags & MATCHFLG_MERGE_FILE) {
45795ec6 791+ unsigned int len = pat_len;
6e313719
WD
792+ if (mflags & MATCHFLG_EXCLUDE_SELF) {
793+ const char *name = strrchr(cp, '/');
794+ if (name)
795+ len -= ++name - cp;
796+ else
797+ name = cp;
798+ make_exclude(listp, name, len, 0);
799+ mflags &= ~MATCHFLG_EXCLUDE_SELF;
800+ len = pat_len;
801+ }
41ebea83 802+ if (mflags & MATCHFLG_PERDIR_MERGE) {
40e20a69 803+ if (parent_dirscan) {
45795ec6 804+ if (!(p = parse_merge_name(cp, &len, module_dirlen)))
41ebea83 805+ continue;
45795ec6 806+ make_exclude(listp, p, len, mflags);
d34d9fad
WD
807+ continue;
808+ }
41ebea83 809+ } else {
6e313719 810+ int flgs = XFLG_FATAL_ERRORS;
45795ec6 811+ if (!(p = parse_merge_name(cp, &len, 0)))
d34d9fad 812+ continue;
6e313719
WD
813+ if (mflags & MATCHFLG_INCLUDE)
814+ flgs |= XFLG_DEF_INCLUDE;
147a9095
WD
815+ else if (mflags & MATCHFLG_NO_PREFIXES)
816+ flgs |= XFLG_DEF_EXCLUDE;
6e313719 817+ add_exclude_file(listp, p, flgs);
41ebea83 818+ continue;
0c7d1fd8 819+ }
a55d21aa 820 }
d34d9fad
WD
821+
822+ make_exclude(listp, cp, pat_len, mflags);
0c7d1fd8 823 }
d34d9fad
WD
824 }
825
ee35f7fb 826@@ -324,7 +792,7 @@ void add_exclude_file(struct exclude_lis
41ebea83
WD
827 int xflags)
828 {
829 FILE *fp;
830- char line[MAXPATHLEN+3]; /* Room for "x " prefix and trailing slash. */
147a9095 831+ char line[MAXPATHLEN+11]; /* Room for prefix chars and trailing slash. */
41ebea83
WD
832 char *eob = line + sizeof line - 1;
833 int word_split = xflags & XFLG_WORD_SPLIT;
834
ee35f7fb 835@@ -338,13 +806,19 @@ void add_exclude_file(struct exclude_lis
6e313719
WD
836 if (!fp) {
837 if (xflags & XFLG_FATAL_ERRORS) {
838 rsyserr(FERROR, errno,
839- "failed to open %s file %s",
840- xflags & XFLG_DEF_INCLUDE ? "include" : "exclude",
ee35f7fb 841- fname);
6e313719
WD
842+ "failed to open %sclude file %s",
843+ xflags & XFLG_DEF_INCLUDE ? "in" : "ex",
ee35f7fb 844+ safe_fname(fname));
6e313719 845 exit_cleanup(RERR_FILEIO);
45795ec6 846 }
c4e46f36
WD
847 return;
848 }
45795ec6
WD
849+ dirbuf[dirbuf_len] = '\0';
850+
c4e46f36 851+ if (verbose > 2) {
1e476835 852+ rprintf(FINFO, "[%s] add_exclude_file(%s,%d)\n",
ee35f7fb 853+ who_am_i(), safe_fname(fname), xflags);
c4e46f36 854+ }
45795ec6 855
c4e46f36
WD
856 while (1) {
857 char *s = line;
ee35f7fb 858@@ -388,7 +862,7 @@ void send_exclude_list(int f)
147a9095
WD
859 * FIXME: This pattern shows up in the output of
860 * report_exclude_result(), which is not ideal. */
861 if (list_only && !recurse)
862- add_exclude(&exclude_list, "/*/*", 0);
863+ add_exclude(&exclude_list, "/*/*", XFLG_DEF_EXCLUDE);
864
865 for (ent = exclude_list.head; ent; ent = ent->next) {
866 unsigned int l;
ee35f7fb 867@@ -402,10 +876,34 @@ void send_exclude_list(int f)
2e064775
WD
868 p[l] = '\0';
869 }
870
871- if (ent->match_flags & MATCHFLG_INCLUDE) {
147a9095 872+ if (ent->match_flags & MATCHFLG_PERDIR_MERGE) {
41ebea83 873+ char buf[32], *op = buf;
147a9095 874+ if (protocol_version < 29) {
ee35f7fb
WD
875+ rprintf(FERROR,
876+ "remote rsync is too old to understand per-directory filter files.\n");
147a9095
WD
877+ exit_cleanup(RERR_SYNTAX);
878+ }
879+ *op++ = ':';
880+ if (ent->match_flags & MATCHFLG_WORD_SPLIT)
881+ *op++ = 's';
882+ if (ent->match_flags & MATCHFLG_NO_INHERIT)
883+ *op++ = 'n';
884+ if (ent->match_flags & MATCHFLG_EXCLUDE_SELF)
885+ *op++ = 'e';
886+ if (ent->match_flags & MATCHFLG_NO_PREFIXES) {
887+ if (ent->match_flags & MATCHFLG_INCLUDE)
888+ *op++ = '+';
889+ else
890+ *op++ = '-';
891+ }
2e064775 892+ *op++ = ' ';
41ebea83
WD
893+ write_int(f, l + (op - buf));
894+ write_buf(f, buf, op - buf);
2e064775 895+ } else if (ent->match_flags & MATCHFLG_INCLUDE) {
a55d21aa 896 write_int(f, l + 2);
2e064775 897 write_buf(f, "+ ", 2);
147a9095
WD
898- } else if (*p == '-' || *p == '+') {
899+ } else if (protocol_version >= 29
900+ || ((*p == '-' || *p == '+') && p[1] == ' ')) {
901 write_int(f, l + 2);
902 write_buf(f, "- ", 2);
903 } else
ee35f7fb 904@@ -419,14 +917,15 @@ void send_exclude_list(int f)
2e064775
WD
905
906 void recv_exclude_list(int f)
907 {
908- char line[MAXPATHLEN+3]; /* Room for "x " prefix and trailing slash. */
147a9095
WD
909+ char line[MAXPATHLEN+11]; /* Room for prefix and trailing slash. */
910+ unsigned int xflags = protocol_version >= 29 ? 0 : XFLG_DEF_EXCLUDE;
2e064775
WD
911 unsigned int l;
912
913 while ((l = read_int(f)) != 0) {
147a9095
WD
914 if (l >= sizeof line)
915 overflow("recv_exclude_list");
916 read_sbuf(f, line, l);
917- add_exclude(&exclude_list, line, 0);
918+ add_exclude(&exclude_list, line, xflags);
919 }
920 }
921
ee35f7fb 922@@ -443,18 +942,18 @@ static char default_cvsignore[] =
147a9095
WD
923
924 void add_cvs_excludes(void)
925 {
926+ static unsigned int cvs_flags = XFLG_WORD_SPLIT | XFLG_NO_PREFIXES
927+ | XFLG_DEF_EXCLUDE;
a55d21aa
WD
928 char fname[MAXPATHLEN];
929 char *p;
930
147a9095
WD
931- add_exclude(&exclude_list, default_cvsignore,
932- XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
933+ add_exclude(&exclude_list, ":C", 0);
934+ add_exclude(&exclude_list, default_cvsignore, cvs_flags);
a55d21aa 935
147a9095
WD
936 if ((p = getenv("HOME"))
937 && pathjoin(fname, sizeof fname, p, ".cvsignore") < sizeof fname) {
938- add_exclude_file(&exclude_list, fname,
939- XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
940+ add_exclude_file(&exclude_list, fname, cvs_flags);
941 }
942
943- add_exclude(&exclude_list, getenv("CVSIGNORE"),
944- XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
945+ add_exclude(&exclude_list, getenv("CVSIGNORE"), cvs_flags);
946 }
37da98ae 947--- orig/flist.c 2005-01-01 21:11:00
20f8d513 948+++ flist.c 2004-08-12 18:59:28
a6587818 949@@ -40,10 +40,9 @@ extern int module_id;
a55d21aa
WD
950 extern int ignore_errors;
951 extern int numeric_ids;
952
953-extern int cvs_exclude;
954-
955 extern int recurse;
956 extern char curr_dir[MAXPATHLEN];
d34d9fad 957+extern unsigned int curr_dir_len;
a55d21aa 958 extern char *files_from;
d34d9fad
WD
959 extern int filesfrom_fd;
960
a6587818 961@@ -67,7 +66,6 @@ extern int list_only;
a55d21aa
WD
962
963 extern struct exclude_list_struct exclude_list;
964 extern struct exclude_list_struct server_exclude_list;
965-extern struct exclude_list_struct local_exclude_list;
966
967 int io_error;
968
a6587818 969@@ -223,8 +221,6 @@ int link_stat(const char *path, STRUCT_S
a55d21aa
WD
970 */
971 static int check_exclude_file(char *fname, int is_dir, int exclude_level)
972 {
973- int rc;
974-
975 #if 0 /* This currently never happens, so avoid a useless compare. */
976 if (exclude_level == NO_EXCLUDES)
977 return 0;
a6587818 978@@ -246,10 +242,7 @@ static int check_exclude_file(char *fnam
a55d21aa
WD
979 if (exclude_level != ALL_EXCLUDES)
980 return 0;
981 if (exclude_list.head
982- && (rc = check_exclude(&exclude_list, fname, is_dir)) != 0)
983- return rc < 0;
984- if (local_exclude_list.head
985- && check_exclude(&local_exclude_list, fname, is_dir) < 0)
986+ && check_exclude(&exclude_list, fname, is_dir) < 0)
987 return 1;
988 return 0;
989 }
37da98ae 990@@ -983,15 +976,7 @@ void send_file_name(int f, struct file_l
a55d21aa
WD
991
992 if (recursive && S_ISDIR(file->mode)
993 && !(file->flags & FLAG_MOUNT_POINT)) {
994- struct exclude_list_struct last_list = local_exclude_list;
995- local_exclude_list.head = local_exclude_list.tail = NULL;
996 send_directory(f, flist, f_name_to(file, fbuf));
7cb7ae4e
WD
997- if (verbose > 2) {
998- rprintf(FINFO, "[%s] popping %sexclude list\n",
999- who_am_i(), local_exclude_list.debug_type);
1000- }
d8af8661 1001- clear_exclude_list(&local_exclude_list);
a55d21aa
WD
1002- local_exclude_list = last_list;
1003 }
1004 }
1005
37da98ae 1006@@ -1002,6 +987,7 @@ static void send_directory(int f, struct
a55d21aa
WD
1007 struct dirent *di;
1008 char fname[MAXPATHLEN];
1009 unsigned int offset;
1010+ void *save_excludes;
1011 char *p;
1012
1013 d = opendir(dir);
37da98ae 1014@@ -1025,18 +1011,7 @@ static void send_directory(int f, struct
a55d21aa
WD
1015 offset++;
1016 }
1017
1018- if (cvs_exclude) {
1019- if (strlcpy(p, ".cvsignore", MAXPATHLEN - offset)
1020- < MAXPATHLEN - offset) {
1021- add_exclude_file(&local_exclude_list, fname,
ee1af13c 1022- XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
a55d21aa
WD
1023- } else {
1024- io_error |= IOERR_GENERAL;
1025- rprintf(FINFO,
1026- "cannot cvs-exclude in long-named directory %s\n",
1027- full_fname(fname));
1028- }
045caa90 1029- }
045caa90
WD
1030+ save_excludes = push_local_excludes(fname, offset);
1031
a55d21aa
WD
1032 for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
1033 char *dname = d_name(di);
37da98ae 1034@@ -1057,6 +1032,8 @@ static void send_directory(int f, struct
bc95f62b 1035 rsyserr(FERROR, errno, "readdir(%s)", dir);
a55d21aa 1036 }
a55d21aa 1037
eabda998
WD
1038+ pop_local_excludes(save_excludes);
1039+
a55d21aa
WD
1040 closedir(d);
1041 }
eabda998 1042
37da98ae 1043@@ -1076,6 +1053,7 @@ struct file_list *send_file_list(int f,
d34d9fad
WD
1044 char *p, *dir, olddir[sizeof curr_dir];
1045 char lastpath[MAXPATHLEN] = "";
1046 struct file_list *flist;
45795ec6 1047+ BOOL need_first_push = True;
d34d9fad
WD
1048 int64 start_write;
1049 int use_ff_fd = 0;
1050
37da98ae 1051@@ -1096,6 +1074,10 @@ struct file_list *send_file_list(int f,
d34d9fad
WD
1052 exit_cleanup(RERR_FILESELECT);
1053 }
1054 use_ff_fd = 1;
1055+ if (curr_dir_len < MAXPATHLEN - 1) {
40e20a69 1056+ push_local_excludes(curr_dir, curr_dir_len);
45795ec6 1057+ need_first_push = False;
1e476835 1058+ }
d34d9fad
WD
1059 }
1060 }
1061
37da98ae 1062@@ -1126,6 +1108,15 @@ struct file_list *send_file_list(int f,
d34d9fad
WD
1063 }
1064 }
1065
45795ec6 1066+ if (need_first_push) {
d34d9fad 1067+ if ((p = strrchr(fname, '/')) != NULL) {
45795ec6
WD
1068+ if (*++p && strcmp(p, ".") != 0)
1069+ push_local_excludes(fname, p - fname);
1070+ } else if (strcmp(fname, ".") != 0)
1071+ push_local_excludes(fname, 0);
1072+ need_first_push = False;
d34d9fad 1073+ }
1e476835 1074+
d34d9fad
WD
1075 if (link_stat(fname, &st, keep_dirlinks) != 0) {
1076 if (f != -1) {
1077 io_error |= IOERR_GENERAL;
147a9095
WD
1078--- orig/loadparm.c 2005-01-01 21:11:00
1079+++ loadparm.c 2005-01-16 19:48:52
1080@@ -133,6 +133,7 @@ typedef struct
1081 char *auth_users;
1082 char *secrets_file;
1083 BOOL strict_modes;
1084+ char *filter;
1085 char *exclude;
1086 char *exclude_from;
1087 char *include;
1088@@ -175,6 +176,7 @@ static service sDefault =
1089 NULL, /* auth users */
1090 NULL, /* secrets file */
1091 True, /* strict modes */
1092+ NULL, /* filter */
1093 NULL, /* exclude */
1094 NULL, /* exclude from */
1095 NULL, /* include */
1096@@ -294,6 +296,7 @@ static struct parm_struct parm_table[] =
1097 {"auth users", P_STRING, P_LOCAL, &sDefault.auth_users, NULL, 0},
1098 {"secrets file", P_STRING, P_LOCAL, &sDefault.secrets_file,NULL, 0},
1099 {"strict modes", P_BOOL, P_LOCAL, &sDefault.strict_modes,NULL, 0},
1100+ {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL, 0},
1101 {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL, 0},
1102 {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from,NULL, 0},
1103 {"include", P_STRING, P_LOCAL, &sDefault.include, NULL, 0},
1104@@ -378,6 +381,7 @@ FN_LOCAL_STRING(lp_hosts_deny, hosts_den
1105 FN_LOCAL_STRING(lp_auth_users, auth_users)
1106 FN_LOCAL_STRING(lp_secrets_file, secrets_file)
1107 FN_LOCAL_BOOL(lp_strict_modes, strict_modes)
1108+FN_LOCAL_STRING(lp_filter, filter)
1109 FN_LOCAL_STRING(lp_exclude, exclude)
1110 FN_LOCAL_STRING(lp_exclude_from, exclude_from)
1111 FN_LOCAL_STRING(lp_include, include)
6e313719 1112--- orig/options.c 2005-01-15 21:23:15
147a9095 1113+++ options.c 2005-01-16 23:34:15
6e313719
WD
1114@@ -144,6 +144,7 @@ int list_only = 0;
1115 char *batch_name = NULL;
1116
1117 static int daemon_opt; /* sets am_daemon after option error-reporting */
147a9095 1118+static int F_option_cnt = 0;
6e313719
WD
1119 static int modify_window_set;
1120 static char *dest_option = NULL;
1121 static char *max_size_arg;
147a9095
WD
1122@@ -291,6 +292,9 @@ void usage(enum logcode F)
1123 rprintf(F," -P equivalent to --partial --progress\n");
1124 rprintf(F," -z, --compress compress file data\n");
1125 rprintf(F," -C, --cvs-exclude auto ignore files in the same way CVS does\n");
1126+ rprintf(F," -f, --filter=RULE add a file-filtering RULE\n");
1127+ rprintf(F," -F same as --filter=': /.rsync-rules'\n");
1128+ rprintf(F," repeated: --filter='- .rsync-rules'\n");
1129 rprintf(F," --exclude=PATTERN exclude files matching PATTERN\n");
6e313719 1130 rprintf(F," --exclude-from=FILE exclude patterns listed in FILE\n");
40e20a69 1131 rprintf(F," --include=PATTERN don't exclude files matching PATTERN\n");
147a9095
WD
1132@@ -320,7 +324,7 @@ void usage(enum logcode F)
1133 }
1134
1135 enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
1136- OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED,
1137+ OPT_FILTER, OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED,
1138 OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST,
1139 OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW,
1140 OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_TIMEOUT, OPT_MAX_SIZE,
1141@@ -343,6 +347,7 @@ static struct poptOption long_options[]
1142 {"delete-excluded", 0, POPT_ARG_NONE, 0, OPT_DELETE_EXCLUDED, 0, 0 },
1143 {"force", 0, POPT_ARG_NONE, &force_delete, 0, 0, 0 },
1144 {"numeric-ids", 0, POPT_ARG_NONE, &numeric_ids, 0, 0, 0 },
1145+ {"filter", 'f', POPT_ARG_STRING, 0, OPT_FILTER, 0, 0 },
1146 {"exclude", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE, 0, 0 },
1147 {"include", 0, POPT_ARG_STRING, 0, OPT_INCLUDE, 0, 0 },
1148 {"exclude-from", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE_FROM, 0, 0 },
1149@@ -393,6 +398,7 @@ static struct poptOption long_options[]
d34d9fad
WD
1150 {"ignore-errors", 0, POPT_ARG_NONE, &ignore_errors, 0, 0, 0 },
1151 {"blocking-io", 0, POPT_ARG_VAL, &blocking_io, 1, 0, 0 },
1152 {"no-blocking-io", 0, POPT_ARG_VAL, &blocking_io, 0, 0, 0 },
147a9095 1153+ {0, 'F', POPT_ARG_NONE, 0, 'F', 0, 0 },
5388f859 1154 {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 },
37da98ae 1155 {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
5388f859 1156 {"log-format", 0, POPT_ARG_STRING, &log_format, 0, 0, 0 },
147a9095
WD
1157@@ -622,10 +628,15 @@ int parse_arguments(int *argc, const cha
1158 delete_mode = 1;
1159 break;
1160
1161- case OPT_EXCLUDE:
1162+ case OPT_FILTER:
1163 add_exclude(&exclude_list, poptGetOptArg(pc), 0);
1164 break;
1165
1166+ case OPT_EXCLUDE:
1167+ add_exclude(&exclude_list, poptGetOptArg(pc),
1168+ XFLG_DEF_EXCLUDE);
1169+ break;
1170+
1171 case OPT_INCLUDE:
1172 add_exclude(&exclude_list, poptGetOptArg(pc),
1173 XFLG_DEF_INCLUDE);
1174@@ -643,8 +654,8 @@ int parse_arguments(int *argc, const cha
1175 goto options_rejected;
1176 }
1177 add_exclude_file(&exclude_list, arg, XFLG_FATAL_ERRORS
1178- | (opt == OPT_INCLUDE_FROM
1179- ? XFLG_DEF_INCLUDE : 0));
1180+ | (opt == OPT_INCLUDE_FROM ? XFLG_DEF_INCLUDE
1181+ : XFLG_DEF_EXCLUDE));
1182 break;
1183
1184 case 'h':
1185@@ -668,6 +679,19 @@ int parse_arguments(int *argc, const cha
d34d9fad
WD
1186 am_sender = 1;
1187 break;
1188
147a9095
WD
1189+ case 'F':
1190+ switch (++F_option_cnt) {
6e313719
WD
1191+ case 1:
1192+ add_exclude(&exclude_list,
147a9095 1193+ ": /.rsync-rules", 0);
6e313719
WD
1194+ break;
1195+ case 2:
1196+ add_exclude(&exclude_list,
147a9095 1197+ "- .rsync-rules", 0);
6e313719
WD
1198+ break;
1199+ }
d34d9fad 1200+ break;
1e476835 1201+
d34d9fad
WD
1202 case 'P':
1203 do_progress = 1;
1204 keep_partial = 1;
147a9095
WD
1205@@ -972,7 +996,7 @@ int parse_arguments(int *argc, const cha
1206 partial_dir = NULL;
1207 else if (*partial_dir != '/') {
1208 add_exclude(&exclude_list, partial_dir,
1209- XFLG_DIRECTORY);
1210+ XFLG_DIRECTORY | XFLG_DEF_EXCLUDE);
1211 }
1212 keep_partial = 1;
1213 }
6e313719 1214--- orig/rsync.h 2005-01-15 21:18:09
147a9095
WD
1215+++ rsync.h 2005-01-17 00:16:04
1216@@ -62,7 +62,7 @@
1217 #define FLAG_MOUNT_POINT (1<<2) /* sender only */
1218
1219 /* update this if you make incompatible changes */
1220-#define PROTOCOL_VERSION 28
1221+#define PROTOCOL_VERSION 29
1222
1223 /* We refuse to interoperate with versions that are not in this range.
1224 * Note that we assume we'll work with later versions: the onus is on
1225@@ -108,9 +108,11 @@
1226
1227 #define XFLG_FATAL_ERRORS (1<<0)
1228 #define XFLG_DEF_INCLUDE (1<<1)
1229-#define XFLG_WORDS_ONLY (1<<2)
4f92d035 1230+#define XFLG_DEF_EXCLUDE (1<<2)
40e20a69 1231 #define XFLG_WORD_SPLIT (1<<3)
a60267ab 1232 #define XFLG_DIRECTORY (1<<4)
4f92d035
WD
1233+#define XFLG_NO_PREFIXES (1<<5)
1234+#define XFLG_ABS_PATH (1<<6)
40e20a69
WD
1235
1236 #define PERMS_REPORT (1<<0)
1237 #define PERMS_SKIP_MTIME (1<<1)
147a9095 1238@@ -512,11 +514,21 @@ struct map_struct {
0c7d1fd8
WD
1239 #define MATCHFLG_INCLUDE (1<<4) /* this is an include, not an exclude */
1240 #define MATCHFLG_DIRECTORY (1<<5) /* this matches only directories */
1241 #define MATCHFLG_CLEAR_LIST (1<<6) /* this item is the "!" token */
4f92d035 1242+#define MATCHFLG_WORD_SPLIT (1<<7) /* split rules on whitespace */
147a9095 1243+#define MATCHFLG_NO_INHERIT (1<<8) /* don't inherit these rules */
4f92d035
WD
1244+#define MATCHFLG_NO_PREFIXES (1<<9) /* parse no prefixes from patterns */
1245+#define MATCHFLG_MERGE_FILE (1<<10)/* specifies a file to merge */
1246+#define MATCHFLG_PERDIR_MERGE (1<<11)/* merge-file is searched per-dir */
1247+#define MATCHFLG_EXCLUDE_SELF (1<<12)/* merge-file name should be excluded */
1248+#define MATCHFLG_FINISH_SETUP (1<<13)/* per-dir merge file needs setup */
a55d21aa
WD
1249 struct exclude_struct {
1250 struct exclude_struct *next;
1251 char *pattern;
0c7d1fd8 1252 unsigned int match_flags;
bc95f62b 1253- int slash_cnt;
bc95f62b
WD
1254+ union {
1255+ int slash_cnt;
eabda998 1256+ struct exclude_list_struct *mergelist;
bc95f62b 1257+ } u;
a55d21aa
WD
1258 };
1259
1260 struct exclude_list_struct {
2e064775 1261--- orig/rsync.yo 2005-01-15 04:36:32
147a9095
WD
1262+++ rsync.yo 2005-01-16 22:49:57
1263@@ -361,6 +361,9 @@ verb(
1264 -P equivalent to --partial --progress
1265 -z, --compress compress file data
1266 -C, --cvs-exclude auto ignore files in the same way CVS does
1267+ -f, --filter=RULE add a file-filtering RULE
1268+ -F same as --filter=': /.rsync-rules'
1269+ repeated: --filter='- .rsync-rules'
1270 --exclude=PATTERN exclude files matching PATTERN
6e313719 1271 --exclude-from=FILE exclude patterns listed in FILE
1e476835 1272 --include=PATTERN don't exclude files matching PATTERN
147a9095
WD
1273@@ -754,14 +757,41 @@ Finally, any file is ignored if it is in
1274 .cvsignore file and matches one of the patterns listed therein.
1275 See the bf(cvs(1)) manual for more information.
1276
1277-dit(bf(--exclude=PATTERN)) This option allows you to selectively exclude
1278-certain files from the list of files to be transferred. This is most
1279-useful in combination with a recursive transfer.
1280+dit(bf(-f, --filter=RULE)) This option allows you to add rules to selectively
1281+exclude certain files from the list of files to be transferred. This is
1282+most useful in combination with a recursive transfer.
6e313719 1283
147a9095
WD
1284-You may use as many --exclude options on the command line as you like
1285+You may use as many --filter options on the command line as you like
1286 to build up the list of files to exclude.
1287
1288-See the EXCLUDE PATTERNS section for detailed information on this option.
1289+See the FILTER RULES section for detailed information on this option.
1290+
1291+dit(bf(-F)) The -F option is a shorthand for adding two --filter rules to
6e313719
WD
1292+your command. The first time it is used is a shorthand for this rule:
1293+
1294+verb(
147a9095 1295+ --filter=': /.rsync-rules'
6e313719
WD
1296+)
1297+
147a9095
WD
1298+This tells rsync to look for per-directory .rsync-rules files that have
1299+been sprinkled through the hierarchy and use their rules to filter the
1300+files in the transfer. If -F is repeated, it is a shorthand for this
1301+rule:
6e313719
WD
1302+
1303+verb(
147a9095 1304+ --filter='- .rsync-rules'
6e313719
WD
1305+)
1306+
147a9095 1307+This filters out the .rsync-rules files themselves from the transfer.
6e313719 1308+
147a9095
WD
1309+See the FILTER RULES section for detailed information on how these options
1310+work.
1311+
1312+dit(bf(--exclude=PATTERN)) This option is a simplified form of the
1313+--filter option that defaults to an exclude rule and does not allow
1314+the full rule-parsing syntax of normal filter rules.
1315+
1316+See the FILTER RULES section for detailed information on this option.
1317
1318 dit(bf(--exclude-from=FILE)) This option is similar to the --exclude
1319 option, but instead it adds all exclude patterns listed in the file
1320@@ -769,11 +799,11 @@ FILE to the exclude list. Blank lines i
1321 ';' or '#' are ignored.
1322 If em(FILE) is bf(-) the list will be read from standard input.
5f98a4b1 1323
147a9095
WD
1324-dit(bf(--include=PATTERN)) This option tells rsync to not exclude the
1325-specified pattern of filenames. This is useful as it allows you to
1326-build up quite complex exclude/include rules.
1327+dit(bf(--include=PATTERN)) This option is a simplified form of the
1328+--filter option that defaults to an include rule and does not allow
1329+the full rule-parsing syntax of normal filter rules.
1330
1331-See the EXCLUDE PATTERNS section for detailed information on this option.
1332+See the FILTER RULES section for detailed information on this option.
1333
1334 dit(bf(--include-from=FILE)) This specifies a list of include patterns
1335 from a file.
1336@@ -818,7 +848,8 @@ was located on the remote "src" host.
1337
1338 dit(bf(-0, --from0)) This tells rsync that the filenames it reads from a
1339 file are terminated by a null ('\0') character, not a NL, CR, or CR+LF.
1340-This affects --exclude-from, --include-from, and --files-from.
1341+This affects --exclude-from, --include-from, --files-from, and any
1342+merged files specified in a --filter rule.
1343 It does not affect --cvs-exclude (since all names read from a .cvsignore
1344 file are split on whitespace).
1345
1346@@ -957,8 +988,8 @@ If the partial-dir value is not an absol
1347 will prevent partial-dir files from being transferred and also prevent the
1348 untimely deletion of partial-dir items on the receiving side. An example:
1349 the above --partial-dir option would add an "--exclude=.rsync-partial/"
1350-rule at the end of any other include/exclude rules. Note that if you are
1351-supplying your own include/exclude rules, you may need to manually insert a
1352+rule at the end of any other filter rules. Note that if you are
1353+supplying your own filter rules, you may need to manually insert a
1354 rule for this directory exclusion somewhere higher up in the list so that
1355 it has a high enough priority to be effective (e.g., if your rules specify
1356 a trailing --exclude=* rule, the auto-added rule will be ineffective).
1357@@ -1108,30 +1139,310 @@ page describing the options available fo
1358
1359 enddit()
1360
1361-manpagesection(EXCLUDE PATTERNS)
1362-
1363-The exclude and include patterns specified to rsync allow for flexible
1364-selection of which files to transfer and which files to skip.
1365+manpagesection(FILTER RULES)
1366
1367-Rsync builds an ordered list of include/exclude options as specified on
5f98a4b1
WD
1368-the command line. Rsync checks each file and directory
1369-name against each exclude/include pattern in turn. The first matching
147a9095
WD
1370-pattern is acted on. If it is an exclude pattern, then that file is
1371-skipped. If it is an include pattern then that filename is not
1372-skipped. If no matching include/exclude pattern is found then the
1373+The filter rules allow for flexible selection of which files to transfer
1374+(include) and which files to skip (exclude). The rules either directly
1375+specify include/exclude patterns or they specify a way to acquire more
1376+include/exclude patterns (e.g. to read them from a file).
5f98a4b1
WD
1377+
1378+As the list of files/directories to transfer is built, rsync checks each
147a9095
WD
1379+name to be transferred against the list of include/exclude patterns in
1380+turn, and t first matching pattern is acted on: if it is an exclude
1381+pattern, then that file is skipped; if it is an include pattern then that
1382+filename is not skipped; if no matching pattern is found, then the
5f98a4b1
WD
1383 filename is not skipped.
1384
1385-The filenames matched against the exclude/include patterns are relative
1386-to the "root of the transfer". If you think of the transfer as a
85c7c40a
WD
1387-subtree of names that are being sent from sender to receiver, the root
1388-is where the tree starts to be duplicated in the destination directory.
1389-This root governs where patterns that start with a / match (see below).
147a9095
WD
1390+Rsync builds an ordered list of filter rules as specified on the
1391+command-line. Filter rules have the following syntax:
1392+
1393+itemize(
1394+ it() x RULE
1395+ it() x=RULE
1396+ it() xMODIFIERS RULE
1397+ it() xMODIFIERS=RULE
1398+ it() !
1399+)
1400+
1401+The 'x' is a single-letter that specifies the kind of rule to create. It
1402+can have trailing options, and is separated from the RULE by either a
4f92d035 1403+single space or an equal-sign. Here are the available rule prefixes:
147a9095
WD
1404+
1405+verb(
1406+ - specifies an exclude pattern.
1407+ + specifies an include pattern.
1408+ . specifies a merge-file to read for more rules.
1409+ : specifies a per-directory merge-file.
1410+ ! clears the current include/exclude list
1411+)
1412+
1413+Note that the include/exclude command-line options do not allow the full
1414+range of rule parsing as described above -- they only allow the
1415+specification of include/exclude patterns and the "!" token (not to
1416+mention the comment lines when reading rules from a file). If a pattern
1417+does not begin with "- " (dash, space) or "+ " (plus, space), then the
1418+rule will be interpreted as if "+ " (for an include option) or "- " (for
4f92d035
WD
1419+an exclude option) were prefixed to the string. A --filter option, on
1420+the other hand, must always contain one of the prefixes above.
147a9095
WD
1421+
1422+Note also that the --filter, --include, and --exclude options take one
1423+rule/pattern each. To add multiple ones, you can repeat the options on
1424+the command-line, use the merge-file syntax of the --filter option, or
1425+the --include-from/--exclude-from options.
1426+
1427+manpagesection(INCLUDE/EXCLUDE PATTERN RULES)
1428+
1429+You can include and exclude files by specifing patterns using the "+" and
1430+"-" filter rules (as introduced in the FILTER RULES section above). These
1431+rules specify a pattern that is matched against the names of the files
1432+that are going to be transferred. These patterns can take several forms:
1433+
1434+itemize(
1435+
5f98a4b1
WD
1436+ it() if the pattern starts with a / then it is anchored to a
1437+ particular spot in the hierarchy of files, otherwise it is matched
1438+ against the end of the pathname. This is similar to a leading ^ in
1439+ regular expressions.
1440+ Thus "/foo" would match a file called "foo" at either the "root of the
85c7c40a 1441+ transfer" (for a global rule) or in the merge-file's directory (for a
5f98a4b1
WD
1442+ per-directory rule).
1443+ An unqualified "foo" would match any file or directory named "foo"
147a9095 1444+ anywhere in the tree because the algorithm is applied recursively from
5f98a4b1 1445+ the
147a9095 1446+ top down; it behaves as if each path component gets a turn at being the
5f98a4b1
WD
1447+ end of the file name. Even the unanchored "sub/foo" would match at
1448+ any point in the hierarchy where a "foo" was found within a directory
147a9095
WD
1449+ named "sub". See the section on ANCHORING INCLUDE/EXCLUDE PATTERNS for
1450+ a full discussion of how to specify a pattern that matches at the root
1451+ of the transfer.
2e064775 1452+
147a9095
WD
1453+ it() if the pattern ends with a / then it will only match a
1454+ directory, not a file, link, or device.
2e064775 1455+
147a9095
WD
1456+ it() if the pattern contains a wildcard character from the set
1457+ *?[ then expression matching is applied using the shell filename
1458+ matching rules. Otherwise a simple string match is used.
2e064775 1459+
147a9095
WD
1460+ it() the double asterisk pattern "**" will match slashes while a
1461+ single asterisk pattern "*" will stop at slashes.
2e064775 1462+
147a9095
WD
1463+ it() if the pattern contains a / (not counting a trailing /) or a "**"
1464+ then it is matched against the full pathname, including any leading
1465+ directories. If the pattern doesn't contain a / or a "**", then it is
1466+ matched only against the final component of the filename. Again,
1467+ remember that the algorithm is applied recursively so "full filename" can
1468+ actually be any portion of a path below the starting directory.
2e064775 1469+
147a9095 1470+)
2e064775 1471+
147a9095
WD
1472+Note that, when using the --recursive (-r) option (which is implied by
1473+-a), every subcomponent of every path is visited from the top down, so
1474+include/exclude patterns get applied recursively to each subcomponent.
1475+The exclude patterns actually short-circuit the directory traversal stage
1476+when rsync finds the files to send. If a pattern excludes a particular
1477+parent directory, it can render a deeper include pattern ineffectual
1478+because rsync did not descend through that excluded section of the
1479+hierarchy. This is particularly important when using a trailing '*' rule.
1480+For instance, this won't work:
6e313719 1481+
147a9095
WD
1482+verb(
1483+ + /some/path/this-file-will-not-be-found
1484+ + /file-is-included
1485+ - *
1486+)
6e313719 1487+
147a9095
WD
1488+This fails because the parent directory "some" is excluded by the '*'
1489+rule, so rsync never visits any of the files in the "some" or "some/path"
1490+directories. One solution is to ask for all directories in the hierarchy
1491+to be included by using a single rule: "+ */" (put it somewhere before the
1492+"- *" rule). Another solution is to add specific include rules for all
1493+the parent dirs that need to be visited. For instance, this set of rules
1494+works fine:
2e064775 1495+
147a9095
WD
1496+verb(
1497+ + /some/
1498+ + /some/path/
1499+ + /some/path/this-file-is-found
1500+ + /file-also-included
1501+ - *
1502+)
6e313719 1503+
147a9095
WD
1504+Here are some examples of exclude/include matching:
1505+
1506+itemize(
1507+ it() "- *.o" would exclude all filenames matching *.o
1508+ it() "- /foo" would exclude a file called foo in the transfer-root directory
1509+ it() "- foo/" would exclude any directory called foo
1510+ it() "- /foo/*/bar" would exclude any file called bar two
1511+ levels below a directory called foo in the transfer-root directory
1512+ it() "- /foo/**/bar" would exclude any file called bar two
1513+ or more levels below a directory called foo in the transfer-root directory
1514+ it() The combination of "+ */", "+ *.c", and "- *" would include all
1515+ directories and C source files but nothing else.
1516+ it() The combination of "+ foo/", "+ foo/bar.c", and "- *" would include
5f98a4b1
WD
1517+ only the foo directory and foo/bar.c (the foo directory must be
1518+ explicitly included or it would be excluded by the "*")
1519+)
1520+
147a9095
WD
1521+manpagesection(MERGE-FILE FILTER RULES)
1522+
1523+You can merge whole files into your filter rules by specifying either a
1524+"." or a ":" filter rule (as introduced in the FILTER RULES section
1525+above).
524989ae 1526+
147a9095
WD
1527+There are two kinds of merged files -- single-instance ('.') and
1528+per-directory (':'). A single-instance merge file is read one time, and
1529+its rules are incorporated into the filter list in the place of the "."
1530+rule. For per-directory merge files, rsync will scan every directory that
1531+it traverses for the named file, merging its contents when the file exists
1532+into the current list of inherited rules. These per-directory rule files
1533+must be created on the sending side because it is the sending side that is
1534+being scanned for the available files to transfer. These rule files may
1535+also need to be transferred to the receiving side if you want them to
1536+affect what files don't get deleted (see PER-DIRECTORY RULES AND DELETE
1537+below).
1538+
1539+Some examples:
d34d9fad 1540+
2e064775 1541+verb(
147a9095
WD
1542+ . /etc/rsync/default.rules
1543+ : .per-dir-rules
1544+ :n- .non-inherited-per-dir-excludes
2e064775 1545+)
d34d9fad 1546+
147a9095 1547+The following modifiers are accepted after the "." or ":":
5f98a4b1 1548+
147a9095
WD
1549+itemize(
1550+ it() A "-" or "+" specifies that the file should consist of only
1551+ exclude (-) or include (+) patterns, with no other rule-parsing except
1552+ for the list-clearing token ("!").
1553+
1554+ it() A "C" is a shorthand for the modifiers "sn-", which makes the
1555+ parsing compatible with the way CVS parses their exclude files. If no
1556+ filename is specified, ".cvsignore" is assumed.
85c7c40a 1557+
147a9095
WD
1558+ it() A "e" will exclude the merge-file from the transfer; e.g.
1559+ ":e\ .rules" is like ":\ .rules" and "-\ .rules".
1560+
1561+ it() An "n" specifies that the rules are not inherited by subdirectories.
1562+
1563+ it() An "s" specifies that the rules are split on all whitespace instead
1564+ of the normal line-splitting. It also turns off comments.
1565+)
85c7c40a 1566+
147a9095
WD
1567+Per-directory rules are inherited in all subdirectories of the directory
1568+where the merge-file was found unless the 'n' modifier was used. Each
1569+subdirectory's rules are prefixed to the inherited per-directory rules
1570+from its parents, which gives the newest rules a higher priority than the
1571+inherited rules. The entire set of per-dir rules is grouped together in
1572+the spot where the merge-file was specified, so it is possible to override
1573+per-dir rules via a rule that got specified earlier in the list of global
1574+rules. When the list-clearing rule ("!") is read from a per-directory
1575+file, it only clears the inherited rules for the current merge file.
1576+
1577+Another way to prevent a single per-dir rule from being inherited is to
1578+anchor it with a leading slash. Anchored rules in a per-directory
1579+merge-file are relative to the merge-file's directory, so a pattern "/foo"
1580+would only match the file "foo" in the directory where the per-dir filter
1581+file was found.
1582+
1583+Here's an example filter file which you'd specify via --filter=". file":
524989ae
WD
1584+
1585+verb(
147a9095
WD
1586+ . /home/user/.global-rules
1587+ - *.gz
1588+ : .rules
524989ae 1589+ + *.[ch]
147a9095 1590+ - *.o
3d3aaf9f
WD
1591+)
1592+
147a9095
WD
1593+This will merge the contents of the /home/user/.global-rules file at the
1594+start of the list and also turns the ".rules" filename into a per-directory
1595+filter file. All rules read-in prior to the start of the directory scan
2e064775
WD
1596+follow the global anchoring rules (i.e. a leading slash matches at the root
1597+of the transfer).
524989ae 1598+
5f98a4b1 1599+If a per-directory merge-file is specified with a path that is a parent
d34d9fad
WD
1600+directory of the first transfer directory, rsync will scan all the parent
1601+dirs from that starting point to the transfer directory for the indicated
147a9095 1602+per-directory file. For instance, here is a common filter (see -F):
d34d9fad
WD
1603+
1604+verb(
147a9095 1605+ --filter=': /.rsync-rules'
d34d9fad
WD
1606+)
1607+
147a9095 1608+That rule tells rsync to scan for the file .rsync-rules in all
6e313719
WD
1609+directories from the root down through the parent directory of the
1610+transfer prior to the start of the normal directory scan of the file in
1611+the directories that are sent as a part of the transfer. (Note: for an
1612+rsync daemon, the root is always the same as the module's "path".)
41ebea83
WD
1613+
1614+Some examples of this pre-scanning for per-directory files:
d34d9fad
WD
1615+
1616+verb(
147a9095
WD
1617+ rsync -avF /src/path/ /dest/dir
1618+ rsync -av --filter=': ../../.rsync-rules' /src/path/ /dest/dir
1619+ rsync -av --fitler=': .rsync-rules' /src/path/ /dest/dir
d34d9fad 1620+)
1e476835 1621+
147a9095 1622+The first two commands above will look for ".rsync-rules" in "/" and
41ebea83 1623+"/src" before the normal scan begins looking for the file in "/src/path"
5f98a4b1 1624+and its subdirectories. The last command avoids the parent-dir scan
147a9095 1625+and only looks for the ".rsync-rules" files in each directory that is
5f98a4b1
WD
1626+a part of the transfer.
1627+
147a9095
WD
1628+If you want to include the contents of a ".cvsignore" in your patterns,
1629+you should use the rule ":C" -- this is a short-hand for the rule
1630+":sn-\ .cvsignore", and ensures that the .cvsignore file's contents are
1631+interpreted according to the same parsing rules that CVS uses. You can
1632+use this to affect where the --cvs-exclude (-C) option's inclusion of the
1633+per-directory .cvsignore file gets placed into your rules by putting a
1634+":C" wherever you like in your filter rules. Without this, rsync would
1635+add the per-dir rule for the .cvignore file at the end of all your other
1636+rules (giving it a lower priority than your command-line rules). For
1637+example:
5f98a4b1
WD
1638+
1639+verb(
147a9095 1640+ cat <<EOT | rsync -avC --filter='. -' a/ b
6e313719 1641+ + foo.o
147a9095
WD
1642+ :C
1643+ - *.old
1644+ EOT
6e313719 1645+
147a9095 1646+ rsync -avC --include=foo.o -f :C --exclude='*.old' a/ b
5f98a4b1
WD
1647+)
1648+
6e313719
WD
1649+Both of the above rsync commands are identical. Each one will merge all
1650+the per-directory .cvsignore rules in the middle of the list rather than
1651+at the end. This allows their dir-specific rules to supersede the rules
147a9095 1652+that follow the :C instead of being subservient to all your rules. (The
6e313719 1653+global rules taken from the $HOME/.cvsignore file and from $CVSIGNORE are
147a9095
WD
1654+not repositioned from their spot at the end of your rules, however -- feel
1655+free to manually include $HOME/.cvsignore elsewhere in your rules.)
524989ae 1656+
147a9095 1657+manpagesection(LIST-CLEARING FILTER RULE)
3d3aaf9f 1658+
147a9095
WD
1659+You can clear the current include/exclude list by using the "!" filter
1660+rule (as introduced in the FILTER RULES section above). The "current"
1661+list is either the global list of rules (if the rule is encountered while
1662+parsing the filter options) or a set of per-directory rules (which are
1663+inherited in their own sub-list, so a subdirectory can use this to clear
1664+out the parent's rules).
1665+
1666+manpagesection(ANCHORING INCLUDE/EXCLUDE PATTERNS)
3d3aaf9f 1667+
147a9095
WD
1668+As mentioned earlier, global include/exclude patterns are anchored at the
1669+"root of the transfer" (as opposed to per-directory patterns, which are
1670+anchored at the merge-file's directory). If you think of the transfer as
1671+a subtree of names that are being sent from sender to receiver, the
1672+transfer-root is where the tree starts to be duplicated in the destination
1673+directory. This root governs where patterns that start with a / match.
1674
1675 Because the matching is relative to the transfer-root, changing the
1676 trailing slash on a source path or changing your use of the --relative
1677 option affects the path you need to use in your matching (in addition to
1678 changing how much of the file tree is duplicated on the destination
1679-system). The following examples demonstrate this.
1680+host). The following examples demonstrate this.
1681
1682 Let's say that we want to match two source files, one with an absolute
1683 path of "/home/me/foo/bar", and one with a path of "/home/you/bar/baz".
1684@@ -1163,114 +1474,59 @@ verb(
1685 Target file: /dest/you/bar/baz
1686 )
1687
1688-The easiest way to see what name you should include/exclude is to just
1689+The easiest way to see what name you should filter is to just
1690 look at the output when using --verbose and put a / in front of the name
1691 (use the --dry-run option if you're not yet ready to copy any files).
1692
1693-Note that, when using the --recursive (-r) option (which is implied by -a),
1694-every subcomponent of
1695-every path is visited from the top down, so include/exclude patterns get
1696-applied recursively to each subcomponent.
1697-The exclude patterns actually short-circuit the directory traversal stage
1698-when rsync finds the files to send. If a pattern excludes a particular
1699-parent directory, it can render a deeper include pattern ineffectual
1700-because rsync did not descend through that excluded section of the
1701-hierarchy.
1702-
1703-Note also that the --include and --exclude options take one pattern
1704-each. To add multiple patterns use the --include-from and
1705---exclude-from options or multiple --include and --exclude options.
1706-
1707-The patterns can take several forms. The rules are:
1708-
1709-itemize(
1710-
1711- it() if the pattern starts with a / then it is matched against the
1712- start of the filename, otherwise it is matched against the end of
1713- the filename.
1714- This is the equivalent of a leading ^ in regular expressions.
1715- Thus "/foo" would match a file called "foo" at the transfer-root
1716- (see above for how this is different from the filesystem-root).
1717- On the other hand, "foo" would match any file called "foo"
1718- anywhere in the tree because the algorithm is applied recursively from
1719- top down; it behaves as if each path component gets a turn at being the
1720- end of the file name.
1721-
1722- it() if the pattern ends with a / then it will only match a
1723- directory, not a file, link, or device.
1724-
1725- it() if the pattern contains a wildcard character from the set
1726- *?[ then expression matching is applied using the shell filename
1727- matching rules. Otherwise a simple string match is used.
1728+manpagesection(PER-DIRECTORY RULES AND DELETE)
1729
1730- it() the double asterisk pattern "**" will match slashes while a
1731- single asterisk pattern "*" will stop at slashes.
1732+Without a delete option, per-directory rules are only relevant on the
1733+sending side, so you can feel free to exclude the merge files themselves
1734+without affecting the transfer. To make this easy, the 'e' modifier adds
1735+this exclude for you, as seen in these two equivalent commands:
1736
1737- it() if the pattern contains a / (not counting a trailing /) or a "**"
1738- then it is matched against the full filename, including any leading
1739- directory. If the pattern doesn't contain a / or a "**", then it is
1740- matched only against the final component of the filename. Again,
1741- remember that the algorithm is applied recursively so "full filename" can
1742- actually be any portion of a path below the starting directory.
3d3aaf9f 1743+verb(
147a9095
WD
1744+ rsync -av --filter=': .excl' --exclude=.excl host:src/dir /dest
1745+ rsync -av --filter=':e .excl' host:src/dir /dest
3d3aaf9f 1746+)
147a9095
WD
1747
1748- it() if the pattern starts with "+ " (a plus followed by a space)
1749- then it is always considered an include pattern, even if specified as
1750- part of an exclude option. The prefix is discarded before matching.
1751-
1752- it() if the pattern starts with "- " (a minus followed by a space)
1753- then it is always considered an exclude pattern, even if specified as
1754- part of an include option. The prefix is discarded before matching.
1755-
1756- it() if the pattern is a single exclamation mark ! then the current
1757- include/exclude list is reset, removing all previously defined patterns.
1758-)
1759-
1760-The +/- rules are most useful in a list that was read from a file, allowing
1761-you to have a single exclude list that contains both include and exclude
1762-options in the proper order.
1763-
1764-Remember that the matching occurs at every step in the traversal of the
1765-directory hierarchy, so you must be sure that all the parent directories of
1766-the files you want to include are not excluded. This is particularly
1767-important when using a trailing '*' rule. For instance, this won't work:
3d3aaf9f
WD
1768+However, if you want to do a delete on the receiving side AND you want some
1769+files to be excluded from being deleted, you'll need to be sure that the
1770+receiving side knows what files to exclude. The easiest way is to include
147a9095 1771+the per-directory merge files in the transfer and use --delete-after,
3d3aaf9f
WD
1772+because this ensures that the receiving side gets all the same exclude
1773+rules as the sending side before it tries to delete anything:
147a9095
WD
1774
1775 verb(
1776- + /some/path/this-file-will-not-be-found
1777- + /file-is-included
1778- - *
1779+ rsync -avF --delete-after host:src/dir /dest
1780 )
1781
1782-This fails because the parent directory "some" is excluded by the '*' rule,
1783-so rsync never visits any of the files in the "some" or "some/path"
1784-directories. One solution is to ask for all directories in the hierarchy
1785-to be included by using a single rule: --include='*/' (put it somewhere
1786-before the --exclude='*' rule). Another solution is to add specific
1787-include rules for all the parent dirs that need to be visited. For
1788-instance, this set of rules works fine:
6e313719
WD
1789+However, if the merge files are not a part of the transfer, you'll need to
1790+either specify some global exclude rules (i.e. specified on the command
1791+line), or you'll need to maintain your own per-directory merge files on
1792+the receiving side. An example of the first is this (assume that the
147a9095
WD
1793+remote .rules files exclude themselves):
1794
1795 verb(
1796- + /some/
1797- + /some/path/
1798- + /some/path/this-file-is-found
1799- + /file-also-included
1800- - *
1801+ rsync -av --filter=': .rules' --filter='. /my/extra.rules'
3d3aaf9f 1802+ --delete host:src/dir /dest
147a9095
WD
1803 )
1804
1805-Here are some examples of exclude/include matching:
3d3aaf9f 1806+In the above example the extra.rules file can affect both sides of the
147a9095
WD
1807+transfer, but (on the sending side) the rules are subservient to the rules
1808+merged from the .rules files because they were specified after the
1809+per-directory merge rule.
3d3aaf9f 1810+
147a9095
WD
1811+In one final example, the remote side is excluding the .rsync-rules
1812+files from the transfer, but we want to use our own .rsync-rules files
3d3aaf9f
WD
1813+to control what gets deleted on the receiving side. To do this we must
1814+specifically exclude the per-directory merge files (so that they don't get
1815+deleted) and then put rules into the local files to control what else
6e313719 1816+should not get deleted. Like one of these commands:
147a9095
WD
1817
1818-itemize(
1819- it() --exclude "*.o" would exclude all filenames matching *.o
1820- it() --exclude "/foo" would exclude a file called foo in the transfer-root directory
1821- it() --exclude "foo/" would exclude any directory called foo
1822- it() --exclude "/foo/*/bar" would exclude any file called bar two
1823- levels below a directory called foo in the transfer-root directory
1824- it() --exclude "/foo/**/bar" would exclude any file called bar two
1825- or more levels below a directory called foo in the transfer-root directory
1826- it() --include "*/" --include "*.c" --exclude "*" would include all
1827- directories and C source files
1828- it() --include "foo/" --include "foo/bar.c" --exclude "*" would include
1829- only foo/bar.c (the foo/ directory must be explicitly included or
1830- it would be excluded by the "*")
3d3aaf9f 1831+verb(
147a9095
WD
1832+ rsync -av --filter=':e /.rsync-rules' --delete host:src/dir /dest
1833+ rsync -avFF --delete host:src/dir /dest
3d3aaf9f 1834 )
524989ae 1835
3d3aaf9f 1836 manpagesection(BATCH MODE)
147a9095
WD
1837@@ -1439,7 +1695,7 @@ it. The most common cause is incorrectly
1838 scripts (such as .cshrc or .profile) that contain output statements
1839 for non-interactive logins.
1840
1841-If you are having trouble debugging include and exclude patterns, then
1842+If you are having trouble debugging filter patterns, then
1843 try specifying the -vv option. At this level of verbosity rsync will
1844 show why each individual file is included or excluded.
1845
13bed3dd 1846--- orig/testsuite/exclude.test 2004-05-29 21:25:45
ee35f7fb
WD
1847+++ testsuite/exclude.test 2005-01-17 06:19:10
1848@@ -23,19 +23,50 @@ export HOME CVSIGNORE
7d31425d
WD
1849 makepath "$fromdir/foo/down/to/you"
1850 makepath "$fromdir/bar/down/to/foo/too"
1851 makepath "$fromdir/mid/for/foo/and/that/is/who"
c4e46f36 1852+cat >"$fromdir/.excl" <<EOF
ee35f7fb
WD
1853+- down
1854+: .excl-temp
1855+!
147a9095
WD
1856+- .excl
1857+- *.bak
1858+- *.old
1859+- *.junk
c4e46f36 1860+EOF
7d31425d
WD
1861 echo kept >"$fromdir/foo/file1"
1862 echo removed >"$fromdir/foo/file2"
1863 echo cvsout >"$fromdir/foo/file2.old"
c4e46f36 1864+cat >"$fromdir/foo/.excl" <<EOF
7d31425d
WD
1865++ .excl
1866+- file1
c4e46f36
WD
1867+EOF
1868+cat >"$fromdir/bar/.excl" <<EOF
147a9095
WD
1869+- home-cvs-exclude
1870+: .excl2
7d31425d 1871++ to
c4e46f36 1872+EOF
7d31425d 1873 echo cvsout >"$fromdir/bar/down/to/home-cvs-exclude"
c4e46f36 1874+cat >"$fromdir/bar/down/to/.excl2" <<EOF
147a9095 1875+- .excl2
c4e46f36 1876+EOF
7d31425d
WD
1877 echo keeper >"$fromdir/bar/down/to/foo/file1"
1878 echo cvsout >"$fromdir/bar/down/to/foo/file1.bak"
1879 echo gone >"$fromdir/bar/down/to/foo/file3"
1880 echo lost >"$fromdir/bar/down/to/foo/file4"
1881 echo cvsout >"$fromdir/bar/down/to/foo/file4.junk"
1882 echo smashed >"$fromdir/bar/down/to/foo/to"
c4e46f36 1883+cat >"$fromdir/bar/down/to/foo/.excl2" <<EOF
7d31425d 1884++ *.junk
c4e46f36 1885+EOF
7d31425d 1886+# This one should be ineffectual
c4e46f36 1887+cat >"$fromdir/mid/.excl2" <<EOF
147a9095 1888+- extra
c4e46f36 1889+EOF
7d31425d
WD
1890 echo cvsout >"$fromdir/mid/one-in-one-out"
1891 echo one-in-one-out >"$fromdir/mid/.cvsignore"
1892 echo cvsin >"$fromdir/mid/one-for-all"
c4e46f36 1893+cat >"$fromdir/mid/.excl" <<EOF
147a9095 1894+:C
c4e46f36 1895+EOF
7d31425d
WD
1896 echo cvsin >"$fromdir/mid/for/one-in-one-out"
1897 echo expunged >"$fromdir/mid/for/foo/extra"
1898 echo retained >"$fromdir/mid/for/foo/keep"
ee35f7fb
WD
1899@@ -45,6 +76,7 @@ ln -s too "$fromdir/bar/down/to/foo/sym"
1900
1901 excl="$scratchdir/exclude-from"
1902 cat >"$excl" <<EOF
1903+!
1904 # If the second line of these two lines does anything, it's a bug.
1905 + **/bar
1906 - /bar
1907@@ -82,7 +114,8 @@ $RSYNC -av --existing --include='*/' --e
1908
1909 # Now, test if rsync excludes the same files.
1910
1911-checkit "$RSYNC -avv --exclude-from=\"$excl\" \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
1912+checkit "$RSYNC -avv --exclude-from=\"$excl\" \
1913+ --delete \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
1914
1915 # Modify the chk dir by removing cvs-ignored files and then tweaking the dir times.
1916
1917@@ -97,7 +130,26 @@ $RSYNC -av --existing --include='*/' --e
6e313719
WD
1918 # Now, test if rsync excludes the same files, this time with --cvs-exclude
1919 # and --delete-excluded.
1920
1921-checkit "$RSYNC -avvC --exclude-from=\"$excl\" \
147a9095 1922+checkit "$RSYNC -avvC --filter=\". $excl\" \
6e313719
WD
1923+ --delete-excluded \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
1924+
7d31425d
WD
1925+# Modify the chk dir for our merge-exclude test and then tweak the dir times.
1926+
1927+rm "$chkdir"/.excl
1928+rm "$chkdir"/foo/file1
1929+rm "$chkdir"/bar/.excl
1930+rm "$chkdir"/bar/down/to/.excl2
1931+rm "$chkdir"/bar/down/to/foo/.excl2
1932+rm "$chkdir"/mid/.excl
1933+cp -p "$fromdir"/bar/down/to/foo/*.junk "$chkdir"/bar/down/to/foo
1934+cp -p "$fromdir"/bar/down/to/foo/to "$chkdir"/bar/down/to/foo
1935+
1936+$RSYNC -av --existing --include='*/' --exclude='*' "$fromdir/" "$chkdir/"
1937+
1938+# Now, test if rsync excludes the same files, this time with a merge-exclude
1939+# file.
1940+
ee35f7fb 1941+checkit "sed '/!/d' \"$excl\" | $RSYNC -avv -f :_.excl -f ._- \
6e313719
WD
1942 --delete-excluded \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
1943
7d31425d 1944 # The script would have aborted on error, so getting here means we've won.