..wayne..
--- orig/clientserver.c 2004-08-02 02:29:16
-+++ clientserver.c 2004-08-09 17:35:56
++++ clientserver.c 2004-08-10 15:44:15
@@ -48,12 +48,14 @@ extern int no_detach;
extern int default_af_hint;
extern char *bind_address;
char *auth_user;
-+/* The length of the lp_path() string (when we're not chrooted). */
++/* Length of lp_path() string when in daemon mode & not chrooted, else 0. */
+unsigned int module_dirlen = 0;
+
/**
log_init();
---- orig/exclude.c 2004-08-05 23:16:37
-+++ exclude.c 2004-08-10 04:51:05
-@@ -27,16 +27,75 @@
- #include "rsync.h"
-
- extern int verbose;
-+extern int am_sender;
+--- orig/exclude.c 2004-08-10 18:17:01
++++ exclude.c 2004-08-13 07:40:08
+@@ -30,13 +30,69 @@ extern int verbose;
extern int eol_nulls;
extern int list_only;
extern int recurse;
+extern int io_error;
-+extern int module_id;
-+extern int delete_mode;
-+extern int delete_excluded;
+extern int sanitize_paths;
extern char curr_dir[];
+ * 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;
/** Build an exclude structure given an exclude pattern. */
static void make_exclude(struct exclude_list_struct *listp, const char *pat,
-@@ -46,23 +105,50 @@ static void make_exclude(struct exclude_
+@@ -46,23 +102,50 @@ static void make_exclude(struct exclude_
const char *cp;
unsigned int ex_len;
strlcpy(ret->pattern + ex_len, pat, pat_len + 1);
pat_len += ex_len;
-@@ -81,14 +167,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 +208,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)
{
-@@ -122,18 +477,20 @@ 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. */
if ((p = strrchr(name,'/')) != NULL)
name = p+1;
}
- else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/') {
- static char full_name[MAXPATHLEN];
-- int plus = curr_dir[1] == '\0'? 1 : 0;
-- pathjoin(full_name, sizeof full_name, curr_dir+plus, name);
-+ char *cd = curr_dir + module_dirlen;
-+ int plus = cd[1] == '\0'? 1 : 0;
-+ pathjoin(full_name, sizeof full_name, cd+plus, name);
+ else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/'
+- && curr_dir[1]) {
+- pathjoin(full_name, sizeof full_name, curr_dir + 1, name);
++ && curr_dir_len > module_dirlen + 1) {
++ pathjoin(full_name, sizeof full_name,
++ curr_dir + module_dirlen + 1, name);
name = full_name;
}
-- if (!name[0]) return 0;
-+ if (!name[0])
-+ return 0;
-
- if (ex->match_flags & MATCHFLG_DIRECTORY && !name_is_dir)
- return 0;
-@@ -148,9 +505,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 +578,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 +617,36 @@ static const char *get_exclude_tok(const
+@@ -253,11 +615,36 @@ static const char *get_exclude_tok(const
p = (const char *)s;
}
s += 2;
} else if (xflags & XFLG_DEF_INCLUDE)
mflags |= MATCHFLG_INCLUDE;
-@@ -273,6 +662,8 @@ static const char *get_exclude_tok(const
+@@ -273,6 +660,8 @@ static const char *get_exclude_tok(const
if (*p == '!' && len == 1 && !(xflags & XFLG_WORDS_ONLY))
mflags |= MATCHFLG_CLEAR_LIST;
*len_ptr = len;
*flag_ptr = mflags;
-@@ -284,7 +675,7 @@ void add_exclude(struct exclude_list_str
+@@ -284,7 +673,7 @@ void add_exclude(struct exclude_list_str
int xflags)
{
unsigned int pat_len, mflags;
if (!pattern)
return;
-@@ -292,9 +683,15 @@ void add_exclude(struct exclude_list_str
+@@ -292,9 +681,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 +703,24 @@ void add_exclude(struct exclude_list_str
+@@ -306,13 +701,24 @@ void add_exclude(struct exclude_list_str
continue;
}
}
}
-@@ -321,7 +729,7 @@ void add_exclude_file(struct exclude_lis
+@@ -321,7 +727,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;
-@@ -342,6 +750,12 @@ void add_exclude_file(struct exclude_lis
+@@ -342,6 +748,12 @@ void add_exclude_file(struct exclude_lis
}
return;
}
while (1) {
char *s = line;
-@@ -402,7 +816,21 @@ void send_exclude_list(int f)
+@@ -402,7 +814,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 +871,7 @@ void add_cvs_excludes(void)
+@@ -443,6 +869,7 @@ void add_cvs_excludes(void)
char fname[MAXPATHLEN];
char *p;
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-09 18:21:55
-@@ -39,10 +39,9 @@ extern int module_id;
+--- orig/flist.c 2004-09-21 09:40:27
++++ 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
+@@ -978,15 +971,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
+@@ -997,6 +982,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
+@@ -1020,18 +1006,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
+@@ -1052,6 +1027,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,
+@@ -1071,6 +1048,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,
+@@ -1091,6 +1069,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,
+@@ -1121,6 +1103,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
+--- orig/options.c 2004-09-20 05:10:48
++++ options.c 2004-08-12 18:59:28
@@ -287,6 +287,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");
{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 +591,11 @@ int parse_arguments(int *argc, const cha
+@@ -585,6 +587,11 @@ int parse_arguments(int *argc, const cha
am_sender = 1;
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 @@
};
struct exclude_list_struct {
---- orig/rsync.yo 2004-08-03 15:34:32
-+++ rsync.yo 2004-08-10 04:49:06
-@@ -335,6 +335,7 @@ verb(
+--- orig/rsync.yo 2004-09-20 05:10:48
++++ rsync.yo 2004-08-13 00:43:31
+@@ -364,6 +364,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
-0 --from0 all file lists are delimited by nulls
--version print version number
--daemon run as an rsync daemon
-@@ -979,15 +980,22 @@ The exclude and include patterns specifi
+@@ -1025,24 +1026,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
-The filenames matched against the exclude/include patterns are relative
-to the "root of the transfer". If you think of the transfer as a
+-subtree of names that are being sent from sender to receiver, the root
+-is where the tree starts to be duplicated in the destination directory.
+-This root governs where patterns that start with a / match (see below).
+The global include/exclude rules are anchored at the "root of the
+transfer" (as opposed to per-directory rules, which are anchored at
-+the current directory). If you think of the transfer as a
- subtree of names that are being sent from sender to receiver, the root
- is where the tree starts to be duplicated in the destination directory.
- This root governs where patterns that start with a / match (see below).
-@@ -996,7 +1004,7 @@ Because the matching is relative to the
++the merge-file's directory). If you think of the transfer as a
++subtree of names that are being sent from sender to receiver, the
++transfer-root is where the tree starts to be duplicated in the
++destination directory. This root governs where patterns that start
++with a / match (as described in the list on pattern forms below).
+
+ Because the matching is relative to the transfer-root, changing the
trailing slash on a source path or changing your use of the --relative
option affects the path you need to use in your matching (in addition to
changing how much of the file tree is duplicated on the destination
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 +1051,29 @@ because rsync did not descend through th
+@@ -1089,23 +1098,27 @@ because rsync did not descend through th
hierarchy.
Note also that the --include and --exclude options take one pattern
-each. To add multiple patterns use the --include-from and
---exclude-from options or multiple --include and --exclude options.
+each. To add multiple patterns use the --include-from and --exclude-from
-+options or multiple --include and --exclude options. See also the
-+section on MERGED EXCLUDE FILES for how to merge-files together and
-+specify local rules within a hierarchy of files.
++options or multiple --include and --exclude options.
-The patterns can take several forms. The rules are:
+The include/exclude patterns can take several forms. The rules are:
+ against the end of the pathname. This is similar to a leading ^ in
+ regular expressions.
+ Thus "/foo" would match a file called "foo" at either the "root of the
-+ transfer" (for a global rule) or in the current directory (for a
++ transfer" (for a global rule) or in the merge-file's directory (for a
+ per-directory rule).
+ An unqualified "foo" would match any file or directory named "foo"
anywhere in the tree because the algorithm is applied recursively from
it() if the pattern ends with a / then it will only match a
directory, not a file, link, or device.
-@@ -1072,22 +1086,31 @@ itemize(
+@@ -1118,22 +1131,31 @@ itemize(
single asterisk pattern "*" will stop at slashes.
it() if the pattern contains a / (not counting a trailing /) or a "**"
)
The +/- rules are most useful in a list that was read from a file, allowing
-@@ -1134,10 +1157,109 @@ itemize(
+@@ -1180,8 +1202,160 @@ itemize(
it() --include "*/" --include "*.c" --exclude "*" would include all
directories and C source files
it() --include "foo/" --include "foo/bar.c" --exclude "*" would include
+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. You select which one you want by
-+optionally including an option prior to the merge-file name:
++single-instance and per-directory. The choice is made via an option
++placed prior to the merge-file name:
+
+startdit()
+
+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.
-+
-+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
-+newest rules a higher priority than the inherited rules. If you don't
-+want a rule to be inherited, anchor it with a leading slash. Anchored
-+rules in a per-directory merge-file are relative to the current
-+directory, so a rule "/foo" would only exclude the file "foo" in the
-+directory where the per-dir exclude file was found.
++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
++to send. The files may also need to be transferred to the receiving side
++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()
+
-+Here's an example exclude file (which you'd specify via the normal
-+--exclude-from=FILE option):
++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
++newest rules a higher priority than the inherited rules. The entire set
++of per-dir rules is grouped together in the spot where the merge-file was
++specified, so it is possible to override per-dir rules via a rule that
++got specified earlier in the list of global rules.
++
++If you don't want a per-dir rule to be inherited, anchor it with a leading
++slash. Anchored rules in a per-directory merge-file are relative to the
++merge-file's directory, so a rule "/foo" would only exclude the file "foo"
++in the directory where the per-dir exclude file was found.
++
++Here's an example exclude file which you'd specify via the normal
++--exclude-from=FILE option:
+
+verb(
+ . /home/user/.global_excludes
+ . -p .excl
+ + *.[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
-+(only per-directory rules that are read in once the transfer begins get
-+the current-dir anchoring).
++.global_excludes file are anchored just like all other global rules.
+
+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
+)
+
+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 an
-+rsync daemon, the "root dir" is always the module's "path" setting.)
++directories from the root down through the source of the transfer. (For
++an rsync daemon, the "root dir" is always the module's "path" setting.)
+
+Some examples of this pre-scanning for per-directory files:
+
+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 its this rule at the end of all your other
+rules (giving it a lower priority than your command-line rules). For
+example:
+
+rules taken from the $HOME/.cvsignore file and from $CVSIGNORE are not
+repositioned by this.)
+
- manpagesection(BATCH MODE)
++manpagesection(PER-DIRECTORY EXCLUDES AND DELETE)
++
++Without a delete option, per-directory excludes are only relevant on the
++sending side, so you can feel free to exclude the merge files themselves
++without affecting the transfer:
++
++verb(
++ 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
++files to be excluded from being deleted, you'll need to be sure that the
++receiving side knows what files to exclude. The easiest way is to include
++the per-directory merge files in the transfer and use --delete-after
++because this ensures that the receiving side gets all the same exclude
++rules as the sending side before it tries to delete anything:
++
++verb(
++ rsync -avE --delete-after host:src/dir /dest
++)
++
++However, if you 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
++ --delete host:src/dir /dest
++)
++
++In the above example the extra.rules file can affect both sides of the
++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
++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
++deleted) and then put rules into the local files to control what else
++should not get deleted. Like this:
++
++verb(
++ rsync -avE --exclude=.rsync-excludes --delete host:src/dir /dest
+ )
- bf(Note:) Batch mode should be considered experimental in this version
+ manpagesection(BATCH MODE)
--- orig/testsuite/exclude.test 2004-05-29 21:25:45
+++ testsuite/exclude.test 2004-08-08 06:35:15
@@ -23,19 +23,47 @@ export HOME CVSIGNORE
+
# 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++] = '/';
- }