+ *.c
. -p .excl2
- . -i .excl3
+ . .excl3
*.o
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).
+but its rules would be inherited by all its subdirectories (because it
+is being read into a per-dir file that has the -i option set).
..wayne..
--- orig/exclude.c 2004-08-05 23:16:37
-+++ exclude.c 2004-08-06 23:22:00
-@@ -27,17 +27,66 @@
++++ exclude.c 2004-08-07 08:32:01
+@@ -27,17 +27,82 @@
#include "rsync.h"
extern int verbose;
+ int count;
+};
+
++/* The dirbuf is set by push_local_excludes() to the current subdirectory
++ * relative to curr_dir that is being processed. The path always has a
++ * trailing slash appended, and the variable dirbuf_offset contains the
++ * length of this path prefix (i.e. it is an offset where you can copy a
++ * filename to create a pathname that can be opened for reading/writing.
++ *
++ * The path is normally relative (e.g. "sub/dir/foo"), but it is set to an
++ * absolute path when the push code is working on a parent-dir scan of dirs
++ * that might be higher than the root of the transfer. In that case, the
++ * path is absolute, and any newly-created per-dir merge files will (at
++ * least temporarily) get an absolute file name so that we know at what
++ * point in the hierarchy it first makes an appearance. */
+static char dirbuf[MAXPATHLEN];
+static unsigned int dirbuf_offset = 0;
++
++/* This array contains a list of all the currently active per-dir merge
++ * files. This makes it easier to save the appropriate values when we
++ * "push" down into each subdirectory. */
+static struct exclude_struct **mergelist_parents;
+static int mergelist_cnt = 0;
+static int mergelist_size = 0;
/** Build an exclude structure given an exclude pattern. */
static void make_exclude(struct exclude_list_struct *listp, const char *pat,
unsigned int pat_len, unsigned int mflags)
-@@ -46,6 +95,31 @@ static void make_exclude(struct exclude_
+@@ -46,6 +111,31 @@ static void make_exclude(struct exclude_
const char *cp;
unsigned int ex_len;
ret = new(struct exclude_struct);
if (!ret)
out_of_memory("make_exclude");
-@@ -81,14 +155,40 @@ static void make_exclude(struct exclude_
+@@ -81,14 +171,40 @@ static void make_exclude(struct exclude_
mflags |= MATCHFLG_DIRECTORY;
}
listp->tail->next = ret;
listp->tail = ret;
}
-@@ -96,22 +196,247 @@ static void make_exclude(struct exclude_
+@@ -96,22 +212,255 @@ static void make_exclude(struct exclude_
static void free_exclude(struct exclude_struct *ex)
{
listp->head = listp->tail = NULL;
}
-+/* This returns a fully expanded filename for the merge-file name if the
-+ * name has any slashes in it, otherwise it returns the original name. */
++/* This returns an expanded (absolute) filename for the merge-file name if
++ * the name has any slashes in it OR if the dirbuf value is absolute;
++ * otherwise it returns the original merge_file name. If the len_ptr value
++ * is non-NULL the merge_file name is not null terminated and the length
++ * value is contained therein (and will be updated with the new length). We
++ * always return a name that is null terminated. */
+static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr)
+{
+ static char buf[MAXPATHLEN];
+ char *fn, tmpbuf[MAXPATHLEN];
+ unsigned int fn_len, cd_len;
+
-+ cd_len = dirbuf_offset && *dirbuf == '/' ? 0 : curr_dir_len + 1;
++ cd_len = *dirbuf == '/' ? 0 : curr_dir_len + 1;
+ if (cd_len && *merge_file != '/') {
-+ const char *p;
++ /* Return the name unchanged it doesn't have any slashes. */
+ if (len_ptr) {
-+ for (p = merge_file + *len_ptr;
-+ --p > merge_file && *p != '/'; ) {}
-+ } else
-+ p = strchr(merge_file, '/');
-+ if (!p || p == merge_file)
++ const char *p = merge_file + *len_ptr;
++ while (--p > merge_file && *p != '/') {}
++ if (p == merge_file) {
++ strlcpy(buf, merge_file, *len_ptr + 1);
++ return buf;
++ }
++ } else if (strchr(merge_file, '/') == NULL)
+ return (char *)merge_file;
+ }
+
+ strlcpy(to, merge_file, *len_ptr + 1);
+ merge_file = to;
+ }
-+ dirbuf[dirbuf_offset] = '\0';
+ if (!sanitize_path(fn, merge_file, dirbuf)) {
+ rprintf(FERROR, "merge filename overflows: %s\n",
+ merge_file);
+ return buf;
+}
+
++/* This routine takes a per-dir merge file entry and finishes its setup.
++ * If the name has a path portion then we check to see if it refers to a
++ * parent directory of the first transfer dir. If it does, we scan all the
++ * dirs from that point through the parent dir of the transfer dir looking
++ * for the per-dir merge file in each one. */
+static void prep_merge_file(struct exclude_struct *ex,
+ struct exclude_list_struct *lp, int flags,
+ char *dir, unsigned int dirlen)
+{
+ char buf[MAXPATHLEN];
-+ char *x, *y, *fn = ex->pattern;
++ char *x, *y, *pat = ex->pattern;
++ unsigned int len;
+
-+ if (!(fn = parse_merge_name(fn, NULL)) || *fn != '/')
++ if (!(x = parse_merge_name(pat, NULL)) || *x != '/')
+ return;
+
-+ x = strrchr(fn, '/');
-+ *x = '\0';
-+ if (dir[dirlen]) /* avoid writing to a read-only string */
-+ dir[dirlen] = '\0';
-+ pathjoin(dirbuf, MAXPATHLEN, sanitize_paths ? "" : curr_dir, dir);
-+ if (dirlen == 2 && *dir == '.') {
-+ int len = strlen(dirbuf);
-+ dirbuf[len-2] = '\0';
-+ }
-+ if (!*fn)
-+ fn = "/";
-+ if (*fn == '/')
-+ strlcpy(buf, fn, MAXPATHLEN);
++ y = strrchr(x, '/');
++ *y = '\0';
++ ex->pattern = strdup(y+1);
++ pathjoin(dirbuf, MAXPATHLEN, sanitize_paths ? "/" : curr_dir, dir);
++ if (!*x)
++ x = "/";
++ if (*x == '/')
++ strlcpy(buf, x, MAXPATHLEN);
+ else
-+ pathjoin(buf, MAXPATHLEN, dirbuf, fn);
-+ fn = x + 1;
++ pathjoin(buf, MAXPATHLEN, dirbuf, x);
+
-+ clean_fname(buf);
-+ if (!*buf || buf[1])
-+ strlcat(buf, "/", MAXPATHLEN);
++ len = clean_fname(buf);
++ if (len != 1 && len < MAXPATHLEN-1) {
++ buf[len++] = '/';
++ buf[len] = '\0';
++ }
+ x = buf;
+ if (sanitize_paths)
+ x += strlen(lp_path(module_id));
++ /* This ensures that the specified dir is a parent of the transfer. */
+ for (y = dirbuf; *x && *x == *y; x++, y++) {}
+ if (*x)
-+ y += strlen(y);
++ y += strlen(y); /* nope -- skip the scan */
+
+ while (*y) {
+ char save[MAXPATHLEN];
+ strlcpy(save, y, MAXPATHLEN);
++ *y = '\0';
+ dirbuf_offset = y - dirbuf;
-+ strlcpy(x, fn, MAXPATHLEN - (x - buf));
++ strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf));
++ if (!(ex->match_flags & MATCHFLG_INHERIT))
++ lp->head = NULL;
+ lp->tail = NULL;
+ add_exclude_file(lp, buf, flags);
+ strlcpy(y, save, MAXPATHLEN);
+ while ((*x++ = *y++) != '/') {}
+ }
++ free(pat);
+
-+ if (dirlen == 2 && *dir == '.') /* dir[dirlen-1] is always '/' */
-+ dirbuf_offset = 0;
-+ else
-+ dirbuf_offset = dirlen;
-+ memcpy(dirbuf, dir, dirbuf_offset);
-+
-+ x = ex->pattern;
-+ ex->pattern = strdup(fn);
-+ free(x);
++ memcpy(dirbuf, dir, dirlen + 1);
++ dirbuf_offset = dirlen;
+}
+
++/* Each time rsync changes to a new directory it call this function to
++ * handle all the per-dir merge files. The "dir" value is the current path
++ * relative to curr_dir (with a mandatory trailing slash). We copy it into
++ * dirbuf so that the routines this calls can append a file name. */
+void *push_local_excludes(char *dir, unsigned int dirlen)
+{
+ struct mergelist_save_struct *push;
+ struct exclude_list_struct *ap;
+ int i;
+
-+ /* 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. */
-+ if (dirlen == 2 && *dir == '.') /* dir[dirlen-1] is always '/' */
-+ dirbuf_offset = 0;
-+ else
-+ dirbuf_offset = dirlen;
-+ memcpy(dirbuf, dir, dirbuf_offset);
-+
++ if (dirlen == 2 && *dir == '.') { /* dir[dirlen-1] is always '/' */
++ dir += 2;
++ dirlen = 0;
++ }
++ memcpy(dirbuf, dir, dirlen + 1);
++ dirbuf_offset = dirlen;
+
+ if (!(push = new_array(struct mergelist_save_struct, 1)))
+ out_of_memory("push_local_excludes");
+ "cannot add local excludes in long-named directory %s\n",
+ full_fname(dirbuf));
+ }
++ dirbuf[dirbuf_offset] = '\0';
+ }
+
+ return (void*)push;
static int check_one_exclude(char *name, struct exclude_struct *ex,
int name_is_dir)
{
-@@ -122,7 +447,7 @@ static int check_one_exclude(char *name,
+@@ -122,7 +471,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. */
if ((p = strrchr(name,'/')) != NULL)
name = p+1;
}
-@@ -133,7 +458,8 @@ static int check_one_exclude(char *name,
+@@ -133,7 +482,8 @@ static int check_one_exclude(char *name,
name = full_name;
}
if (ex->match_flags & MATCHFLG_DIRECTORY && !name_is_dir)
return 0;
-@@ -148,9 +474,9 @@ static int check_one_exclude(char *name,
+@@ -148,9 +498,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 +547,13 @@ int check_exclude(struct exclude_list_st
+@@ -221,6 +571,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,12 +586,41 @@ static const char *get_exclude_tok(const
+@@ -253,11 +610,38 @@ static const char *get_exclude_tok(const
p = (const char *)s;
}
+ }
+ done:
s += 2;
-+ if (mflags & MATCHFLG_MERGE_FILE && *s == '.' && s[1] == '/')
-+ s += 2;
} else if (xflags & XFLG_DEF_INCLUDE)
mflags |= MATCHFLG_INCLUDE;
-
-@@ -292,9 +654,15 @@ void add_exclude(struct exclude_list_str
+@@ -292,9 +676,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 +674,23 @@ void add_exclude(struct exclude_list_str
+@@ -306,13 +696,23 @@ void add_exclude(struct exclude_list_str
continue;
}
- mflags & MATCHFLG_INCLUDE ? "in" : "ex");
+ if (mflags & MATCHFLG_MERGE_FILE) {
+ if (mflags & MATCHFLG_PERDIR_MERGE) {
-+ if (dirbuf_offset && *dirbuf == '/') {
++ if (*dirbuf == '/') {
+ if (!(cp = parse_merge_name(cp, &pat_len)))
+ continue;
+ make_exclude(listp, cp, pat_len, mflags);
}
}
-@@ -321,7 +699,7 @@ void add_exclude_file(struct exclude_lis
+@@ -321,7 +721,7 @@ void add_exclude_file(struct exclude_lis
int xflags)
{
FILE *fp;
char *eob = line + sizeof line - 1;
int word_split = xflags & XFLG_WORD_SPLIT;
-@@ -343,6 +721,11 @@ void add_exclude_file(struct exclude_lis
+@@ -343,6 +743,11 @@ void add_exclude_file(struct exclude_lis
return;
}
while (1) {
char *s = line;
int ch, overflow = 0;
-@@ -402,7 +785,21 @@ void send_exclude_list(int f)
+@@ -402,7 +807,21 @@ void send_exclude_list(int f)
if (ent->match_flags & MATCHFLG_INCLUDE) {
write_int(f, l + 2);
write_buf(f, "+ ", 2);
write_int(f, l + 2);
write_buf(f, "- ", 2);
} else
-@@ -443,6 +840,7 @@ void add_cvs_excludes(void)
+@@ -443,6 +862,7 @@ void add_cvs_excludes(void)
char fname[MAXPATHLEN];
char *p;
XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
--- orig/flist.c 2004-08-05 21:57:29
-+++ flist.c 2004-08-06 20:52:26
++++ flist.c 2004-08-07 07:44:29
@@ -39,10 +39,9 @@ extern int module_id;
extern int ignore_errors;
extern int numeric_ids;
char *p;
d = opendir(dir);
-@@ -996,18 +982,7 @@ static void send_directory(int f, struct
+@@ -996,18 +982,8 @@ static void send_directory(int f, struct
offset++;
}
- full_fname(fname));
- }
- }
++ fname[offset] = '\0';
+ 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 +1003,8 @@ static void send_directory(int f, struct
+@@ -1028,6 +1004,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,
+@@ -1047,6 +1025,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,14 @@ struct file_list *send_file_list(int f,
+@@ -1067,6 +1046,14 @@ struct file_list *send_file_list(int f,
exit_cleanup(RERR_FILESELECT);
}
use_ff_fd = 1;
}
}
-@@ -1097,6 +1083,22 @@ struct file_list *send_file_list(int f,
+@@ -1097,6 +1084,22 @@ struct file_list *send_file_list(int f,
}
}