From: Wayne Davison Date: Fri, 6 Aug 2004 09:39:57 +0000 (+0000) Subject: Improved the option syntax by eliminating the --inherit=DEPTH option and X-Git-Url: https://mattmccutchen.net/rsync/rsync-patches.git/commitdiff_plain/d34d9fad7ec13b99b7902679a5ac927609797c49 Improved the option syntax by eliminating the --inherit=DEPTH option and adding individual options to the ". [OPTIONS] FILE" command itself. This lets the user directly specify if the file is a per-directory file or not (using -p) and if the rules are inherited or not (using -i). The user also specifies how high up the scanning starts for per-dir files by specifying a path on per-dir files (e.g. ". -p /start/.excl"). Added the -E option which is an easy way to specify ". -pi /.rsync-excludes". --- diff --git a/filter.diff b/filter.diff index e55f98d..d5ed382 100644 --- a/filter.diff +++ b/filter.diff @@ -4,14 +4,15 @@ command before "make": make proto This patch adds the ability to merge rules into your excludes/includes -using a ". FILE" idiom. If you specify a name without slashes, that -filename will be looked for in every subdirectory that rsync visits, -and the rules found in that subdirectory's file will affect that dir -and (optionally) its subdirectories. +using a ". FILE" idiom. If you specify a name with a preceding -p +option, that filename will be looked for in every subdirectory that +rsync visits, and the rules found in that subdirectory's file will +affect either just that dir or, if -i is specified, that dir and its +subdirs. For example: - rsync -av --exclude='. .excl' --inherit=. from/ to + rsync -av --exclude='. -pi .excl' from/ to The above will look for a file named ".excl" in every directory of the hierarchy that rsync visits, and it will exclude (by default) names @@ -19,20 +20,22 @@ based on the rules found therein. If one of the .excl files contains this: + *.c - . .excl2 - . ./.excl3 + . -p .excl2 + . -i .excl3 *.o -Then the file ".excl2" will also be read in the current dir, and all -subdirs of the current dir (due to the --inherit option). The file -".excl3" would just be read in for the current dir because its name -contained a slash. +Then the file ".excl2" will also be read in from the current dir and all +its subdirs (due to the -p option) with each file's rules only affecting +the file's current directory. The file ".excl3" would just be read in +for the current dir (because it was not specified with the -p option), +but its rules would be inherited by all its subdirectories (because of +the -i option). ..wayne.. ---- orig/exclude.c 2004-05-22 19:30:03 -+++ exclude.c 2004-08-03 17:06:22 -@@ -27,16 +27,65 @@ +--- orig/exclude.c 2004-08-05 23:16:37 ++++ exclude.c 2004-08-06 09:01:33 +@@ -27,17 +27,66 @@ #include "rsync.h" extern int verbose; @@ -44,7 +47,6 @@ contained a slash. +extern int module_id; +extern int delete_mode; +extern int delete_excluded; -+extern int inherit_exclude_levels; +extern int sanitize_paths; extern char curr_dir[]; @@ -54,10 +56,7 @@ contained a slash. -struct exclude_list_struct local_exclude_list = { 0, 0, "per-dir .cvsignore " }; struct exclude_list_struct server_exclude_list = { 0, 0, "server " }; char *exclude_path_prefix = NULL; -+struct exclude_struct **mergelist_parents; -+int mergelist_cnt = 0; -+int mergelist_size = 0; -+ + +struct mergelist_save_struct { + struct exclude_list_struct *array; + int count; @@ -65,6 +64,10 @@ contained a slash. + +static char dirbuf[MAXPATHLEN]; +static unsigned int dirbuf_offset = 0; ++static struct exclude_struct **mergelist_parents; ++static int mergelist_cnt = 0; ++static int mergelist_size = 0; ++static int initialized_mergelist_cnt = -1; + +/* Each exclude_list_struct describes a singly-linked list by keeping track + * of both the head and tail pointers. The list is slightly unusual in that @@ -96,13 +99,21 @@ contained a slash. + * the list for a new local dir, we just save off the exclude_list_struct + * values (so we can pop back to them later) and set the tail to NULL. + */ - ++ /** Build an exclude structure given an exclude pattern. */ static void make_exclude(struct exclude_list_struct *listp, const char *pat, -@@ -46,6 +95,24 @@ static void make_exclude(struct exclude_ + unsigned int pat_len, unsigned int mflags) +@@ -46,6 +95,31 @@ static void make_exclude(struct exclude_ const char *cp; unsigned int ex_len; ++ if (verbose > 2) { ++ rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s%sclude)\n", ++ who_am_i(), (int)pat_len, pat, listp->debug_type, ++ mflags & MATCHFLG_MERGE_FILE ? "FILE " : "", ++ mflags & MATCHFLG_INCLUDE ? "in" : "ex"); ++ } ++ + if (mflags & MATCHFLG_MERGE_FILE) { + int i; + /* If the local include file was already mentioned, don't @@ -116,7 +127,7 @@ contained a slash. + if ((pat_len == 10 || (pat_len > 10 && pat[pat_len-11] == '/')) + && strncmp(pat+pat_len-10, ".cvsignore", 10) == 0) { + mflags |= MATCHFLG_CVSIGNORE; -+ mflags &= ~MATCHFLG_INCLUDE; ++ mflags &= ~(MATCHFLG_INCLUDE | MATCHFLG_INHERIT); + } else + mflags &= ~MATCHFLG_CVSIGNORE; + } @@ -124,7 +135,7 @@ contained a slash. ret = new(struct exclude_struct); if (!ret) out_of_memory("make_exclude"); -@@ -81,14 +148,36 @@ static void make_exclude(struct exclude_ +@@ -81,14 +155,40 @@ static void make_exclude(struct exclude_ mflags |= MATCHFLG_DIRECTORY; } @@ -136,16 +147,20 @@ contained a slash. + if (!lp) + out_of_memory("make_exclude"); + lp->head = lp->tail = NULL; -+ if (asprintf(&lp->debug_type, "per-dir %s ", ret->pattern) < 0) ++ if ((cp = strrchr(ret->pattern, '/')) != NULL) ++ cp++; ++ else ++ cp = ret->pattern; ++ if (asprintf(&lp->debug_type, "per-dir %s ", cp) < 0) + out_of_memory("make_exclude"); + ret->u.mergelist = lp; + if (mergelist_cnt == mergelist_size) { -+ mergelist_size += 5; -+ mergelist_parents = realloc_array(mergelist_parents, -+ struct exclude_struct *, -+ mergelist_size); -+ if (!mergelist_parents) -+ out_of_memory("make_exclude"); ++ mergelist_size += 5; ++ mergelist_parents = realloc_array(mergelist_parents, ++ struct exclude_struct *, ++ mergelist_size); ++ if (!mergelist_parents) ++ out_of_memory("make_exclude"); + } + mergelist_parents[mergelist_cnt++] = ret; + } else { @@ -165,7 +180,7 @@ contained a slash. listp->tail->next = ret; listp->tail = ret; } -@@ -96,22 +185,156 @@ static void make_exclude(struct exclude_ +@@ -96,22 +196,180 @@ static void make_exclude(struct exclude_ static void free_exclude(struct exclude_struct *ex) { @@ -199,43 +214,60 @@ contained a slash. listp->head = listp->tail = NULL; } -+void pre_inherit_files(char *fname, unsigned int len) ++static void prep_merge_file(struct exclude_struct *ex, ++ struct exclude_list_struct *lp, int flags, ++ char *dir, unsigned int dirlen) +{ -+ char path[MAXPATHLEN], save[MAXPATHLEN]; -+ unsigned int limit; -+ int i; -+ -+ if (sanitize_paths) -+ limit = strlen(lp_path(module_id)); -+ else -+ limit = 0; -+ fname[len] = '\0'; -+ if (*fname == '/') -+ strcpy(path, fname); /* safe */ -+ else if (len == 2 && *fname == '.' && fname[1] == '/') -+ len = strlcpy(path, curr_dir, sizeof path); -+ else -+ len = pathjoin(path, sizeof path, curr_dir, fname); ++ char buf[MAXPATHLEN]; ++ char *x, *y, *fn = ex->pattern; + -+ if (len >= MAXPATHLEN || len == 0) ++ if (!(x = strrchr(fn, '/'))) + return; + -+ for (i = inherit_exclude_levels, len--; i && len > limit; ) { -+ if (path[--len] == '/') -+ i--; ++ *x = '\0'; ++ if (dir[dirlen]) ++ dir[dirlen] = '\0'; ++ pathjoin(dirbuf, MAXPATHLEN, sanitize_paths ? "" : curr_dir, dir); ++ if (dirlen == 2 && *dir == '.') { ++ int len = strlen(dirbuf); ++ dirbuf[len-2] = '\0'; + } -+ for (len++; i < inherit_exclude_levels; i++) { -+ char *s = path + len; -+ strcpy(save, s); -+ push_local_excludes(path, len); -+ strcpy(s, save); -+ if (!(s = strchr(s, '/'))) -+ break; /* Impossible ... */ -+ len = s - path + 1; ++ if (!*fn) ++ fn = "/"; ++ if (*fn == '/') ++ strcpy(buf, fn); /* safe */ ++ else ++ pathjoin(buf, MAXPATHLEN, dirbuf, fn); ++ fn = x + 1; ++ ++ clean_fname(buf); ++ if (!*buf || buf[1]) ++ strlcat(buf, "/", MAXPATHLEN); ++ for (x = buf, y = dirbuf; *x && *x == *y; x++, y++) {} ++ if (*x) ++ y += strlen(y); ++ ++ while (*y) { ++ char save[MAXPATHLEN]; ++ strcpy(save, y); /* safe */ ++ dirbuf_offset = y - dirbuf; ++ strlcpy(x, fn, MAXPATHLEN - (x - buf)); ++ lp->tail = NULL; ++ add_exclude_file(lp, buf, flags); ++ strcpy(y, save); /* safe */ ++ while ((*x++ = *y++) != '/') {} + } ++ ++ if (dirlen == 2 && *dir == '.') /* dir[dirlen-1] is always '/' */ ++ dirbuf_offset = 0; ++ else ++ dirbuf_offset = dirlen; ++ memcpy(dirbuf, dir, dirbuf_offset); ++ ++ memmove(ex->pattern, fn, strlen(fn) + 1); +} + -+void *push_local_excludes(char *fname, unsigned int offset) ++void *push_local_excludes(char *dir, unsigned int dirlen) +{ + struct mergelist_save_struct *push; + struct exclude_list_struct *ap; @@ -243,10 +275,12 @@ contained a slash. + + /* Make it easy to construct the full path for a merge-file that was + * specified with a relative path by saving off the current dir. */ -+ memcpy(dirbuf, fname, offset); -+ dirbuf_offset = offset; -+ if (dirbuf_offset == 2 && *dirbuf == '.') ++ if (dirlen == 2 && *dir == '.') /* dir[dirlen-1] is always '/' */ + dirbuf_offset = 0; ++ else ++ dirbuf_offset = dirlen; ++ memcpy(dirbuf, dir, dirbuf_offset); ++ + + if (!(push = new_array(struct mergelist_save_struct, 1))) + out_of_memory("push_local_excludes"); @@ -273,16 +307,21 @@ contained a slash. + who_am_i(), lp->debug_type); + } + -+ if (ex->match_flags & MATCHFLG_CVSIGNORE) { -+ lp->head = NULL; /* CVS doesn't inherit rules. */ ++ if (ex->match_flags & MATCHFLG_CVSIGNORE) + flags = XFLG_WORD_SPLIT | XFLG_WORDS_ONLY; -+ } else { ++ else { + flags = ex->match_flags & MATCHFLG_INCLUDE + ? XFLG_DEF_INCLUDE : 0; -+ if (inherit_exclude_levels < 0) -+ lp->head = NULL; + } -+ lp->tail = NULL; /* Switch any local rules to inherited. */ ++ ++ if (initialized_mergelist_cnt < i) { ++ prep_merge_file(ex, lp, flags, dir, dirlen); ++ initialized_mergelist_cnt = i; ++ } ++ ++ if (!(ex->match_flags & MATCHFLG_INHERIT)) ++ lp->head = NULL; ++ lp->tail = NULL; + if (strlcpy(dirbuf + dirbuf_offset, ex->pattern, + MAXPATHLEN - dirbuf_offset) < MAXPATHLEN - dirbuf_offset) + add_exclude_file(lp, dirbuf, flags); @@ -315,7 +354,7 @@ contained a slash. + clear_exclude_list(lp); + } + -+ mergelist_cnt = pop->count; ++ mergelist_cnt = initialized_mergelist_cnt = pop->count; + for (i = 0, ap = pop->array; i < mergelist_cnt; i++) { + memcpy(mergelist_parents[i]->u.mergelist, ap++, + sizeof (struct exclude_list_struct)); @@ -328,7 +367,7 @@ contained a slash. static int check_one_exclude(char *name, struct exclude_struct *ex, int name_is_dir) { -@@ -122,7 +345,7 @@ static int check_one_exclude(char *name, +@@ -122,7 +380,7 @@ static int check_one_exclude(char *name, /* If the pattern does not have any slashes AND it does not have * a "**" (which could match a slash), then we just match the * name portion of the path. */ @@ -337,7 +376,7 @@ contained a slash. if ((p = strrchr(name,'/')) != NULL) name = p+1; } -@@ -133,7 +356,8 @@ static int check_one_exclude(char *name, +@@ -133,7 +391,8 @@ static int check_one_exclude(char *name, name = full_name; } @@ -347,7 +386,7 @@ contained a slash. if (ex->match_flags & MATCHFLG_DIRECTORY && !name_is_dir) return 0; -@@ -148,9 +372,9 @@ static int check_one_exclude(char *name, +@@ -148,9 +407,9 @@ static int check_one_exclude(char *name, if (ex->match_flags & MATCHFLG_WILD) { /* A non-anchored match with an infix slash and no "**" * needs to match the last slash_cnt+1 name elements. */ @@ -359,7 +398,7 @@ contained a slash. for (p = name + strlen(name) - 1; p >= name; p--) { if (*p == '/' && !--cnt) break; -@@ -221,6 +445,13 @@ int check_exclude(struct exclude_list_st +@@ -221,6 +480,13 @@ int check_exclude(struct exclude_list_st struct exclude_struct *ent; for (ent = listp->head; ent; ent = ent->next) { @@ -373,12 +412,12 @@ contained a slash. if (check_one_exclude(name, ent, name_is_dir)) { report_exclude_result(name, ent, name_is_dir, listp->debug_type); -@@ -253,11 +484,16 @@ static const char *get_exclude_tok(const +@@ -253,12 +519,41 @@ static const char *get_exclude_tok(const p = (const char *)s; } - /* Is this a '+' or '-' followed by a space (not whitespace)? */ -+ /* Is this a +/-/. followed by a space (not whitespace)? */ ++ /* Check for a +/-/. followed by a space (not whitespace). */ if (!(xflags & XFLG_WORDS_ONLY) - && (*s == '-' || *s == '+') && s[1] == ' ') { + && (*s == '-' || *s == '+' || *s == '.') && s[1] == ' ') { @@ -388,11 +427,36 @@ contained a slash. + mflags |= MATCHFLG_MERGE_FILE; + if (xflags & XFLG_DEF_INCLUDE) + mflags |= MATCHFLG_INCLUDE; ++ while (s[2] == '-') { ++ s += 2; ++ do { ++ switch (*++s) { ++ case 'i': ++ mflags |= MATCHFLG_INHERIT; ++ break; ++ case 'p': ++ mflags |= MATCHFLG_PERDIR_MERGE; ++ break; ++ case '-': ++ if (s[1] == ' ') ++ goto done; ++ default: ++ rprintf(FERROR, ++ "invalid merge options: %s\n", ++ p); ++ exit_cleanup(RERR_SYNTAX); ++ } ++ } while (s[1] != ' '); ++ } + } ++ done: s += 2; ++ if (mflags & MATCHFLG_MERGE_FILE && *s == '.' && s[1] == '/') ++ s += 2; } else if (xflags & XFLG_DEF_INCLUDE) mflags |= MATCHFLG_INCLUDE; -@@ -292,6 +528,7 @@ void add_exclude(struct exclude_list_str + +@@ -292,6 +587,7 @@ void add_exclude(struct exclude_list_str cp = pattern; pat_len = 0; while (1) { @@ -400,66 +464,69 @@ contained a slash. cp = get_exclude_tok(cp + pat_len, &pat_len, &mflags, xflags); if (!pat_len) break; -@@ -306,11 +543,57 @@ void add_exclude(struct exclude_list_str +@@ -306,13 +602,55 @@ void add_exclude(struct exclude_list_str continue; } +- make_exclude(listp, cp, pat_len, mflags); +- +- if (verbose > 2) { +- rprintf(FINFO, "[%s] add_exclude(%.*s, %s%sclude)\n", +- who_am_i(), (int)pat_len, cp, listp->debug_type, +- mflags & MATCHFLG_INCLUDE ? "in" : "ex"); + if (mflags & MATCHFLG_MERGE_FILE) { -+ char name[MAXPATHLEN]; -+ if (pat_len >= sizeof name) { ++ char buf1[MAXPATHLEN], buf2[MAXPATHLEN]; ++ unsigned int len = pat_len; ++ const char *fn = cp; ++ if (len >= MAXPATHLEN) { + rprintf(FERROR, -+ "merge filename too long: %s\n", cp); ++ "merge filename too long: %.*s\n", ++ (int)len, fn); + continue; + } + /* We need a null-terminated version of the filename. */ -+ strlcpy(name, cp, pat_len+1); -+ if (!(xflags & XFLG_PERDIR_MERGE) -+ && (inherit_exclude_levels == -1 -+ || strchr(name, '/') != NULL)) { -+ char *mem = NULL; -+ if (*name == '/') { -+ if (sanitize_paths) { -+ mem = alloc_sanitize_path(name, -+ curr_dir); -+ cp = mem; -+ } else -+ cp = name; -+ } else { -+ if (sanitize_paths) { -+ dirbuf[dirbuf_offset] = '\0'; -+ sanitize_path(name, dirbuf); -+ } -+ if (!dirbuf_offset) -+ cp = name; -+ else if (strlcpy(dirbuf + dirbuf_offset, -+ name, MAXPATHLEN - dirbuf_offset) -+ >= MAXPATHLEN - dirbuf_offset) { -+ rprintf(FERROR, -+ "merge filename too long: %s...\n", -+ dirbuf); -+ continue; -+ } else -+ cp = dirbuf; ++ if (fn[len]) { ++ strlcpy(buf1, fn, len+1); ++ fn = buf1; ++ } ++ if (sanitize_paths) { ++ dirbuf[dirbuf_offset] = '\0'; ++ if (!sanitize_path(buf2, fn, dirbuf)) { ++ rprintf(FERROR, ++ "merge filename too long: %s\n", ++ fn); ++ continue; ++ } ++ len = strlen(fn = buf2); ++ } ++ if (*fn != '/' && dirbuf_offset ++ && (!(mflags & MATCHFLG_PERDIR_MERGE) ++ || *dirbuf == '/')) { ++ len = strlcpy(dirbuf + dirbuf_offset, fn, ++ MAXPATHLEN - dirbuf_offset) ++ + dirbuf_offset; ++ if (len >= MAXPATHLEN) { ++ rprintf(FERROR, ++ "merge filename too long: %s...\n", ++ dirbuf); ++ continue; + } -+ add_exclude_file(listp, cp, ++ fn = dirbuf; ++ } ++ if (mflags & MATCHFLG_PERDIR_MERGE) ++ make_exclude(listp, fn, len, mflags); ++ else { ++ add_exclude_file(listp, fn, + xflags | XFLG_FATAL_ERRORS); -+ if (mem) -+ free(mem); -+ continue; + } -+ } -+ - make_exclude(listp, cp, pat_len, mflags); - - if (verbose > 2) { -- rprintf(FINFO, "[%s] add_exclude(%.*s, %s%sclude)\n", -+ rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s%sclude)\n", - who_am_i(), (int)pat_len, cp, listp->debug_type, -+ mflags & MATCHFLG_MERGE_FILE ? "FILE " : "", - mflags & MATCHFLG_INCLUDE ? "in" : "ex"); ++ continue; } ++ ++ make_exclude(listp, cp, pat_len, mflags); } -@@ -343,6 +626,11 @@ void add_exclude_file(struct exclude_lis + } + +@@ -343,6 +681,11 @@ void add_exclude_file(struct exclude_lis return; } @@ -471,7 +538,7 @@ contained a slash. while (1) { char *s = line; int ch, overflow = 0; -@@ -402,7 +690,11 @@ void send_exclude_list(int f) +@@ -402,7 +745,11 @@ void send_exclude_list(int f) if (ent->match_flags & MATCHFLG_INCLUDE) { write_int(f, l + 2); write_buf(f, "+ ", 2); @@ -484,31 +551,32 @@ contained a slash. write_int(f, l + 2); write_buf(f, "- ", 2); } else -@@ -411,6 +703,13 @@ void send_exclude_list(int f) +@@ -411,6 +758,14 @@ void send_exclude_list(int f) } write_int(f, 0); + ++#if 0 + /* If we're the receiver and we don't need the excludes, dump them. */ + if (!am_sender && (!delete_mode || delete_excluded)) { + clear_exclude_list(&exclude_list); -+ inherit_exclude_levels = 0; + mergelist_cnt = 0; + } ++#endif } -@@ -443,6 +742,7 @@ void add_cvs_excludes(void) +@@ -443,6 +798,7 @@ void add_cvs_excludes(void) char fname[MAXPATHLEN]; char *p; -+ add_exclude(&exclude_list, ". .cvsignore", XFLG_PERDIR_MERGE); ++ add_exclude(&exclude_list, ". -p .cvsignore", 0); add_exclude(&exclude_list, default_cvsignore, XFLG_WORD_SPLIT | XFLG_WORDS_ONLY); ---- orig/flist.c 2004-07-17 15:20:05 -+++ flist.c 2004-08-03 17:14:17 -@@ -39,8 +39,6 @@ extern int module_id; +--- orig/flist.c 2004-08-05 21:57:29 ++++ flist.c 2004-08-06 07:56:11 +@@ -39,10 +39,9 @@ extern int module_id; extern int ignore_errors; extern int numeric_ids; @@ -516,15 +584,10 @@ contained a slash. - extern int recurse; extern char curr_dir[MAXPATHLEN]; ++extern unsigned int curr_dir_len; extern char *files_from; -@@ -59,6 +57,7 @@ extern int implied_dirs; - extern int copy_links; - extern int copy_unsafe_links; - extern int protocol_version; -+extern int inherit_exclude_levels; - extern int sanitize_paths; - extern int delete_excluded; - extern int orig_umask; + extern int filesfrom_fd; + @@ -66,7 +65,6 @@ extern int list_only; extern struct exclude_list_struct exclude_list; @@ -578,7 +641,7 @@ contained a slash. char *p; d = opendir(dir); -@@ -996,18 +982,13 @@ static void send_directory(int f, struct +@@ -996,18 +982,7 @@ static void send_directory(int f, struct offset++; } @@ -594,17 +657,11 @@ contained a slash. - full_fname(fname)); - } - } -+ if (inherit_exclude_levels > 0) { -+ pre_inherit_files(fname, offset); -+ inherit_exclude_levels = 0; -+ } else if (inherit_exclude_levels < -1) -+ inherit_exclude_levels = -1; -+ + save_excludes = push_local_excludes(fname, offset); for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) { char *dname = d_name(di); -@@ -1028,6 +1009,8 @@ static void send_directory(int f, struct +@@ -1028,6 +1003,8 @@ static void send_directory(int f, struct rsyserr(FERROR, errno, "readdir(%s)", dir); } @@ -613,102 +670,84 @@ contained a slash. closedir(d); } ---- orig/options.c 2004-08-03 15:41:32 -+++ options.c 2004-08-04 08:36:48 -@@ -51,6 +51,7 @@ int preserve_gid = 0; - int preserve_times = 0; - int update_only = 0; - int cvs_exclude = 0; -+int inherit_exclude_levels = -9999; - int dry_run = 0; - int local_server = 0; - int ignore_times = 0; -@@ -287,6 +288,7 @@ void usage(enum logcode F) - rprintf(F," --exclude-from=FILE exclude patterns listed in FILE\n"); - rprintf(F," --include=PATTERN don't exclude files matching PATTERN\n"); - rprintf(F," --include-from=FILE don't exclude patterns listed in FILE\n"); -+ rprintf(F," --inherit=DEPTH inherit contents of per-dir merge files\n"); - rprintf(F," --files-from=FILE read FILE for list of source-file names\n"); - rprintf(F," -0, --from0 all *-from file lists are delimited by nulls\n"); - rprintf(F," --version print version number\n"); -@@ -319,7 +321,7 @@ void usage(enum logcode F) - - enum {OPT_VERSION = 1000, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, - OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED, OPT_LINK_DEST, -- OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, -+ OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_INHERIT, OPT_MODIFY_WINDOW, - OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_TIMEOUT, - OPT_REFUSED_BASE = 9000}; - -@@ -344,6 +346,7 @@ static struct poptOption long_options[] - {"include", 0, POPT_ARG_STRING, 0, OPT_INCLUDE, 0, 0 }, - {"exclude-from", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE_FROM, 0, 0 }, - {"include-from", 0, POPT_ARG_STRING, 0, OPT_INCLUDE_FROM, 0, 0 }, -+ {"inherit", 0, POPT_ARG_STRING, 0, OPT_INHERIT, 0, 0 }, - {"safe-links", 0, POPT_ARG_NONE, &safe_symlinks, 0, 0, 0 }, - {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 }, - {"backup", 'b', POPT_ARG_NONE, &make_backups, 0, 0, 0 }, -@@ -566,6 +569,31 @@ int parse_arguments(int *argc, const cha - XFLG_FATAL_ERRORS | XFLG_DEF_INCLUDE); - break; - -+ case OPT_INHERIT: -+ arg = poptGetOptArg(pc); -+ if (isdigit(*arg)) -+ inherit_exclude_levels = atoi(arg); -+ else if (*arg == '.') { -+ if (!*++arg) -+ inherit_exclude_levels = 0; -+ else if (*arg == '.') { -+ inherit_exclude_levels = 1; -+ arg++; -+ while (strncmp(arg, "/..", 3) == 0) { -+ inherit_exclude_levels++; -+ arg += 3; -+ } -+ if (*arg) -+ inherit_exclude_levels = -1; -+ } +@@ -1047,6 +1024,7 @@ struct file_list *send_file_list(int f, + char *p, *dir, olddir[sizeof curr_dir]; + char lastpath[MAXPATHLEN] = ""; + struct file_list *flist; ++ void *first_push = NULL; + int64 start_write; + int use_ff_fd = 0; + +@@ -1067,6 +1045,14 @@ struct file_list *send_file_list(int f, + exit_cleanup(RERR_FILESELECT); + } + use_ff_fd = 1; ++ if (curr_dir_len < MAXPATHLEN - 1) { ++ char buf[MAXPATHLEN]; ++ strcpy(buf, curr_dir); ++ buf[curr_dir_len] = '/'; ++ buf[curr_dir_len+1] = '\0'; ++ first_push = push_local_excludes(buf, ++ curr_dir_len+1); + } -+ if (inherit_exclude_levels < 0) { -+ snprintf(err_buf, sizeof err_buf, -+ "Invalid argument given to --inherit.\n"); -+ return 0; + } + } + +@@ -1097,6 +1083,22 @@ struct file_list *send_file_list(int f, + } + } + ++ if (!first_push) { ++ char ch; ++ if ((p = strrchr(fname, '/')) != NULL) { ++ dir = fname; ++ p++; ++ } else { ++ dir = "./"; ++ p = dir + 2; + } -+ break; ++ if ((ch = *p) != '\0') ++ *p = '\0'; ++ first_push = push_local_excludes(dir, p - dir); ++ if (ch) ++ *p = ch; ++ } + - case 'h': - usage(FINFO); - exit_cleanup(0); -@@ -969,6 +997,12 @@ void server_options(char **args,int *arg - if (x != 1) - args[ac++] = argstr; - -+ if (inherit_exclude_levels >= 0) { -+ if (asprintf(&arg, "--inherit=%d", inherit_exclude_levels) < 0) -+ goto oom; -+ args[ac++] = arg; -+ } + if (link_stat(fname, &st, keep_dirlinks) != 0) { + if (f != -1) { + io_error |= IOERR_GENERAL; +--- orig/options.c 2004-08-05 21:57:29 ++++ options.c 2004-08-05 21:57:19 +@@ -389,6 +389,7 @@ static struct poptOption long_options[] + {"ignore-errors", 0, POPT_ARG_NONE, &ignore_errors, 0, 0, 0 }, + {"blocking-io", 0, POPT_ARG_VAL, &blocking_io, 1, 0, 0 }, + {"no-blocking-io", 0, POPT_ARG_VAL, &blocking_io, 0, 0, 0 }, ++ {0, 'E', POPT_ARG_NONE, 0, 'E', 0, 0 }, + {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 }, + {"config", 0, POPT_ARG_STRING, &config_file, 0, 0, 0 }, + {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 }, +@@ -589,6 +590,11 @@ int parse_arguments(int *argc, const cha + am_sender = 1; + break; + ++ case 'E': ++ add_exclude(&exclude_list, ++ ". -ip /.rsync-excludes", 0); ++ break; + - if (block_size) { - if (asprintf(&arg, "-B%u", block_size) < 0) - goto oom; + case 'P': + do_progress = 1; + keep_partial = 1; --- orig/rsync.h 2004-08-03 15:41:32 -+++ rsync.h 2004-07-03 20:21:27 -@@ -108,6 +108,7 @@ - #define XFLG_DEF_INCLUDE (1<<1) - #define XFLG_WORDS_ONLY (1<<2) - #define XFLG_WORD_SPLIT (1<<3) -+#define XFLG_PERDIR_MERGE (1<<4) - - #define PERMS_REPORT (1<<0) - #define PERMS_SKIP_MTIME (1<<1) -@@ -499,11 +500,16 @@ struct map_struct { ++++ rsync.h 2004-08-06 06:23:10 +@@ -499,11 +499,18 @@ struct map_struct { #define MATCHFLG_INCLUDE (1<<4) /* this is an include, not an exclude */ #define MATCHFLG_DIRECTORY (1<<5) /* this matches only directories */ #define MATCHFLG_CLEAR_LIST (1<<6) /* this item is the "!" token */ +#define MATCHFLG_MERGE_FILE (1<<7) /* specifies a file to merge */ +#define MATCHFLG_CVSIGNORE (1<<8) /* parse this as a .cvsignore file */ ++#define MATCHFLG_PERDIR_MERGE (1<<9) /* content is inherited by subdirs */ ++#define MATCHFLG_INHERIT (1<<10)/* content is inherited by subdirs */ struct exclude_struct { struct exclude_struct *next; char *pattern; @@ -722,45 +761,16 @@ contained a slash. struct exclude_list_struct { --- orig/rsync.yo 2004-08-03 15:34:32 -+++ rsync.yo 2004-08-04 08:36:18 -@@ -334,6 +334,7 @@ verb( - --exclude-from=FILE exclude patterns listed in FILE ++++ rsync.yo 2004-08-06 09:32:25 +@@ -335,6 +335,7 @@ verb( --include=PATTERN don't exclude files matching PATTERN --include-from=FILE don't exclude patterns listed in FILE -+ --inherit=DEPTH inherit contents of per-dir merge files --files-from=FILE read FILE for list of source-file names ++ -E same as --exclude='. -pi /.rsync-excludes' -0 --from0 all file lists are delimited by nulls --version print version number -@@ -711,6 +712,28 @@ dit(bf(--include-from=FILE)) This specif - from a file. - If em(FILE) is "-" the list will be read from standard input. - -+dit(bf(--inherit=DEPTH)) Using this option allows you to specify that the -+contents of per-directory merge files is inherited by the subdirectories of -+the spot where the rules were read in. If a subdirectory has its own -+per-directory merge file, its contents are prefixed to the inherited rules, -+which gives the newest rules a higher priority than the inherited rules. -+ -+The DEPTH value tells rsync how much deeper than the root directory of the -+transfer should be scanned for merge files. If you don't need any higher -+directories scanned, use "." (dot). If you want the parent directory -+scanned, specify ".." (dot dot) or "1" (i.e. one directory higher). You -+can continue to specify either more ".."s (separated by slashes) or simply -+supply the count of how many parent-directory levels should be scanned. -+The reason this is useful is that you may wish to transfer just a small -+portion of a larger tree of files, but to be sure to get all the -+appropriate exclude rules, you need to make rsync read in all the -+merge files from the top of the tree of related files. -+ -+Note also that you can eliminate all the inherited rules for the current -+per-directory ruleset by putting the list-clearing token (!) in the file. -+This only clears the rules in the current per-directory sub-list for the -+current directory and its subdirectories. -+ - dit(bf(--files-from=FILE)) Using this option allows you to specify the - exact list of files to transfer (as read from the specified FILE or "-" - for standard input). It also tweaks the default behavior of rsync to make -@@ -1086,6 +1109,11 @@ itemize( + --daemon run as an rsync daemon +@@ -1086,6 +1087,11 @@ itemize( then it is always considered an exclude pattern, even if specified as part of an include option. The prefix is discarded before matching. @@ -772,37 +782,44 @@ contained a slash. it() if the pattern is a single exclamation mark ! then the current include/exclude list is reset, removing all previously defined patterns. ) -@@ -1138,6 +1166,67 @@ itemize( +@@ -1138,6 +1144,97 @@ itemize( it would be excluded by the "*") ) +manpagesection(MERGING EXCLUDE FILES) + -+You can merge whole files into an exclude file using a rule that starts -+with a ". " (a dot followed by a space) and has a filename in place of the -+pattern. There are two types of merge rules, single-instance and -+per-directory: -+ -+itemize( -+ it() If the filename has no slashes in it, it is a per-directory merge; -+ rsync scans every directory that it traverses for the named file, merging -+ its contents (when it exists) at the start of this per-directory -+ sub-list. -+ -+ it() If a filename has a slash in it, it is a single-instance merge; the -+ named file's contents will be merged into the current exclude file, -+ replacing the merge rule. Thus, you should use the name ./foo instead of -+ foo if you don't want to scan for "foo" in all the subdirectories in the -+ transferred tree of directories. -+) ++You can merge whole files into an exclude file by specifying a rule that ++starts with a ". " (a dot followed by a space) and putting a filename in ++place of the pattern. The filename may be preceded by one or more options: ++ ++startdit() ++ ++dit(bf(-i)) All subdirectories of the current directory inherit the rules we ++read in from this file. When applied to a per-directory merge file, the ++rules read in are prefixed to the inherited rules from a parent directory, ++which gives the newest rules a higher priority than the inherited rules. ++ ++Note also that you can eliminate all the inherited rules for a directory ++and its subdirectories by putting the list-clearing token ! at the start of ++a per-directory file. This only clears the rules in the current sub-list, ++not all the rules. ++ ++dit(bf(-p)) Make the file a per-directory merge-file. Rsync will scan ++every directory that it traverses for the named file, merging its contents ++when the file exists. ++ ++dit(bf(--)) End the scanning of options. Useful if you want to specify a ++filename that begins with a dash. ++ ++enddit() + +Here's an example exclude file (which you'd specify via the normal -+--exclude-from option): ++--exclude-from=FILE option): + +verb( + . /home/user/.global_excludes + - *.gz -+ . .excl ++ . -pi .excl + + *.[ch] + - *.o +) @@ -810,38 +827,61 @@ contained a slash. +This will merge the contents of the /home/user/.global_excludes file at the +start of the list and also turns the ".excl" filename into a per-directory +exclude file whose local contents will be merged into the list in place of -+the .excl line. ++the .excl line. The rules read in from the .global_excludes file affect ++all the directories because it was read in before the directory scanning ++began (just as if it were an --exclude-from option). The rules read in ++from each directory's .excl file are inherited by that directory's ++subdirectories because the -i option was specified. ++ ++Note also that the parsing of any merge-file named ".cvsignore" is always ++done in a CVS-compatible manner, even if -C wasn't specified. This means ++that its rules are always excludes (even if an include option specified the ++file), tokens are split on whitespace, the rules are never inherited, and ++no special characters are honored. + +Additionally, you can affect where the --cvs-exclude (-C) option's +inclusion of a per-directory .cvsignore file gets placed into your rules by -+adding an explicit merge rule for ".cvsignore". For instance, specifying -+this: ++adding an explicit per-directory merge rule for ".cvsignore". For example, ++specifying this: + +verb( -+ rsync -avC --exclude='. .cvsignore' --exclude-from=foo a/ b ++ rsync -avC --exclude='. -p .cvsignore' --exclude-from=foo a/ b +) + +will merge all the per-directory .cvsignore rules at the start of your list +rather than at the end. This allows their dir-specific rules to supersede +your rules instead of being subservient to them. (The global rules taken -+from the $HOME/.cvsignore file and from $CVSIGNORE are not affected by ++from the $HOME/.cvsignore file and from $CVSIGNORE are not repositioned by +this.) + -+Note also that the parsing of any merge-file named ".cvsignore" is always -+done in a CVS-compatible manner, even if -C wasn't specified (i.e. the -+rules are always exclude rules (even when specified by an include option); -+they are split on whitespace; the contents is never inherited; and no -+prefixes, list-clearing tokens, or comment characters are honored). ++If a per-directory merge file is specified with a path that is a parent ++directory of the first transfer directory, rsync will scan all the parent ++dirs from that starting point to the transfer directory for the indicated ++per-directory file. For instance, the -E option is an abbreviation for ++this command: ++ ++verb( ++ --exclude='. -pi /.rsync-excludes' ++) ++ ++That exclude tells rsync to scan for the file .rsync-excludes in all ++directories from the root up through the source of the transfer. For ++example: ++ ++verb( ++ rsync -avE /src/path/ /dest/dir ++) + -+See the --inherit option for how to make the rules read from a -+per-directory merge file inherited by all the subdirectories of the -+spot where the per-directory rule file was found. ++For the above command rsync would try to open /.rsync-excludes and ++/src/.rsync-excludes before looking for the file in /src/path. If you ++specify the long version of the option manually and leave off the leading ++slash from "/.rsync-excludes", rsync would start scanning in /src/path. + manpagesection(BATCH MODE) bf(Note:) Batch mode should be considered experimental in this version --- orig/testsuite/exclude.test 2004-05-29 21:25:45 -+++ testsuite/exclude.test 2004-07-03 20:21:27 ++++ testsuite/exclude.test 2004-08-06 09:02:29 @@ -23,19 +23,47 @@ export HOME CVSIGNORE makepath "$fromdir/foo/down/to/you" makepath "$fromdir/bar/down/to/foo/too" @@ -861,7 +901,7 @@ contained a slash. +EOF +cat >"$fromdir/bar/.excl" <"$fromdir/bar/down/to/home-cvs-exclude" @@ -885,7 +925,7 @@ contained a slash. echo one-in-one-out >"$fromdir/mid/.cvsignore" echo cvsin >"$fromdir/mid/one-for-all" +cat >"$fromdir/mid/.excl" <"$fromdir/mid/for/one-in-one-out" echo expunged >"$fromdir/mid/for/foo/extra" @@ -910,7 +950,7 @@ contained a slash. +# Now, test if rsync excludes the same files, this time with a merge-exclude +# file. + -+checkit "$RSYNC -avv --exclude='. .excl' --exclude-from=\"$excl\" --inherit=. \ ++checkit "$RSYNC -avv --exclude='. -pi .excl' --exclude-from=\"$excl\" \ + --delete-excluded \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir" + # The script would have aborted on error, so getting here means we've won.