Fixed failures/fuzz.
[rsync/rsync-patches.git] / filter.diff
... / ...
CommitLineData
1After applying this patch and running configure, you MUST run this
2command before "make":
3
4 make proto
5
6This patch adds the ability to merge rules into your excludes/includes
7using a ". FILE" idiom. If you specify a name without slashes, that
8filename will be looked for in every subdirectory that rsync visits,
9and the rules found in that subdirectory's file will affect that dir
10and its subdirectories.
11
12For example:
13
14 rsync -av --exclude='. .excl' from/ to
15
16The above will look for a file named ".excl" in every directory of the
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:
20
21 + *.c
22 . .excl2
23 . ./.excl3
24 *.o
25
26Then the file ".excl2" will also be read in the current dir, and all
27subdirs of the current dir. The file ".excl3" would just be read in
28for the current dir because its name contained a slash.
29
30..wayne..
31
32--- exclude.c 22 May 2004 05:32:20 -0000 1.82
33+++ exclude.c 28 May 2004 21:56:09 -0000
34@@ -30,13 +30,57 @@ extern int verbose;
35 extern int eol_nulls;
36 extern int list_only;
37 extern int recurse;
38+extern int io_error;
39+extern int sanitize_paths;
40+extern int inherit_exclude_levels;
41
42 extern char curr_dir[];
43
44 struct exclude_list_struct exclude_list = { 0, 0, "" };
45-struct exclude_list_struct local_exclude_list = { 0, 0, "per-dir .cvsignore " };
46 struct exclude_list_struct server_exclude_list = { 0, 0, "server " };
47 char *exclude_path_prefix = NULL;
48+struct exclude_struct **mergelist_parents;
49+int mergelist_cnt = 0;
50+int mergelist_size = 0;
51+
52+struct mergelist_save_struct {
53+ struct exclude_list_struct *array;
54+ int count;
55+};
56+
57+static char dirbuf[MAXPATHLEN];
58+static unsigned int dirbuf_offset = 0;
59+
60+/* Each exclude_list_struct describes a singly-linked list by keeping track
61+ * of both the head and tail pointers. The list is slightly unusual in that
62+ * a parent-dir's content can be appended to the end of the local list in a
63+ * special way: the last item in the local list has its "next" pointer set
64+ * to point to the inherited list, but the local list's tail pointer points
65+ * at the end of the local list. Thus, if the local list is empty, the head
66+ * will be pointing at the inherited content but the tail will be NULL. To
67+ * help you visualize this, here are the possible list arrangements:
68+ *
69+ * Completely Empty Local Content Only
70+ * ================================== ====================================
71+ * head -> NULL head -> Local1 -> Local2 -> NULL
72+ * tail -> NULL tail -------------^
73+ *
74+ * Inherited Content Only Both Local and Inherited Content
75+ * ================================== ====================================
76+ * head -> Parent1 -> Parent2 -> NULL head -> L1 -> L2 -> P1 -> P2 -> NULL
77+ * tail -> NULL tail ---------^
78+ *
79+ * This means that anyone wanting to traverse the whole list to USE it just
80+ * needs to start at the head and use the "next" pointers until it goes
81+ * NULL. To add new local content, we insert the item after the tail item
82+ * and update the tail (obviously, if "tail" was NULL, we insert it at the
83+ * head). To clear the local list, WE MUST NOT FREE THE INHERITED CONTENT
84+ * because it is shared between the current list and our parent list(s).
85+ * The easiest way to handle this is to simply truncate the list after the
86+ * tail item and then free the local list from the head. When inheriting
87+ * the list for a new local dir, we just save off the exclude_list_struct
88+ * values (so we can pop back to them later) and set the tail to NULL.
89+ */
90
91 /** Build an exclude structure given an exclude pattern. */
92 static void make_exclude(struct exclude_list_struct *listp, const char *pat,
93@@ -46,6 +90,24 @@ static void make_exclude(struct exclude_
94 const char *cp;
95 unsigned int ex_len;
96
97+ if (mflags & MATCHFLG_MERGE_FILE) {
98+ int i;
99+ /* If the local include file was already mentioned, don't
100+ * add it again. */
101+ for (i = 0; i < mergelist_cnt; i++) {
102+ struct exclude_struct *ex = mergelist_parents[i];
103+ if (strlen(ex->pattern) == pat_len
104+ && memcmp(ex->pattern, pat, pat_len) == 0)
105+ return;
106+ }
107+ if ((pat_len == 10 || (pat_len > 10 && pat[pat_len-11] == '/'))
108+ && strncmp(pat+pat_len-10, ".cvsignore", 10) == 0) {
109+ mflags |= MATCHFLG_CVSIGNORE;
110+ mflags &= ~MATCHFLG_INCLUDE;
111+ } else
112+ mflags &= ~MATCHFLG_CVSIGNORE;
113+ }
114+
115 ret = new(struct exclude_struct);
116 if (!ret)
117 out_of_memory("make_exclude");
118@@ -81,14 +143,36 @@ static void make_exclude(struct exclude_
119 mflags |= MATCHFLG_DIRECTORY;
120 }
121
122- for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++)
123- ret->slash_cnt++;
124+ if (mflags & MATCHFLG_MERGE_FILE) {
125+ struct exclude_list_struct *lp
126+ = new_array(struct exclude_list_struct, 1);
127+ if (!lp)
128+ out_of_memory("make_exclude");
129+ lp->head = lp->tail = NULL;
130+ if (asprintf(&lp->debug_type, "per-dir %s ", ret->pattern) < 0)
131+ out_of_memory("make_exclude");
132+ ret->u.mergelist = lp;
133+ if (mergelist_cnt == mergelist_size) {
134+ mergelist_size += 5;
135+ mergelist_parents = realloc_array(mergelist_parents,
136+ struct exclude_struct *,
137+ mergelist_size);
138+ if (!mergelist_parents)
139+ out_of_memory("make_exclude");
140+ }
141+ mergelist_parents[mergelist_cnt++] = ret;
142+ } else {
143+ for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++)
144+ ret->u.slash_cnt++;
145+ }
146
147 ret->match_flags = mflags;
148
149- if (!listp->tail)
150+ if (!listp->tail) {
151+ ret->next = listp->head;
152 listp->head = listp->tail = ret;
153- else {
154+ } else {
155+ ret->next = listp->tail->next;
156 listp->tail->next = ret;
157 listp->tail = ret;
158 }
159@@ -96,22 +180,143 @@ static void make_exclude(struct exclude_
160
161 static void free_exclude(struct exclude_struct *ex)
162 {
163+ if (ex->match_flags & MATCHFLG_MERGE_FILE) {
164+ free(ex->u.mergelist->debug_type);
165+ free(ex->u.mergelist);
166+ }
167 free(ex->pattern);
168 free(ex);
169 }
170
171-void clear_exclude_list(struct exclude_list_struct *listp)
172+static void clear_exclude_list(struct exclude_list_struct *listp)
173 {
174- struct exclude_struct *ent, *next;
175-
176- for (ent = listp->head; ent; ent = next) {
177- next = ent->next;
178- free_exclude(ent);
179+ if (listp->tail) {
180+ struct exclude_struct *ent, *next;
181+ /* Truncate any inherited items from the local list. */
182+ listp->tail->next = NULL;
183+ /* Now free everything that is left. */
184+ for (ent = listp->head; ent; ent = next) {
185+ next = ent->next;
186+ free_exclude(ent);
187+ }
188 }
189
190 listp->head = listp->tail = NULL;
191 }
192
193+static void pre_inherit_files(struct exclude_list_struct *lp, const char *fn,
194+ int flags)
195+{
196+ char *t = dirbuf + dirbuf_offset;
197+ int i;
198+
199+ if (dirbuf_offset + inherit_exclude_levels * 3 + strlen(fn)
200+ >= MAXPATHLEN)
201+ return;
202+
203+ /* XXX sanitize_path() !! */
204+
205+ for (i = 0; i < inherit_exclude_levels; i++)
206+ *t++ = '.', *t++ = '.', *t++ = '/';
207+ while (i-- > 0) {
208+ lp->tail = NULL;
209+ strcpy(t, fn);
210+ add_exclude_file(lp, dirbuf, flags);
211+ t -= 3;
212+ }
213+}
214+
215+void *push_local_excludes(char *fname, unsigned int offset)
216+{
217+ struct mergelist_save_struct *push;
218+ struct exclude_list_struct *ap;
219+ int i;
220+
221+ /* Make it easy to construct the full path for a merge-file that was
222+ * specified with a relative path by saving off the current dir. */
223+ memcpy(dirbuf, fname, offset);
224+ dirbuf_offset = offset;
225+
226+ if (!(push = new_array(struct mergelist_save_struct, 1)))
227+ out_of_memory("push_local_excludes");
228+
229+ push->count = mergelist_cnt;
230+ push->array = new_array(struct exclude_list_struct, mergelist_cnt);
231+ if (!push->array)
232+ out_of_memory("push_local_excludes");
233+
234+ for (i = 0, ap = push->array; i < mergelist_cnt; i++) {
235+ memcpy(ap++, mergelist_parents[i]->u.mergelist,
236+ sizeof (struct exclude_list_struct));
237+ }
238+
239+ /* Note: add_exclude_file() might increase mergelist_cnt, so keep
240+ * this loop separate from the above loop. */
241+ for (i = 0; i < mergelist_cnt; i++) {
242+ struct exclude_struct *ex = mergelist_parents[i];
243+ struct exclude_list_struct *lp = ex->u.mergelist;
244+ int flags;
245+
246+ if (verbose > 2) {
247+ rprintf(FINFO, "[%s] pushing %sexclude list\n",
248+ who_am_i(), lp->debug_type);
249+ }
250+
251+ if (ex->match_flags & MATCHFLG_CVSIGNORE) {
252+ lp->head = NULL; /* CVS doesn't inherit rules. */
253+ flags = XFLG_WORD_SPLIT | XFLG_WORDS_ONLY;
254+ } else {
255+ flags = ex->match_flags & MATCHFLG_INCLUDE
256+ ? XFLG_DEF_INCLUDE : 0;
257+ if (inherit_exclude_levels > 0)
258+ pre_inherit_files(lp, ex->pattern, flags);
259+ }
260+ lp->tail = NULL; /* Switch any local rules to inherited. */
261+ if (strlcpy(dirbuf + dirbuf_offset, ex->pattern,
262+ MAXPATHLEN - dirbuf_offset) < MAXPATHLEN - dirbuf_offset)
263+ add_exclude_file(lp, dirbuf, flags);
264+ else {
265+ io_error |= IOERR_GENERAL;
266+ rprintf(FINFO,
267+ "cannot add local excludes in long-named directory %s\n",
268+ full_fname(dirbuf));
269+ }
270+ }
271+
272+ if (inherit_exclude_levels > 0)
273+ inherit_exclude_levels = 0;
274+
275+ return (void*)push;
276+}
277+
278+void pop_local_excludes(void *mem)
279+{
280+ struct mergelist_save_struct *pop = (struct mergelist_save_struct*)mem;
281+ struct exclude_list_struct *ap;
282+ int i;
283+
284+ for (i = mergelist_cnt; i-- > 0; ) {
285+ struct exclude_struct *ex = mergelist_parents[i];
286+ struct exclude_list_struct *lp = ex->u.mergelist;
287+
288+ if (verbose > 2) {
289+ rprintf(FINFO, "[%s] popping %sexclude list\n",
290+ who_am_i(), lp->debug_type);
291+ }
292+
293+ clear_exclude_list(lp);
294+ }
295+
296+ mergelist_cnt = pop->count;
297+ for (i = 0, ap = pop->array; i < mergelist_cnt; i++) {
298+ memcpy(mergelist_parents[i]->u.mergelist, ap++,
299+ sizeof (struct exclude_list_struct));
300+ }
301+
302+ free(pop->array);
303+ free(pop);
304+}
305+
306 static int check_one_exclude(char *name, struct exclude_struct *ex,
307 int name_is_dir)
308 {
309@@ -122,7 +327,7 @@ static int check_one_exclude(char *name,
310 /* If the pattern does not have any slashes AND it does not have
311 * a "**" (which could match a slash), then we just match the
312 * name portion of the path. */
313- if (!ex->slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
314+ if (!ex->u.slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
315 if ((p = strrchr(name,'/')) != NULL)
316 name = p+1;
317 }
318@@ -148,9 +353,9 @@ static int check_one_exclude(char *name,
319 if (ex->match_flags & MATCHFLG_WILD) {
320 /* A non-anchored match with an infix slash and no "**"
321 * needs to match the last slash_cnt+1 name elements. */
322- if (!match_start && ex->slash_cnt
323+ if (!match_start && ex->u.slash_cnt
324 && !(ex->match_flags & MATCHFLG_WILD2)) {
325- int cnt = ex->slash_cnt + 1;
326+ int cnt = ex->u.slash_cnt + 1;
327 for (p = name + strlen(name) - 1; p >= name; p--) {
328 if (*p == '/' && !--cnt)
329 break;
330@@ -221,6 +426,13 @@ int check_exclude(struct exclude_list_st
331 struct exclude_struct *ent;
332
333 for (ent = listp->head; ent; ent = ent->next) {
334+ if (ent->match_flags & MATCHFLG_MERGE_FILE) {
335+ int rc = check_exclude(ent->u.mergelist, name,
336+ name_is_dir);
337+ if (rc)
338+ return rc;
339+ continue;
340+ }
341 if (check_one_exclude(name, ent, name_is_dir)) {
342 report_exclude_result(name, ent, name_is_dir,
343 listp->debug_type);
344@@ -253,11 +465,16 @@ static const char *get_exclude_tok(const
345 p = (const char *)s;
346 }
347
348- /* Is this a '+' or '-' followed by a space (not whitespace)? */
349+ /* Is this a +/-/. followed by a space (not whitespace)? */
350 if (!(xflags & XFLG_WORDS_ONLY)
351- && (*s == '-' || *s == '+') && s[1] == ' ') {
352+ && (*s == '-' || *s == '+' || *s == '.') && s[1] == ' ') {
353 if (*s == '+')
354 mflags |= MATCHFLG_INCLUDE;
355+ else if (*s == '.') {
356+ mflags |= MATCHFLG_MERGE_FILE;
357+ if (xflags & XFLG_DEF_INCLUDE)
358+ mflags |= MATCHFLG_INCLUDE;
359+ }
360 s += 2;
361 } else if (xflags & XFLG_DEF_INCLUDE)
362 mflags |= MATCHFLG_INCLUDE;
363@@ -306,11 +523,54 @@ void add_exclude(struct exclude_list_str
364 continue;
365 }
366
367+ if (mflags & MATCHFLG_MERGE_FILE) {
368+ char name[MAXPATHLEN];
369+ if (pat_len >= sizeof name) {
370+ rprintf(FERROR,
371+ "merge filename too long: %s\n", cp);
372+ continue;
373+ }
374+ strlcpy(name, cp, pat_len+1);
375+ if (!(xflags & XFLG_PERDIR_MERGE)
376+ && (inherit_exclude_levels == -1
377+ || strchr(name, '/') != NULL)) {
378+ char *mem = NULL;
379+ if (*name == '/') {
380+ if (sanitize_paths) {
381+ mem = alloc_sanitize_path(name,
382+ curr_dir);
383+ cp = mem;
384+ } else
385+ cp = name;
386+ } else {
387+ if (sanitize_paths) {
388+ dirbuf[dirbuf_offset] = '\0';
389+ sanitize_path(name, dirbuf);
390+ }
391+ if (strlcpy(dirbuf + dirbuf_offset,
392+ name, MAXPATHLEN - dirbuf_offset)
393+ >= MAXPATHLEN - dirbuf_offset) {
394+ rprintf(FERROR,
395+ "merge filename too long: %s...\n",
396+ dirbuf);
397+ continue;
398+ }
399+ cp = dirbuf;
400+ }
401+ add_exclude_file(listp, cp,
402+ xflags | XFLG_FATAL_ERRORS);
403+ if (mem)
404+ free(mem);
405+ continue;
406+ }
407+ }
408+
409 make_exclude(listp, cp, pat_len, mflags);
410
411 if (verbose > 2) {
412- rprintf(FINFO, "[%s] add_exclude(%.*s, %s%sclude)\n",
413+ rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s%sclude)\n",
414 who_am_i(), (int)pat_len, cp, listp->debug_type,
415+ mflags & MATCHFLG_MERGE_FILE ? "FILE " : "",
416 mflags & MATCHFLG_INCLUDE ? "in" : "ex");
417 }
418 }
419@@ -343,6 +603,11 @@ void add_exclude_file(struct exclude_lis
420 return;
421 }
422
423+ if (verbose > 2) {
424+ rprintf(FINFO, "[%s] add_exclude_file(%s,%d)\n",
425+ who_am_i(), fname, xflags);
426+ }
427+
428 while (1) {
429 char *s = line;
430 int ch, overflow = 0;
431@@ -402,7 +667,11 @@ void send_exclude_list(int f)
432 if (ent->match_flags & MATCHFLG_INCLUDE) {
433 write_int(f, l + 2);
434 write_buf(f, "+ ", 2);
435- } else if ((*p == '-' || *p == '+') && p[1] == ' ') {
436+ } else if (ent->match_flags & MATCHFLG_MERGE_FILE) {
437+ write_int(f, l + 2);
438+ write_buf(f, ". ", 2);
439+ } else if ((*p == '-' || *p == '+' || *p == '.')
440+ && p[1] == ' ') {
441 write_int(f, l + 2);
442 write_buf(f, "- ", 2);
443 } else
444@@ -443,6 +712,7 @@ void add_cvs_excludes(void)
445 char fname[MAXPATHLEN];
446 char *p;
447
448+ add_exclude(&exclude_list, ". .cvsignore", XFLG_PERDIR_MERGE);
449 add_exclude(&exclude_list, default_cvsignore,
450 XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
451
452--- flist.c 21 May 2004 23:22:14 -0000 1.225
453+++ flist.c 28 May 2004 21:56:09 -0000
454@@ -39,8 +39,6 @@ extern int module_id;
455 extern int ignore_errors;
456 extern int numeric_ids;
457
458-extern int cvs_exclude;
459-
460 extern int recurse;
461 extern char curr_dir[MAXPATHLEN];
462 extern char *files_from;
463@@ -65,7 +63,6 @@ extern int write_batch;
464
465 extern struct exclude_list_struct exclude_list;
466 extern struct exclude_list_struct server_exclude_list;
467-extern struct exclude_list_struct local_exclude_list;
468
469 int io_error;
470
471@@ -210,8 +207,6 @@ int link_stat(const char *path, STRUCT_S
472 */
473 static int check_exclude_file(char *fname, int is_dir, int exclude_level)
474 {
475- int rc;
476-
477 #if 0 /* This currently never happens, so avoid a useless compare. */
478 if (exclude_level == NO_EXCLUDES)
479 return 0;
480@@ -233,10 +228,7 @@ static int check_exclude_file(char *fnam
481 if (exclude_level != ALL_EXCLUDES)
482 return 0;
483 if (exclude_list.head
484- && (rc = check_exclude(&exclude_list, fname, is_dir)) != 0)
485- return rc < 0;
486- if (local_exclude_list.head
487- && check_exclude(&local_exclude_list, fname, is_dir) < 0)
488+ && check_exclude(&exclude_list, fname, is_dir) < 0)
489 return 1;
490 return 0;
491 }
492@@ -942,15 +934,7 @@ void send_file_name(int f, struct file_l
493
494 if (recursive && S_ISDIR(file->mode)
495 && !(file->flags & FLAG_MOUNT_POINT)) {
496- struct exclude_list_struct last_list = local_exclude_list;
497- local_exclude_list.head = local_exclude_list.tail = NULL;
498 send_directory(f, flist, f_name_to(file, fbuf));
499- if (verbose > 2) {
500- rprintf(FINFO, "[%s] popping %sexclude list\n",
501- who_am_i(), local_exclude_list.debug_type);
502- }
503- clear_exclude_list(&local_exclude_list);
504- local_exclude_list = last_list;
505 }
506 }
507
508@@ -961,6 +945,7 @@ static void send_directory(int f, struct
509 struct dirent *di;
510 char fname[MAXPATHLEN];
511 unsigned int offset;
512+ void *save_excludes;
513 char *p;
514
515 d = opendir(dir);
516@@ -984,18 +969,7 @@ static void send_directory(int f, struct
517 offset++;
518 }
519
520- if (cvs_exclude) {
521- if (strlcpy(p, ".cvsignore", MAXPATHLEN - offset)
522- < MAXPATHLEN - offset) {
523- add_exclude_file(&local_exclude_list, fname,
524- XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
525- } else {
526- io_error |= IOERR_GENERAL;
527- rprintf(FINFO,
528- "cannot cvs-exclude in long-named directory %s\n",
529- full_fname(fname));
530- }
531- }
532+ save_excludes = push_local_excludes(fname, offset);
533
534 for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
535 char *dname = d_name(di);
536@@ -1016,6 +990,8 @@ static void send_directory(int f, struct
537 rsyserr(FERROR, errno, "readdir(%s)", dir);
538 }
539
540+ pop_local_excludes(save_excludes);
541+
542 closedir(d);
543 }
544
545--- options.c 27 May 2004 21:51:53 -0000 1.153
546+++ options.c 28 May 2004 21:56:09 -0000
547@@ -48,6 +48,7 @@ int preserve_gid = 0;
548 int preserve_times = 0;
549 int update_only = 0;
550 int cvs_exclude = 0;
551+int inherit_exclude_levels = -9999;
552 int dry_run = 0;
553 int local_server = 0;
554 int ignore_times = 0;
555@@ -306,7 +307,7 @@ void usage(enum logcode F)
556
557 enum {OPT_VERSION = 1000, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
558 OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED, OPT_LINK_DEST,
559- OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW,
560+ OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_INHERIT, OPT_MODIFY_WINDOW,
561 OPT_READ_BATCH, OPT_WRITE_BATCH,
562 OPT_REFUSED_BASE = 9000};
563
564@@ -331,6 +332,7 @@ static struct poptOption long_options[]
565 {"include", 0, POPT_ARG_STRING, 0, OPT_INCLUDE, 0, 0 },
566 {"exclude-from", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE_FROM, 0, 0 },
567 {"include-from", 0, POPT_ARG_STRING, 0, OPT_INCLUDE_FROM, 0, 0 },
568+ {"inherit", 0, POPT_ARG_STRING, 0, OPT_INHERIT, 0, 0 },
569 {"safe-links", 0, POPT_ARG_NONE, &safe_symlinks, 0, 0, 0 },
570 {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 },
571 {"backup", 'b', POPT_ARG_NONE, &make_backups, 0, 0, 0 },
572@@ -543,6 +545,31 @@ int parse_arguments(int *argc, const cha
573 XFLG_FATAL_ERRORS | XFLG_DEF_INCLUDE);
574 break;
575
576+ case OPT_INHERIT:
577+ arg = poptGetOptArg(pc);
578+ if (isdigit(*arg))
579+ inherit_exclude_levels = atoi(arg);
580+ else if (*arg == '.') {
581+ if (!arg[1])
582+ inherit_exclude_levels = 0;
583+ else if (arg[1] == '.') {
584+ inherit_exclude_levels = 0;
585+ arg--;
586+ do {
587+ inherit_exclude_levels++;
588+ arg += 3;
589+ } while (strncmp(arg, "/..", 3) == 0);
590+ if (*arg)
591+ inherit_exclude_levels = -1;
592+ }
593+ }
594+ if (inherit_exclude_levels < 0) {
595+ snprintf(err_buf, sizeof err_buf,
596+ "Invalid argument given to --inherit.\n");
597+ return 0;
598+ }
599+ break;
600+
601 case 'h':
602 usage(FINFO);
603 exit_cleanup(0);
604@@ -767,6 +794,9 @@ int parse_arguments(int *argc, const cha
605 }
606 }
607
608+ if (inherit_exclude_levels < 0)
609+ inherit_exclude_levels = -1;
610+
611 return 1;
612 }
613
614@@ -866,6 +896,12 @@ void server_options(char **args,int *arg
615 if (x != 1)
616 args[ac++] = argstr;
617
618+ if (inherit_exclude_levels >= 0) {
619+ if (asprintf(&arg, "--inherit=%d", inherit_exclude_levels) < 0)
620+ goto oom;
621+ args[ac++] = arg;
622+ }
623+
624 if (block_size) {
625 if (asprintf(&arg, "-B%u", block_size) < 0)
626 goto oom;
627--- rsync.h 16 May 2004 07:28:24 -0000 1.204
628+++ rsync.h 28 May 2004 21:56:10 -0000
629@@ -108,6 +108,7 @@
630 #define XFLG_DEF_INCLUDE (1<<1)
631 #define XFLG_WORDS_ONLY (1<<2)
632 #define XFLG_WORD_SPLIT (1<<3)
633+#define XFLG_PERDIR_MERGE (1<<4)
634
635 #define PERMS_REPORT (1<<0)
636 #define PERMS_SKIP_MTIME (1<<1)
637@@ -496,11 +497,16 @@ struct map_struct {
638 #define MATCHFLG_INCLUDE (1<<4) /* this is an include, not an exclude */
639 #define MATCHFLG_DIRECTORY (1<<5) /* this matches only directories */
640 #define MATCHFLG_CLEAR_LIST (1<<6) /* this item is the "!" token */
641+#define MATCHFLG_MERGE_FILE (1<<7) /* specifies a file to merge */
642+#define MATCHFLG_CVSIGNORE (1<<8) /* parse this as a .cvsignore file */
643 struct exclude_struct {
644 struct exclude_struct *next;
645 char *pattern;
646 unsigned int match_flags;
647- int slash_cnt;
648+ union {
649+ int slash_cnt;
650+ struct exclude_list_struct *mergelist;
651+ } u;
652 };
653
654 struct exclude_list_struct {
655--- rsync.yo 21 May 2004 09:44:32 -0000 1.170
656+++ rsync.yo 28 May 2004 21:56:11 -0000
657@@ -331,6 +331,7 @@ verb(
658 --exclude-from=FILE exclude patterns listed in FILE
659 --include=PATTERN don't exclude files matching PATTERN
660 --include-from=FILE don't exclude patterns listed in FILE
661+ --inherit=DEPTH make per-dir merge files inherited
662 --files-from=FILE read FILE for list of source-file names
663 -0 --from0 all file lists are delimited by nulls
664 --version print version number
665@@ -683,6 +684,28 @@ dit(bf(--include-from=FILE)) This specif
666 from a file.
667 If em(FILE) is bf(-) the list will be read from standard input.
668
669+dit(bf(--inherit=DEPTH)) Using this option allows you to specify that the
670+contents of per-directory merge files is inherited by the subdirectories of
671+the spot where the rules were read in. If a subdirectory has its own
672+per-directory merge file, its contents are prefixed to the inherited rules,
673+which gives them higher priority.
674+
675+The DEPTH value tells rsync how much deeper than the root directory of the
676+transfer should be scanned for merge files. If you don't need any higher
677+directories scanned, use "." (dot). If you want the parent directory
678+scanned, specify ".." (dot dot) or "1" (i.e. one directory higher). You
679+can continue to specify either more ".."s (separated by slashes) or simply
680+supply the count of how many parent-directory levels should be scanned.
681+The reason this is useful is that you may wish to transfer just a small
682+portion of a larger tree of files, but to be sure to get all the
683+appropriate exclude rules, you need to be sure that rsync reads in all the
684+merge files from the top of the tree of related files.
685+
686+Note also that you can eliminate all the inherited rules for the current
687+per-directory ruleset by putting the list-clearing token (!) in the file.
688+This only clears the rules in the current per-directory sub-list for the
689+current directory and its subdirectories.
690+
691 dit(bf(--files-from=FILE)) Using this option allows you to specify the
692 exact list of files to transfer (as read from the specified FILE or "-"
693 for stdin). It also tweaks the default behavior of rsync to make
694@@ -1038,6 +1061,11 @@ itemize(
695 then it is always considered an exclude pattern, even if specified as
696 part of an include option. The prefix is discarded before matching.
697
698+ it() if the pattern starts with ". " (a dot followed by a space) then it
699+ its pattern is taken to be a merge file that is read in to supplement the
700+ current rules. See the section on MERGING EXCLUDE FILES for more
701+ information.
702+
703 it() if the pattern is a single exclamation mark ! then the current
704 include/exclude list is reset, removing all previously defined patterns.
705 )
706@@ -1090,6 +1118,67 @@ itemize(
707 it would be excluded by the "*")
708 )
709
710+manpagesection(MERGING EXCLUDE FILES)
711+
712+You can merge whole files into an exclude file using a rule that starts
713+with a ". " (a dot followed by a space) and has a filename in place of the
714+pattern. There are two types of merge rules, single-instance and
715+per-directory:
716+
717+itemize(
718+ it() If the filename has no slashes in it, it is a per-directory merge;
719+ rsync scans every directory that it traverses for the named file, merging
720+ its contents (when it exists) at the start of this per-directory
721+ sub-list.
722+
723+ it() If a filename has a slash in it, it is a single-instance merge; the
724+ named file's contents will be merged into the current exclude file,
725+ replacing the merge rule. Thus, you should use the name ./foo instead of
726+ foo if you don't want to scan for "foo" in all the subdirectories in the
727+ transferred tree of directories.
728+)
729+
730+Here's an example exclude file (which you'd specify via the normal
731+--exclude-from option):
732+
733+verb(
734+ . /home/user/.global_excludes
735+ - *.gz
736+ . .excl
737+ + *.[ch]
738+ - *.o
739+)
740+
741+This will merge the contents of the /home/user/.global_excludes file at the
742+start of the list and also turns the ".excl" filename into a per-directory
743+exclude file whose local contents will be merged into the list in place of
744+the .excl line.
745+
746+Additionally, you can affect where the --cvs-exclude (-C) option's
747+inclusion of a per-directory .cvsignore file gets placed into your rules by
748+adding an explicit merge rule for ".cvsignore". For instance, specifying
749+this:
750+
751+verb(
752+ rsync -avC --exclude='. .cvsignore' --exclude-from=foo a/ b
753+)
754+
755+will merge all the per-directory .cvsignore rules at the start of your list
756+rather than at the end. This allows their dir-specific rules to supersede
757+your rules instead of being subservient to them. (The global rules taken
758+from the $HOME/.cvsignore file and from $CVSIGNORE are not affected by
759+this.)
760+
761+Note also that the parsing of any merge-file named ".cvsignore" is always
762+done in a CVS-compatible manner, even if -C wasn't specified (i.e. the
763+rules are always exclude rules (even when specified by an include option);
764+they are split on whitespace; and no special prefixes, list-clearing
765+tokens, or comment characters are honored).
766+
767+See the --inherit option for how to make the rules read from a
768+per-directory merge file inherited by all the subdirectories of the
769+spot where the per-directory rule file was found.
770+
771 manpagesection(BATCH MODE)
772
773 bf(Note:) Batch mode should be considered experimental in this version
774--- testsuite/exclude.test 24 May 2004 00:16:07 -0000 1.8
775+++ testsuite/exclude.test 28 May 2004 21:56:11 -0000
776@@ -23,19 +23,47 @@ export HOME CVSIGNORE
777 makepath "$fromdir/foo/down/to/you"
778 makepath "$fromdir/bar/down/to/foo/too"
779 makepath "$fromdir/mid/for/foo/and/that/is/who"
780+cat >"$fromdir/.excl" <<EOF
781+.excl
782+*.bak
783+*.old
784+*.junk
785+EOF
786 echo kept >"$fromdir/foo/file1"
787 echo removed >"$fromdir/foo/file2"
788 echo cvsout >"$fromdir/foo/file2.old"
789+cat >"$fromdir/foo/.excl" <<EOF
790++ .excl
791+- file1
792+EOF
793+cat >"$fromdir/bar/.excl" <<EOF
794+home-cvs-exclude
795+. .excl2
796++ to
797+EOF
798 echo cvsout >"$fromdir/bar/down/to/home-cvs-exclude"
799+cat >"$fromdir/bar/down/to/.excl2" <<EOF
800+.excl2
801+EOF
802 echo keeper >"$fromdir/bar/down/to/foo/file1"
803 echo cvsout >"$fromdir/bar/down/to/foo/file1.bak"
804 echo gone >"$fromdir/bar/down/to/foo/file3"
805 echo lost >"$fromdir/bar/down/to/foo/file4"
806 echo cvsout >"$fromdir/bar/down/to/foo/file4.junk"
807 echo smashed >"$fromdir/bar/down/to/foo/to"
808+cat >"$fromdir/bar/down/to/foo/.excl2" <<EOF
809++ *.junk
810+EOF
811+# This one should be ineffectual
812+cat >"$fromdir/mid/.excl2" <<EOF
813+extra
814+EOF
815 echo cvsout >"$fromdir/mid/one-in-one-out"
816 echo one-in-one-out >"$fromdir/mid/.cvsignore"
817 echo cvsin >"$fromdir/mid/one-for-all"
818+cat >"$fromdir/mid/.excl" <<EOF
819+. .cvsignore
820+EOF
821 echo cvsin >"$fromdir/mid/for/one-in-one-out"
822 echo expunged >"$fromdir/mid/for/foo/extra"
823 echo retained >"$fromdir/mid/for/foo/keep"
824@@ -100,5 +128,24 @@ $RSYNC -av --existing --include='*/' --e
825 checkit "$RSYNC -avvC --exclude-from=\"$excl\" \
826 --delete-excluded \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
827
828+# Modify the chk dir for our merge-exclude test and then tweak the dir times.
829+
830+rm "$chkdir"/.excl
831+rm "$chkdir"/foo/file1
832+rm "$chkdir"/bar/.excl
833+rm "$chkdir"/bar/down/to/.excl2
834+rm "$chkdir"/bar/down/to/foo/.excl2
835+rm "$chkdir"/mid/.excl
836+cp -p "$fromdir"/bar/down/to/foo/*.junk "$chkdir"/bar/down/to/foo
837+cp -p "$fromdir"/bar/down/to/foo/to "$chkdir"/bar/down/to/foo
838+
839+$RSYNC -av --existing --include='*/' --exclude='*' "$fromdir/" "$chkdir/"
840+
841+# Now, test if rsync excludes the same files, this time with a merge-exclude
842+# file.
843+
844+checkit "$RSYNC -avv --exclude='. .excl' --exclude-from=\"$excl\" --inherit=. \
845+ --delete-excluded \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
846+
847 # The script would have aborted on error, so getting here means we've won.
848 exit 0