This patch adds a --last-match command line option to RSYNC 2.6.3 which switches the include/exclude pattern processing from "first-match" (the default) to "last-match" semantics plus the possibility to specify short-circuit patterns (which stop processing immediately if matching) with the operators "-!" and "+!" in addition to the regular operators "-" and "+". The "last-match" is a super-set of the "first-match" semantics, providing more flexible include/exclude specifications by allowing arbitrary nesting of matches. Ralf S. Engelschall rse@engelschall.com 03-Jan-2005 Index: options.c --- options.c.orig 2004-09-23 19:39:05 +0200 +++ options.c 2005-01-02 18:57:15 +0100 @@ -96,6 +96,7 @@ int checksum_seed = 0; int inplace = 0; unsigned int block_size = 0; +int last_match = 0; /** Network address family. **/ @@ -286,6 +287,7 @@ 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," --last-match perform a last-match include/exclude checking\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"); @@ -316,7 +318,7 @@ rprintf(F,"See http://rsync.samba.org/ for updates, bug reports, and answers\n"); } -enum {OPT_VERSION = 1000, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, +enum {OPT_VERSION = 1000, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, OPT_LAST_MATCH, OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED, OPT_LINK_DEST, OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_TIMEOUT, @@ -343,6 +345,7 @@ {"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 }, + {"last-match", 0, POPT_ARG_NONE, 0, OPT_LAST_MATCH, 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 }, @@ -564,6 +567,10 @@ ? XFLG_DEF_INCLUDE : 0)); break; + case OPT_LAST_MATCH: + last_match = 1; + break; + case 'h': usage(FINFO); exit_cleanup(0); Index: rsync.h --- rsync.h.orig 2004-09-22 06:10:10 +0200 +++ rsync.h 2005-01-02 19:00:57 +0100 @@ -500,6 +500,7 @@ #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_SHORT_CIRCUIT (1<<7) /* this item stops processing */ struct exclude_struct { struct exclude_struct *next; char *pattern; Index: exclude.c --- exclude.c.orig 2004-09-22 06:11:15 +0200 +++ exclude.c 2005-01-03 15:47:03 +0100 @@ -30,6 +30,7 @@ extern int eol_nulls; extern int list_only; extern int recurse; +extern int last_match; extern char curr_dir[]; @@ -219,14 +220,23 @@ int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir) { struct exclude_struct *ent; + struct exclude_struct *ent_last = NULL; for (ent = listp->head; ent; ent = ent->next) { if (check_one_exclude(name, ent, name_is_dir)) { - report_exclude_result(name, ent, name_is_dir, - listp->debug_type); - return ent->match_flags & MATCHFLG_INCLUDE ? 1 : -1; + if (last_match && !(ent->match_flags & MATCHFLG_SHORT_CIRCUIT)) { + ent_last = ent; + } + else { + report_exclude_result(name, ent, name_is_dir, listp->debug_type); + return ent->match_flags & MATCHFLG_INCLUDE ? 1 : -1; + } } } + if (ent_last != NULL) { + report_exclude_result(name, ent_last, name_is_dir, listp->debug_type); + return ent_last->match_flags & MATCHFLG_INCLUDE ? 1 : -1; + } return 0; } @@ -259,6 +269,12 @@ if (*s == '+') mflags |= MATCHFLG_INCLUDE; s += 2; + } else if (!(xflags & XFLG_WORDS_ONLY) + && last_match && strlen(s) >= 3 && (s[0] == '-' || s[0] == '+') && s[1] == '!' && s[2] == ' ') { + mflags |= MATCHFLG_SHORT_CIRCUIT; + if (*s == '+') + mflags |= MATCHFLG_INCLUDE; + s += 3; } else if (xflags & XFLG_DEF_INCLUDE) mflags |= MATCHFLG_INCLUDE; if (xflags & XFLG_DIRECTORY) @@ -323,7 +339,7 @@ int xflags) { FILE *fp; - char line[MAXPATHLEN+3]; /* Room for "x " prefix and trailing slash. */ + char line[MAXPATHLEN+4]; /* Room for "x " prefix and trailing slash. */ char *eob = line + sizeof line - 1; int word_split = xflags & XFLG_WORD_SPLIT; @@ -403,10 +419,16 @@ if (ent->match_flags & MATCHFLG_INCLUDE) { write_int(f, l + 2); - write_buf(f, "+ ", 2); + if (ent->match_flags & MATCHFLG_SHORT_CIRCUIT) + write_buf(f, "+! ", 3); + else + write_buf(f, "+ ", 2); } else if ((*p == '-' || *p == '+') && p[1] == ' ') { write_int(f, l + 2); - write_buf(f, "- ", 2); + if (ent->match_flags & MATCHFLG_SHORT_CIRCUIT) + write_buf(f, "-! ", 3); + else + write_buf(f, "- ", 2); } else write_int(f, l); write_buf(f, p, l);