--- exclude.c 22 Apr 2004 09:58:15 -0000 1.70 +++ exclude.c 22 Apr 2004 10:32:52 -0000 @@ -27,15 +27,17 @@ #include "rsync.h" extern int verbose; +extern int protocol_version; extern int eol_nulls; extern int list_only; extern int recurse; extern char curr_dir[]; -struct exclude_list_struct exclude_list = { 0, 0, "" }; -struct exclude_list_struct local_exclude_list = { 0, 0, "local-cvsignore " }; -struct exclude_list_struct server_exclude_list = { 0, 0, "server " }; +struct exclude_list_struct exclude_list = { 0, 0, 0, "" }; +struct exclude_list_struct perdir_exclude_list = { 0, 0, 0, "perdir " }; +struct exclude_list_struct local_exclude_list = { 0, 0, 0, "local-cvsignore " }; +struct exclude_list_struct server_exclude_list = { 0, 0, 0, "server " }; char *exclude_path_prefix = NULL; /** Build an exclude structure given a exclude pattern */ @@ -85,6 +87,8 @@ static void make_exclude(struct exclude_ for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++) ret->slash_cnt++; + ret->next = listp->extra; + if (!listp->tail) listp->head = listp->tail = ret; else { @@ -108,12 +112,26 @@ void free_exclude_list(struct exclude_li who_am_i(), listp->debug_type); } + if (listp->extra) { + if (listp->tail) + listp->tail->next = NULL; + else + listp->head = NULL; + } + for (ent = listp->head; ent; ent = next) { next = ent->next; free_exclude(ent); } - listp->head = listp->tail = NULL; + clear_exclude_list(listp, NULL); +} + +void clear_exclude_list(struct exclude_list_struct *listp, + struct exclude_struct *extra) +{ + listp->head = listp->extra = extra; + listp->tail = NULL; } static int check_one_exclude(char *name, struct exclude_struct *ex, @@ -215,21 +233,25 @@ static void report_exclude_result(char c /* * Return true if file NAME is defined to be excluded by the specified - * exclude list. + * exclude list. Returns -1 for exclude, 0 for normal include, or 1 + * for super include. */ -int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir) +int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir, + int current_exclusion) { struct exclude_struct *ent; for (ent = listp->head; ent; ent = ent->next) { + if (current_exclusion < 0 && ent->include < 2) + continue; if (check_one_exclude(name, ent, name_is_dir)) { report_exclude_result(name, ent, name_is_dir, listp->debug_type); - return !ent->include; + return ent->include - 1; } } - return 0; + return current_exclusion; } @@ -255,10 +277,10 @@ 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)? */ if (!(xflags & XFLG_NO_PREFIXES) - && (*s == '-' || *s == '+') && s[1] == ' ') { - *incl_ptr = *s == '+'; + && (*s == '-' || *s == '+' || *s == '&') && s[1] == ' ') { + *incl_ptr = *s == '+' ? 1 : *s == '&' ? 2 : 0; s += 2; } else *incl_ptr = xflags & XFLG_DEF_INCLUDE; @@ -390,8 +412,12 @@ void send_exclude_list(int f) if (ent->include) { write_int(f, l + 2); - write_buf(f, "+ ", 2); - } else if ((*p == '-' || *p == '+') && p[1] == ' ') { + if (ent->include > 1 && protocol_version >= 28) + write_buf(f, "& ", 2); + else + write_buf(f, "+ ", 2); + } else if ((*p == '-' || *p == '+' || *p == '&') + && p[1] == ' ') { write_int(f, l + 2); write_buf(f, "- ", 2); } else --- flist.c 22 Apr 2004 09:58:18 -0000 1.215 +++ flist.c 22 Apr 2004 10:32:52 -0000 @@ -40,6 +40,7 @@ extern int ignore_errors; extern int numeric_ids; extern int cvs_exclude; +extern const char *perdir_exclude_from; extern int recurse; extern char curr_dir[MAXPATHLEN]; @@ -66,6 +67,7 @@ extern int write_batch; extern struct exclude_list_struct exclude_list; extern struct exclude_list_struct server_exclude_list; +extern struct exclude_list_struct perdir_exclude_list; extern struct exclude_list_struct local_exclude_list; int io_error; @@ -211,6 +213,10 @@ int link_stat(const char *path, STRUCT_S */ static int check_exclude_file(char *fname, int is_dir, int exclude_level) { + static struct exclude_list_struct *elist[] = { + &exclude_list, &perdir_exclude_list, &local_exclude_list, NULL }; + int i, rc; + #if 0 /* This currently never happens, so avoid a useless compare. */ if (exclude_level == NO_EXCLUDES) return 0; @@ -227,16 +233,18 @@ static int check_exclude_file(char *fnam } } if (server_exclude_list.head - && check_exclude(&server_exclude_list, fname, is_dir)) + && check_exclude(&server_exclude_list, fname, is_dir, 0) < 0) return 1; if (exclude_level != ALL_EXCLUDES) return 0; - if (exclude_list.head && check_exclude(&exclude_list, fname, is_dir)) - return 1; - if (local_exclude_list.head - && check_exclude(&local_exclude_list, fname, is_dir)) - return 1; - return 0; + for (i = 0, rc = 0; elist[i]; i++) { + if (!elist[i]->head) + continue; + rc = check_exclude(elist[i], fname, is_dir, rc); + if (rc > 0) + return 0; + } + return rc < 0; } /* used by the one_file_system code */ @@ -944,8 +952,12 @@ void send_file_name(int f, struct file_l if (recursive && S_ISDIR(file->mode) && !(file->flags & FLAG_MOUNT_POINT)) { struct exclude_list_struct last_list = local_exclude_list; - local_exclude_list.head = local_exclude_list.tail = NULL; + struct exclude_list_struct sub_list = perdir_exclude_list; + clear_exclude_list(&local_exclude_list, NULL); + clear_exclude_list(&perdir_exclude_list, sub_list.head); send_directory(f, flist, f_name_to(file, fbuf)); + free_exclude_list(&perdir_exclude_list); + perdir_exclude_list = sub_list; free_exclude_list(&local_exclude_list); local_exclude_list = last_list; } @@ -991,6 +1003,18 @@ static void send_directory(int f, struct io_error |= IOERR_GENERAL; rprintf(FINFO, "cannot cvs-exclude in long-named directory %s\n", + full_fname(fname)); + } + } + + if (perdir_exclude_from) { + if (strlcpy(p, perdir_exclude_from, MAXPATHLEN - offset) + < MAXPATHLEN - offset) + add_exclude_file(&perdir_exclude_list, fname, 0); + else { + io_error |= IOERR_GENERAL; + rprintf(FINFO, + "cannot perdir-exclude in long-named directory %s\n", full_fname(fname)); } } --- options.c 17 Apr 2004 17:07:23 -0000 1.147 +++ options.c 22 Apr 2004 10:32:53 -0000 @@ -70,6 +70,7 @@ int am_server = 0; int am_sender = 0; int am_generator = 0; char *files_from = NULL; +char *perdir_exclude_from = NULL; int filesfrom_fd = -1; char *remote_filesfrom_file = NULL; int eol_nulls = 0; @@ -273,6 +274,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," --perdir-exclude-from=F look in each dir for exclude-file F\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"); @@ -335,6 +337,7 @@ static struct poptOption long_options[] {"dry-run", 'n', POPT_ARG_NONE, &dry_run, 0, 0, 0 }, {"sparse", 'S', POPT_ARG_NONE, &sparse_files, 0, 0, 0 }, {"cvs-exclude", 'C', POPT_ARG_NONE, &cvs_exclude, 0, 0, 0 }, + {"perdir-exclude-from",0,POPT_ARG_STRING, &perdir_exclude_from, 0, 0, 0 }, {"update", 'u', POPT_ARG_NONE, &update_only, 0, 0, 0 }, {"links", 'l', POPT_ARG_NONE, &preserve_links, 0, 0, 0 }, {"copy-links", 'L', POPT_ARG_NONE, ©_links, 0, 0, 0 }, --- proto.h 22 Apr 2004 09:58:09 -0000 1.189 +++ proto.h 22 Apr 2004 10:32:53 -0000 @@ -52,7 +52,10 @@ int daemon_main(void); void setup_protocol(int f_out,int f_in); int claim_connection(char *fname,int max_connections); void free_exclude_list(struct exclude_list_struct *listp); -int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir); +void clear_exclude_list(struct exclude_list_struct *listp, + struct exclude_struct *extra); +int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir, + int current_exclusion); void add_exclude(struct exclude_list_struct *listp, const char *pattern, int xflags); void add_exclude_file(struct exclude_list_struct *listp, const char *fname, --- rsync.h 22 Apr 2004 09:58:24 -0000 1.198 +++ rsync.h 22 Apr 2004 10:32:53 -0000 @@ -502,6 +502,7 @@ struct exclude_struct { struct exclude_list_struct { struct exclude_struct *head; struct exclude_struct *tail; + struct exclude_struct *extra; char *debug_type; }; --- rsync.yo 17 Apr 2004 18:40:16 -0000 1.159 +++ rsync.yo 22 Apr 2004 10:32:54 -0000 @@ -331,6 +331,7 @@ verb( --exclude-from=FILE exclude patterns listed in FILE --include=PATTERN don't exclude files matching PATTERN --include-from=FILE don't exclude patterns listed in FILE + --perdir-exclude-from=F look in each dir for exclude file F --files-from=FILE read FILE for list of source-file names -0 --from0 all file lists are delimited by nulls --version print version number @@ -672,6 +673,15 @@ dit(bf(--include-from=FILE)) This specif from a file. If em(FILE) is bf(-) the list will be read from standard input. +dit(bf(--perdir-exclude-from=FILE)) In any given directory, patterns +listed in FILE (one per line) are excluded from the file lists +associated with that directory and all of its subdirectories. Rules +specified in a subdir take precedence over rules evaluated in a parent +dir (think of each file's rules being prefixed to the prior rules). If +you don't want to inherit the parent-dir rules, start the FILE with a +single exclamation mark ! to clear them out. You may prefix names with +an explicit exclude, include, or super-include prefix (see below). + 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 stdin). It also tweaks the default behavior of rsync to make @@ -711,7 +721,8 @@ was located on the remote "src" host. dit(bf(-0, --from0)) This tells rsync that the filenames it reads from a file are terminated by a null ('\0') character, not a NL, CR, or CR+LF. -This affects --exclude-from, --include-from, and --files-from. +This affects --exclude-from, --include-from, --perdir-exclude-from, +and --files-from. It does not affect --cvs-exclude (since all names read from a .cvsignore file are split on whitespace). @@ -998,6 +1009,12 @@ itemize( it() if the pattern starts with "- " (a minus followed by a space) then it is always considered an exclude pattern, even if specified as part of an include option. The prefix is discarded before matching. + + it() if the pattern starts with "& " (an ampersand followed by a space) + then it is considered a super-include pattern. A super-include will + override an exclude from another exclude list (a normal include only + overrides an exclusion from further down in the same list). The + prefix is discarded before matching. it() if the pattern is a single exclamation mark ! then the current include/exclude list is reset, removing all previously defined patterns. --- t_stub.c 22 Apr 2004 09:58:11 -0000 1.7 +++ t_stub.c 22 Apr 2004 10:32:54 -0000 @@ -46,7 +46,7 @@ struct exclude_list_struct server_exclud } int check_exclude(UNUSED(struct exclude_list_struct *listp), UNUSED(char *name), - UNUSED(int name_is_dir)) + UNUSED(int name_is_dir), UNUSED(int current_exclusion)) { /* This function doesn't really get called in this test context, so * just return 0. */ --- util.c 22 Apr 2004 09:58:21 -0000 1.137 +++ util.c 22 Apr 2004 10:32:54 -0000 @@ -476,7 +476,7 @@ static int exclude_server_path(char *arg if (server_exclude_list.head) { for (s = arg; (s = strchr(s, '/')) != NULL; ) { *s = '\0'; - if (check_exclude(&server_exclude_list, arg, 1)) { + if (check_exclude(&server_exclude_list, arg, 1, 0) < 0) { /* We must leave arg truncated! */ return 1; }