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