make proto
This patch adds the ability to merge rules into your excludes/includes
-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 that dir and its subdirs.
+using either "+m FILE" (for merged includes) or "-m FILE" (for merged
+excludes). It also lets you specify either "+p FILE" or "-p FILE" in
+order to specify a per-directory merge file -- one that will be looked
+for in every sub-directory that rsync visits, and the rules found in
+that subdirectory's file will affect that dir and (if desired) its
+subdirs.
For example:
- rsync -av --exclude='. -p .excl' from/ to
+ rsync -av --exclude='-p .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
this:
+ *.c
- . -p .excl2
- . .excl3
+ -p .excl2
+ -m .excl3
*.o
/foobar
Then the file ".excl2" will also be read in from the current dir and all
-its subdirs (due to the -p option). The file ".excl3" would just be
-read in from the current dir. The exclusion of "foobar" will only
-happen in that .excl file's directory because the rule is anchored (so
-that's how you can make rules local instead of inherited).
+its subdirs. The file ".excl3" would just be read in from the current
+dir. The exclusion of "foobar" will only happen in that .excl file's
+directory because the rule is anchored, so that's how you can make rules
+local instead of inherited.
..wayne..
---- orig/clientserver.c 2004-08-02 02:29:16
+--- orig/clientserver.c 2005-01-01 21:11:00
+++ clientserver.c 2004-08-10 15:44:15
-@@ -48,12 +48,14 @@ extern int no_detach;
+@@ -49,12 +49,14 @@ extern int no_detach;
extern int default_af_hint;
extern char *bind_address;
extern struct exclude_list_struct server_exclude_list;
/**
* Run a client connected to an rsyncd. The alternative to this
* function for remote-shell connections is do_cmd().
-@@ -300,26 +302,28 @@ static int rsync_module(int f_in, int f_
+@@ -304,26 +306,28 @@ static int rsync_module(int f_in, int f_
/* TODO: Perhaps take a list of gids, and make them into the
* supplementary groups. */
log_init();
---- orig/exclude.c 2004-08-10 18:17:01
-+++ exclude.c 2004-08-10 18:16:41
-@@ -30,13 +30,68 @@ extern int verbose;
+--- orig/exclude.c 2005-01-13 23:15:56
++++ exclude.c 2005-01-15 05:29:58
+@@ -30,13 +30,69 @@ extern int verbose;
extern int eol_nulls;
extern int list_only;
extern int recurse;
+ * of this path prefix. The path is always absolute. */
+static char dirbuf[MAXPATHLEN+1];
+static unsigned int dirbuf_len = 0;
++static int dirbuf_depth;
+
+/* This is True when we're scanning parent dirs for per-dir merge-files. */
+static BOOL parent_dirscan = False;
+ * head -> Parent1 -> Parent2 -> NULL head -> L1 -> L2 -> P1 -> P2 -> NULL
+ * tail -> NULL tail ---------^
+ *
-+ * This means that anyone wanting to traverse the whole list to USE it just
++ * This means that anyone wanting to traverse the whole list to use it just
+ * needs to start at the head and use the "next" pointers until it goes
+ * NULL. To add new local content, we insert the item after the tail item
+ * and update the tail (obviously, if "tail" was NULL, we insert it at the
/** Build an exclude structure given an exclude pattern. */
static void make_exclude(struct exclude_list_struct *listp, const char *pat,
-@@ -46,23 +101,50 @@ static void make_exclude(struct exclude_
+@@ -46,23 +102,50 @@ static void make_exclude(struct exclude_
const char *cp;
unsigned int ex_len;
+
+ if (mflags & MATCHFLG_MERGE_FILE) {
+ int i;
-+ /* If the local include file was already mentioned, don't
++ /* If the local merge file was already mentioned, don't
+ * add it again. */
+ for (i = 0; i < mergelist_cnt; i++) {
+ struct exclude_struct *ex = mergelist_parents[i];
strlcpy(ret->pattern + ex_len, pat, pat_len + 1);
pat_len += ex_len;
-@@ -81,14 +163,40 @@ static void make_exclude(struct exclude_
+@@ -81,14 +164,40 @@ static void make_exclude(struct exclude_
mflags |= MATCHFLG_DIRECTORY;
}
listp->tail->next = ret;
listp->tail = ret;
}
-@@ -96,22 +204,265 @@ static void make_exclude(struct exclude_
+@@ -96,22 +205,267 @@ static void make_exclude(struct exclude_
static void free_exclude(struct exclude_struct *ex)
{
+ strlcpy(to, merge_file, *len_ptr + 1);
+ merge_file = to;
+ }
-+ if (!sanitize_path(fn, merge_file, r, dirbuf + module_dirlen)) {
++ if (!sanitize_path(fn, merge_file, r, dirbuf_depth)) {
+ rprintf(FERROR, "merge-file name overflows: %s\n",
+ merge_file);
+ return NULL;
+ }
+ } else {
+ strlcpy(fn, merge_file, len_ptr ? *len_ptr + 1 : MAXPATHLEN);
-+ clean_fname(fn);
++ clean_fname(fn, 1);
+ }
+
+ fn_len = strlen(fn);
+ }
+ memcpy(buf, dirbuf + prefix_skip, dirbuf_len - prefix_skip);
+ memcpy(buf + dirbuf_len - prefix_skip, fn, fn_len + 1);
-+ fn_len = clean_fname(buf);
++ fn_len = clean_fname(buf, 1);
+
+ done:
+ if (len_ptr)
+ memcpy(dirbuf, curr_dir, curr_dir_len);
+ dirbuf[curr_dir_len] = '/';
+ len = curr_dir_len + 1;
-+ if (dirlen >= MAXPATHLEN - len)
-+ dirlen = MAXPATHLEN - len - 1;
++ if (len + dirlen >= MAXPATHLEN)
++ dirlen = 0;
+ } else
+ len = 0;
+ memcpy(dirbuf + len, dir, dirlen);
+ dirbuf[dirlen + len] = '\0';
-+ dirbuf_len = clean_fname(dirbuf);
++ dirbuf_len = clean_fname(dirbuf, 1);
+ if (dirbuf_len > 1 && dirbuf[dirbuf_len-1] == '.'
+ && dirbuf[dirbuf_len-2] == '/')
+ dirbuf_len -= 2;
+ dirbuf[dirbuf_len++] = '/';
+ dirbuf[dirbuf_len] = '\0';
++ if (sanitize_paths)
++ dirbuf_depth = count_dir_elements(dirbuf + module_dirlen);
+}
+
+/* This routine takes a per-dir merge-file entry and finishes its setup.
+ else
+ pathjoin(buf, MAXPATHLEN, dirbuf, x);
+
-+ len = clean_fname(buf);
++ len = clean_fname(buf, 1);
+ if (len != 1 && len < MAXPATHLEN-1) {
+ buf[len++] = '/';
+ buf[len] = '\0';
static int check_one_exclude(char *name, struct exclude_struct *ex,
int name_is_dir)
{
-@@ -125,13 +476,14 @@ static int check_one_exclude(char *name,
+@@ -125,13 +479,14 @@ 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. */
name = full_name;
}
-@@ -148,9 +500,9 @@ static int check_one_exclude(char *name,
+@@ -148,9 +503,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. */
for (p = name + strlen(name) - 1; p >= name; p--) {
if (*p == '/' && !--cnt)
break;
-@@ -221,6 +573,13 @@ int check_exclude(struct exclude_list_st
+@@ -221,6 +576,13 @@ int check_exclude(struct exclude_list_st
struct exclude_struct *ent;
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);
-@@ -253,11 +612,36 @@ static const char *get_exclude_tok(const
+@@ -254,12 +616,28 @@ static const char *get_exclude_tok(const
p = (const char *)s;
}
- /* Is this a '+' or '-' followed by a space (not whitespace)? */
-+ /* Check for a +/-/. followed by a space (not whitespace). */
- if (!(xflags & XFLG_WORDS_ONLY)
+- if (!(xflags & XFLG_WORDS_ONLY)
- && (*s == '-' || *s == '+') && s[1] == ' ') {
-+ && (*s == '-' || *s == '+' || *s == '.') && s[1] == ' ') {
++ /* Check for a leading '+' or '-'. */
++ if (!(xflags & XFLG_WORDS_ONLY) && (*s == '-' || *s == '+')) {
if (*s == '+')
mflags |= MATCHFLG_INCLUDE;
-+ else if (*s == '.') {
-+ mflags |= MATCHFLG_MERGE_FILE;
-+ if (xflags & XFLG_DEF_INCLUDE)
-+ mflags |= MATCHFLG_INCLUDE;
-+ while (s[2] == '-') {
-+ s += 2;
-+ do {
-+ switch (*++s) {
-+ case 'p':
-+ mflags |= MATCHFLG_PERDIR_MERGE
-+ | MATCHFLG_FINISH_SETUP;
-+ break;
-+ case '-':
-+ if (s[1] == ' ')
-+ goto done;
-+ default:
-+ rprintf(FERROR,
-+ "invalid merge options: %s\n",
-+ p);
-+ exit_cleanup(RERR_SYNTAX);
-+ }
-+ } while (s[1] != ' ');
+- s += 2;
++ while (*++s != ' ') {
++ switch (*s) {
++ case 'p':
++ mflags |= MATCHFLG_MERGE_FILE
++ | MATCHFLG_PERDIR_MERGE
++ | MATCHFLG_FINISH_SETUP;
++ break;
++ case 'm':
++ mflags |= MATCHFLG_MERGE_FILE;
++ break;
++ default:
++ rprintf(FERROR,
++ "invalid include/exclude option after %c: %c\n",
++ *p, *s);
++ exit_cleanup(RERR_SYNTAX);
+ }
+ }
-+ done:
- s += 2;
++ s++;
} else if (xflags & XFLG_DEF_INCLUDE)
mflags |= MATCHFLG_INCLUDE;
-@@ -273,6 +657,8 @@ static const char *get_exclude_tok(const
+ if (xflags & XFLG_DIRECTORY)
+@@ -276,6 +654,8 @@ static const char *get_exclude_tok(const
- if (*p == '!' && len == 1 && !(xflags & XFLG_WORDS_ONLY))
+ if (*p == '!' && len == 1)
mflags |= MATCHFLG_CLEAR_LIST;
+ if (xflags & XFLG_ABS_PATH)
+ mflags |= MATCHFLG_ABS_PATH;
*len_ptr = len;
*flag_ptr = mflags;
-@@ -284,7 +670,7 @@ void add_exclude(struct exclude_list_str
+@@ -287,7 +667,7 @@ void add_exclude(struct exclude_list_str
int xflags)
{
unsigned int pat_len, mflags;
if (!pattern)
return;
-@@ -292,9 +678,15 @@ void add_exclude(struct exclude_list_str
+@@ -295,9 +675,15 @@ void add_exclude(struct exclude_list_str
cp = pattern;
pat_len = 0;
while (1) {
if (mflags & MATCHFLG_CLEAR_LIST) {
if (verbose > 2) {
-@@ -306,13 +698,24 @@ void add_exclude(struct exclude_list_str
+@@ -309,13 +695,24 @@ void add_exclude(struct exclude_list_str
continue;
}
}
}
-@@ -321,7 +724,7 @@ void add_exclude_file(struct exclude_lis
+@@ -324,7 +721,7 @@ void add_exclude_file(struct exclude_lis
int xflags)
{
FILE *fp;
- char line[MAXPATHLEN+3]; /* Room for "x " prefix and trailing slash. */
-+ char line[MAXPATHLEN+7]; /* Room for prefix chars and trailing slash. */
++ char line[MAXPATHLEN+4]; /* Room for prefix chars and trailing slash. */
char *eob = line + sizeof line - 1;
int word_split = xflags & XFLG_WORD_SPLIT;
-@@ -342,6 +745,12 @@ void add_exclude_file(struct exclude_lis
+@@ -345,6 +742,12 @@ void add_exclude_file(struct exclude_lis
}
return;
}
while (1) {
char *s = line;
-@@ -402,7 +811,21 @@ void send_exclude_list(int f)
- if (ent->match_flags & MATCHFLG_INCLUDE) {
- write_int(f, l + 2);
- write_buf(f, "+ ", 2);
-- } else if ((*p == '-' || *p == '+') && p[1] == ' ') {
-+ } else if (ent->match_flags & MATCHFLG_MERGE_FILE) {
+@@ -402,7 +805,20 @@ void send_exclude_list(int f)
+ p[l] = '\0';
+ }
+
+- if (ent->match_flags & MATCHFLG_INCLUDE) {
++ if (ent->match_flags & MATCHFLG_MERGE_FILE) {
+ char buf[32], *op = buf;
-+ *op++ = '.';
-+ *op++ = ' ';
-+ if (ent->match_flags & MATCHFLG_PERDIR_MERGE) {
++ if (ent->match_flags & MATCHFLG_INCLUDE)
++ *op++ = '+';
++ else
+ *op++ = '-';
++ if (ent->match_flags & MATCHFLG_PERDIR_MERGE)
+ *op++ = 'p';
-+ if (*p == '-')
-+ *op++ = '-';
-+ *op++ = ' ';
-+ }
++ else
++ *op++ = 'm';
++ *op++ = ' ';
+ write_int(f, l + (op - buf));
+ write_buf(f, buf, op - buf);
-+ } else if ((*p == '-' || *p == '+' || *p == '.')
-+ && p[1] == ' ') {
++ } else if (ent->match_flags & MATCHFLG_INCLUDE) {
write_int(f, l + 2);
- write_buf(f, "- ", 2);
- } else
-@@ -443,6 +866,7 @@ void add_cvs_excludes(void)
+ write_buf(f, "+ ", 2);
+ } else if (*p == '-' || *p == '+') {
+@@ -419,7 +835,7 @@ void send_exclude_list(int f)
+
+ void recv_exclude_list(int f)
+ {
+- char line[MAXPATHLEN+3]; /* Room for "x " prefix and trailing slash. */
++ char line[MAXPATHLEN+4]; /* Room for prefix and trailing slash. */
+ unsigned int l;
+
+ while ((l = read_int(f)) != 0) {
+@@ -446,6 +862,7 @@ void add_cvs_excludes(void)
char fname[MAXPATHLEN];
char *p;
-+ add_exclude(&exclude_list, ". -p .cvsignore", 0);
++ add_exclude(&exclude_list, "-p .cvsignore", 0);
add_exclude(&exclude_list, default_cvsignore,
XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
---- orig/flist.c 2004-08-05 21:57:29
-+++ flist.c 2004-08-10 17:20:11
-@@ -39,10 +39,9 @@ extern int module_id;
+--- orig/flist.c 2005-01-01 21:11:00
++++ flist.c 2004-08-12 18:59:28
+@@ -40,10 +40,9 @@ extern int module_id;
extern int ignore_errors;
extern int numeric_ids;
extern char *files_from;
extern int filesfrom_fd;
-@@ -66,7 +65,6 @@ extern int list_only;
+@@ -67,7 +66,6 @@ extern int list_only;
extern struct exclude_list_struct exclude_list;
extern struct exclude_list_struct server_exclude_list;
int io_error;
-@@ -221,8 +219,6 @@ int link_stat(const char *path, STRUCT_S
+@@ -223,8 +221,6 @@ int link_stat(const char *path, STRUCT_S
*/
static int check_exclude_file(char *fname, int is_dir, int exclude_level)
{
#if 0 /* This currently never happens, so avoid a useless compare. */
if (exclude_level == NO_EXCLUDES)
return 0;
-@@ -244,10 +240,7 @@ static int check_exclude_file(char *fnam
+@@ -246,10 +242,7 @@ static int check_exclude_file(char *fnam
if (exclude_level != ALL_EXCLUDES)
return 0;
if (exclude_list.head
return 1;
return 0;
}
-@@ -573,7 +566,7 @@ void receive_file_entry(struct file_stru
- clean_fname(thisname);
-
- if (sanitize_paths)
-- sanitize_path(thisname, thisname, NULL);
-+ sanitize_path(thisname, thisname, "", NULL);
-
- if ((basename = strrchr(thisname, '/')) != NULL) {
- dirname_len = ++basename - thisname; /* counts future '\0' */
-@@ -671,7 +664,7 @@ void receive_file_entry(struct file_stru
- file->u.link = bp;
- read_sbuf(f, bp, linkname_len - 1);
- if (sanitize_paths)
-- sanitize_path(bp, bp, lastdir);
-+ sanitize_path(bp, bp, "", lastdir);
- bp += linkname_len;
- }
- #endif
-@@ -761,7 +754,7 @@ struct file_struct *make_file(char *fnam
- }
- clean_fname(thisname);
- if (sanitize_paths)
-- sanitize_path(thisname, thisname, NULL);
-+ sanitize_path(thisname, thisname, "", NULL);
-
- memset(sum, 0, SUM_LENGTH);
-
-@@ -954,15 +947,7 @@ void send_file_name(int f, struct file_l
+@@ -983,15 +976,7 @@ void send_file_name(int f, struct file_l
if (recursive && S_ISDIR(file->mode)
&& !(file->flags & FLAG_MOUNT_POINT)) {
}
}
-@@ -973,6 +958,7 @@ static void send_directory(int f, struct
+@@ -1002,6 +987,7 @@ static void send_directory(int f, struct
struct dirent *di;
char fname[MAXPATHLEN];
unsigned int offset;
char *p;
d = opendir(dir);
-@@ -996,18 +982,7 @@ static void send_directory(int f, struct
+@@ -1025,18 +1011,7 @@ static void send_directory(int f, struct
offset++;
}
for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
char *dname = d_name(di);
-@@ -1028,6 +1003,8 @@ static void send_directory(int f, struct
+@@ -1057,6 +1032,8 @@ static void send_directory(int f, struct
rsyserr(FERROR, errno, "readdir(%s)", dir);
}
closedir(d);
}
-@@ -1047,6 +1024,7 @@ struct file_list *send_file_list(int f,
+@@ -1076,6 +1053,7 @@ struct file_list *send_file_list(int f,
char *p, *dir, olddir[sizeof curr_dir];
char lastpath[MAXPATHLEN] = "";
struct file_list *flist;
int64 start_write;
int use_ff_fd = 0;
-@@ -1067,6 +1045,10 @@ struct file_list *send_file_list(int f,
+@@ -1096,6 +1074,10 @@ struct file_list *send_file_list(int f,
exit_cleanup(RERR_FILESELECT);
}
use_ff_fd = 1;
}
}
-@@ -1077,13 +1059,13 @@ struct file_list *send_file_list(int f,
- if (use_ff_fd) {
- if (read_filesfrom_line(filesfrom_fd, fname) == 0)
- break;
-- sanitize_path(fname, fname, NULL);
-+ sanitize_path(fname, fname, "", NULL);
- } else {
- if (argc-- == 0)
- break;
- strlcpy(fname, *argv++, MAXPATHLEN);
- if (sanitize_paths)
-- sanitize_path(fname, fname, NULL);
-+ sanitize_path(fname, fname, "", NULL);
- }
-
- l = strlen(fname);
-@@ -1097,6 +1079,15 @@ struct file_list *send_file_list(int f,
+@@ -1126,6 +1108,15 @@ struct file_list *send_file_list(int f,
}
}
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-09 18:22:26
-@@ -287,6 +287,7 @@ void usage(enum logcode F)
+--- orig/options.c 2005-01-15 04:40:15
++++ options.c 2005-01-13 23:52:00
+@@ -296,6 +296,7 @@ void usage(enum logcode F)
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," --files-from=FILE read FILE for list of source-file names\n");
-+ rprintf(F," -E same as --exclude='. -p /.rsync-excludes'\n");
++ rprintf(F," -E same as --exclude='-p /.rsync-excludes'\n");
rprintf(F," -0, --from0 all *-from file lists are delimited by nulls\n");
rprintf(F," --version print version number\n");
- rprintf(F," --daemon run as an rsync daemon\n");
-@@ -389,6 +390,7 @@ static struct poptOption long_options[]
+ rprintf(F," --port=PORT specify double-colon alternate port number\n");
+@@ -393,6 +394,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 },
++ {0, 'E', POPT_ARG_NONE, 0, 'E', 0, 0 },
+ {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 },
{"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
-@@ -589,6 +591,11 @@ int parse_arguments(int *argc, const cha
+ {"log-format", 0, POPT_ARG_STRING, &log_format, 0, 0, 0 },
+@@ -668,6 +670,11 @@ int parse_arguments(int *argc, const cha
am_sender = 1;
break;
+ case 'E':
+ add_exclude(&exclude_list,
-+ ". -p /.rsync-excludes", 0);
++ "-p /.rsync-excludes", 0);
+ break;
+
case 'P':
do_progress = 1;
keep_partial = 1;
-@@ -728,17 +735,17 @@ int parse_arguments(int *argc, const cha
- if (sanitize_paths) {
- int i;
- for (i = *argc; i-- > 0; )
-- (*argv)[i] = sanitize_path(NULL, (*argv)[i], NULL);
-+ (*argv)[i] = sanitize_path(NULL, (*argv)[i], "", NULL);
- if (tmpdir)
-- tmpdir = sanitize_path(NULL, tmpdir, "");
-+ tmpdir = sanitize_path(NULL, tmpdir, NULL, NULL);
- if (partial_dir)
-- partial_dir = sanitize_path(NULL, partial_dir, "");
-+ partial_dir = sanitize_path(NULL, partial_dir, NULL, NULL);
- if (compare_dest)
-- compare_dest = sanitize_path(NULL, compare_dest, "");
-+ compare_dest = sanitize_path(NULL, compare_dest, NULL, NULL);
- if (backup_dir)
-- backup_dir = sanitize_path(NULL, backup_dir, "");
-+ backup_dir = sanitize_path(NULL, backup_dir, NULL, NULL);
- if (files_from)
-- files_from = sanitize_path(NULL, files_from, "");
-+ files_from = sanitize_path(NULL, files_from, NULL, NULL);
- }
- if (server_exclude_list.head && !am_sender) {
- struct exclude_list_struct *elp = &server_exclude_list;
---- orig/rsync.h 2004-08-03 15:41:32
-+++ rsync.h 2004-08-08 06:07:01
-@@ -108,6 +108,7 @@
- #define XFLG_DEF_INCLUDE (1<<1)
+--- orig/rsync.h 2005-01-10 00:21:12
++++ rsync.h 2004-09-22 08:48:53
+@@ -111,6 +111,7 @@
#define XFLG_WORDS_ONLY (1<<2)
#define XFLG_WORD_SPLIT (1<<3)
-+#define XFLG_ABS_PATH (1<<4)
+ #define XFLG_DIRECTORY (1<<4)
++#define XFLG_ABS_PATH (1<<5)
#define PERMS_REPORT (1<<0)
#define PERMS_SKIP_MTIME (1<<1)
-@@ -499,11 +500,18 @@ struct map_struct {
+@@ -510,11 +511,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 */
};
struct exclude_list_struct {
---- orig/rsync.yo 2004-08-03 15:34:32
-+++ rsync.yo 2004-08-10 17:17:33
-@@ -335,6 +335,7 @@ verb(
+--- orig/rsync.yo 2005-01-15 04:36:32
++++ rsync.yo 2005-01-14 00:10:38
+@@ -366,6 +366,7 @@ verb(
--include=PATTERN don't exclude files matching PATTERN
--include-from=FILE don't exclude patterns listed in FILE
--files-from=FILE read FILE for list of source-file names
-+ -E same as --exclude='. -p /.rsync-excludes'
++ -E same as --exclude='-p /.rsync-excludes'
-0 --from0 all file lists are delimited by nulls
--version print version number
- --daemon run as an rsync daemon
-@@ -979,24 +980,32 @@ The exclude and include patterns specifi
+ --port=PORT specify double-colon alternate port number
+@@ -1114,24 +1115,32 @@ The exclude and include patterns specifi
selection of which files to transfer and which files to skip.
Rsync builds an ordered list of include/exclude options as specified on
Let's say that we want to match two source files, one with an absolute
path of "/home/me/foo/bar", and one with a path of "/home/you/bar/baz".
-@@ -1043,23 +1052,27 @@ because rsync did not descend through th
+@@ -1178,23 +1187,27 @@ because rsync did not descend through th
hierarchy.
Note also that the --include and --exclude options take one pattern
it() if the pattern ends with a / then it will only match a
directory, not a file, link, or device.
-@@ -1072,22 +1085,31 @@ itemize(
+@@ -1207,22 +1220,44 @@ itemize(
single asterisk pattern "*" will stop at slashes.
it() if the pattern contains a / (not counting a trailing /) or a "**"
remember that the algorithm is applied recursively so "full filename" can
actually be any portion of a path below the starting directory.
- it() if the pattern starts with "+ " (a plus followed by a space)
- then it is always considered an include pattern, even if specified as
+- it() if the pattern starts with "+ " (a plus followed by a space)
+- then it is always considered an include pattern, even if specified as
- part of an exclude option. The prefix is discarded before matching.
-+ part of an exclude option. (The prefix is discarded before matching.)
-
- it() if the pattern starts with "- " (a minus followed by a space)
- then it is always considered an exclude pattern, even if specified as
+-
+- 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.
-+ part of an include option. (The prefix is discarded before matching.)
-+
-+ it() if the pattern starts with ". " (a dot followed by a space) then its
-+ pattern is taken to be a merge-file that is read in to supplement the
-+ current rules. See the section on MERGED EXCLUDE FILES for more
-+ information.
-
- it() if the pattern is a single exclamation mark ! then the current
++ it() if the pattern starts with "+" (a plus), the actual pattern begins
++ after the first space, and is always considered to be an include pattern,
++ even if specified as part of an exclude file/option. Option letters may
++ follow the "+" prior to the separating space (see below).
++
++ it() if the pattern starts with "-" (a minus), the actual pattern begins
++ after the first space, and is always considered to be an exclude pattern,
++ even if specified as part of an include file/option. Option letters may
++ follow the "-" prior to the separating space (see below).
+
+- it() if the pattern is a single exclamation mark ! then the current
++ it() if the pattern is a "!" (single exclamation mark) then the current
include/exclude list is reset, removing all previously defined patterns.
+ The "current" list is either the global list of rules (which are
+ specified via options) or a set of per-directory rules (which are
+ inherited in their own sub-list, so a subdirectory can use this to
+ clear out the parent's rules).
++
++)
++
++For a line that starts with a "+" (plus) or a "-" (minus), the following
++option letters may be suffixed:
++
++itemize(
++
++ it() An "m" means that the string following the space is to be taken to
++ be a merge-file that is read in to supplement the current rules. See the
++ section on MERGED EXCLUDE FILES for more information.
++
++ it() A "p" means that the string following the space is to be taken to be
++ a per-directory merge-file that is read in to supplement the current
++ rules. See the section on MERGED EXCLUDE FILES for more information.
++
)
The +/- rules are most useful in a list that was read from a file, allowing
-@@ -1134,8 +1156,160 @@ itemize(
+@@ -1269,8 +1304,157 @@ itemize(
it() --include "*/" --include "*.c" --exclude "*" would include all
directories and C source files
it() --include "foo/" --include "foo/bar.c" --exclude "*" would include
+
+manpagesection(MERGED EXCLUDE FILES)
+
-+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. There are two kinds of merged exclude files --
-+single-instance and per-directory. The choice is made via an option
-+placed prior to the merge-file name:
++You can merge whole files into an exclude file by specifying either the
++"m" or "p" option following the initial "+" or "-", and putting a filename
++in place of the pattern. There are two kinds of merged exclude files --
++single-instance ("m") and per-directory ("p"). For example:
+
-+startdit()
++verb(
++ -m /etc/rsync/default.excludes
++ +p .per-dir-includes
++)
+
-+dit(bf(-p)) Make the file a per-directory merge-file. Rsync will scan
++For a per-directory merge file, rsync will scan
+every directory that it traverses for the named file, merging its contents
+when the file exists. These exclude files must exist on the sending side
+because it is the sending side that is being scanned for available files
+if you want them to affect what files don't get deleted (see PER-DIRECTORY
+EXCLUDES AND DELETE below).
+
-+dit(bf(--)) End the scanning of options. Useful if you want to specify a
-+filename that begins with a dash.
-+
-+enddit()
-+
+Per-directory rules are inherited in all subdirectories of the directory
+where the merge-file was found. Each subdirectory's rules are prefixed
+to the inherited rules from the parent directories, which gives the
+--exclude-from=FILE option:
+
+verb(
-+ . /home/user/.global_excludes
++ -m /home/user/.global_excludes
+ *.gz
-+ . -p .excl
++ +p .incl
+ + *.[ch]
+ *.o
+)
+
+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. All the merged rules default to being exclude rules because
-+an exclude statement was used to specify them. Rules read in from the
-+.global_excludes file are anchored just like all other global rules.
++start of the list and also turns the ".incl" filename into a per-directory
++include file. All rules read in prior to the start of the directory scan
++follow the global anchoring rules (i.e. a leading slash matches at the root
++of the transfer).
+
+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
+this command:
+
+verb(
-+ --exclude='. -p /.rsync-excludes'
++ --exclude='-p /.rsync-excludes'
+)
+
+That exclude tells rsync to scan for the file .rsync-excludes in all
+
+verb(
+ rsync -avE /src/path/ /dest/dir
-+ rsync -av --exclude='. -p ../../.rsync-excludes' /src/path/ /dest/dir
-+ rsync -av --exclude='. -p .rsync-excludes' /src/path/ /dest/dir
++ rsync -av --exclude='-p ../../.rsync-excludes' /src/path/ /dest/dir
++ rsync -av --exclude='-p .rsync-excludes' /src/path/ /dest/dir
+)
+
+The first two commands above will look for ".rsync-excludes" in "/" and
+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), patterns are split on whitespace, the rules are never
-+inherited, and no special characters are honored (e.g. no comments, no "!",
-+etc.).
++inherited, and no special characters are honored except for "!" (e.g. no
++comments, and no +/- prefixes).
+
+Additionally, you can affect where the --cvs-exclude (-C) option's
+inclusion of the per-directory .cvsignore file gets placed into your rules
+by adding your own explicit per-directory merge rule for ".cvsignore".
-+Without this, rsync would add its this rule at the end of all your other
++Without this, rsync would add this rule at the end of all your other
+rules (giving it a lower priority than your command-line rules). For
+example:
+
+verb(
-+ rsync -avC --exclude='. -p .cvsignore' --exclude-from=foo a/ b
++ rsync -avC --exclude='-p .cvsignore' --exclude-from=foo a/ b
+)
+
+The above will merge all the per-directory .cvsignore rules at the start of
+without affecting the transfer:
+
+verb(
-+ rsync -av --exclude='. -p .excl' --exclude=.excl host:src/dir /dest
++ rsync -av --exclude='-p .excl' --exclude=.excl host:src/dir /dest
+)
+
+However, if you want to do a delete on the receiving side AND you want some
+ rsync -avE --delete-after host:src/dir /dest
+)
+
-+However, if you the merge files are not a part of the transfer, you'll need
++However, if the merge files are not a part of the transfer, you'll need
+to either use a global exclude rule (i.e. specified on the command line),
+or you'll need to maintain your own per-directory merge files on the
+receiving side. An example of the first is this (assume that the remote
+.ctrl files exclude themselves):
+
+verb(
-+ rsync -av --exclude='. -p .ctrl' --exclude-from=/my/extra.rules
++ rsync -av --exclude='-p .ctrl' --exclude-from=/my/extra.rules
+ --delete host:src/dir /dest
+)
+
+transfer, but the rules are subservient to the rules merged from the .ctrl
+files because they were specified after the per-directory merge rule.
+
-+In the final example, the remote side is excluding the .rsync-excludes
++In one final example, the remote side is excluding the .rsync-excludes
+files from the transfer, but we want to use our own .rsync-excludes files
+to control what gets deleted on the receiving side. To do this we must
+specifically exclude the per-directory merge files (so that they don't get
manpagesection(BATCH MODE)
--- orig/testsuite/exclude.test 2004-05-29 21:25:45
-+++ testsuite/exclude.test 2004-08-08 06:35:15
++++ testsuite/exclude.test 2005-01-14 00:14:33
@@ -23,19 +23,47 @@ export HOME CVSIGNORE
makepath "$fromdir/foo/down/to/you"
makepath "$fromdir/bar/down/to/foo/too"
+EOF
+cat >"$fromdir/bar/.excl" <<EOF
+home-cvs-exclude
-+. -p .excl2
++-p .excl2
++ to
+EOF
echo cvsout >"$fromdir/bar/down/to/home-cvs-exclude"
echo one-in-one-out >"$fromdir/mid/.cvsignore"
echo cvsin >"$fromdir/mid/one-for-all"
+cat >"$fromdir/mid/.excl" <<EOF
-+. -p .cvsignore
++-p .cvsignore
+EOF
echo cvsin >"$fromdir/mid/for/one-in-one-out"
echo expunged >"$fromdir/mid/for/foo/extra"
+# Now, test if rsync excludes the same files, this time with a merge-exclude
+# file.
+
-+checkit "$RSYNC -avv --exclude='. -p .excl' --exclude-from=\"$excl\" \
++checkit "$RSYNC -avv --exclude='-p .excl' --exclude-from=\"$excl\" \
+ --delete-excluded \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
+
# The script would have aborted on error, so getting here means we've won.
exit 0
---- orig/util.c 2004-08-09 21:07:10
-+++ util.c 2004-08-09 21:07:25
-@@ -524,7 +524,7 @@ static void glob_expand_one(char *s, cha
- s = ".";
-
- if (sanitize_paths)
-- s = sanitize_path(NULL, s, NULL);
-+ s = sanitize_path(NULL, s, "", NULL);
- else
- s = strdup(s);
-
-@@ -706,18 +706,16 @@ unsigned int clean_fname(char *name)
- * "/" (either removing it or expanding it) and any leading or embedded
- * ".." components that attempt to escape past the module's top dir.
- *
-- * If dest is NULL, a buffer is allocated to hold the result. If dest is
-- * the same buffer as p (the path) OR if reldir is NULL, a leading slash
-- * is dropped instead of being expanded to be the module's top dir.
-+ * If dest is NULL, a buffer is allocated to hold the result. It is legal
-+ * to call with the dest and the path (p) pointing to the same buffer, but
-+ * rootdir is ignored to avoid expansion of the string.
-+ *
-+ * The rootdir string contains a value to use in place of a leading slash.
-+ * Specify NULL to get the default of lp_path(module_id).
- *
- * If reldir is non-NULL (and non-empty), it is a sanitized directory that
- * the path will be relative to, so allow as many '..'s at the beginning of
-- * the path as there are components in reldir. This is used for symbolic
-- * link targets. If reldir is non-null and the path began with "/", to be
-- * completely like a chroot we should add in depth levels of ".." at the
-- * beginning of the path, but that would blow the assumption that the path
-- * doesn't grow and it is not likely to end up being a valid symlink
-- * anyway, so just do the normal removal of the leading "/" instead.
-+ * the path as there are components in reldir.
- *
- * While we're at it, remove double slashes and "." components like
- * clean_fname() does, but DON'T remove a trailing slash because that is
-@@ -725,7 +723,8 @@ unsigned int clean_fname(char *name)
- *
- * If the resulting path would be empty, change it into ".".
- */
--char *sanitize_path(char *dest, const char *p, const char *reldir)
-+char *sanitize_path(char *dest, const char *p, const char *rootdir,
-+ const char *reldir)
- {
- char *start, *sanp;
- int depth = 0;
-@@ -734,8 +733,10 @@ char *sanitize_path(char *dest, const ch
-
- if (dest != p) {
- int plen = strlen(p);
-- if (*p == '/' && reldir) {
-- rlen = strlen(lp_path(module_id));
-+ if (*p == '/') {
-+ if (!rootdir)
-+ rootdir = lp_path(module_id);
-+ rlen = strlen(rootdir);
- reldir = NULL;
- p++;
- }
-@@ -745,7 +746,7 @@ char *sanitize_path(char *dest, const ch
- } else if (!(dest = new_array(char, rlen + plen + 1)))
- out_of_memory("sanitize_path");
- if (rlen) {
-- memcpy(dest, lp_path(module_id), rlen);
-+ memcpy(dest, rootdir, rlen);
- if (rlen > 1)
- dest[rlen++] = '/';
- }