Got rid of some fuzz.
[rsync/rsync-patches.git] / filter.diff
1 After applying this patch and running configure, you MUST run this
2 command before "make":
3
4     make proto
5
6 This patch adds the --filter option, which implements an improved set of
7 include/exclude rules:
8
9     . SINGLE-INSTANCE_MERGE_FILE
10     : PER-DIRECTORY_MERGE_FILE
11     - exclude-pattern
12     + include-pattern
13
14 Note that the prefix for a filter rule is NOT optional, and that the
15 separating space can be replaced by an equal-sign (=) or an underscore (_),
16 if desired.  There are also optional modifiers that can be specified for
17 the merge-file rules.
18
19 A per-directory merge file is one that will be looked for in every
20 sub-directory that rsync visits, and the rules found in that sub-
21 directory's file will affect that dir and (if desired) its subdirs.
22
23 For example:
24
25   rsync -av --filter :_.rules from/ to
26
27 The above will look for a file named ".rules" in every directory of the
28 hierarchy that rsync visits, and it will filter names based on the rules
29 found therein.  If one of the .rules files contains this:
30
31   + *.c
32   : .rules2
33   . .rules3
34   - *.o
35   - /foobar
36
37 Then the file ".rules2" will also be read in from the current dir and all
38 its subdirs.  The file ".rules3" would just be read in from the current dir
39 only.  The exclusion of "foobar" will only happen in that .rules file's
40 directory because the rule is anchored, which is one way to make a rule
41 local instead of inherited (see also the 'n' modifier).
42
43 ..wayne..
44
45 --- orig/clientserver.c 2005-01-22 22:48:52
46 +++ clientserver.c      2005-01-16 23:33:02
47 @@ -49,12 +49,14 @@ extern int no_detach;
48  extern int default_af_hint;
49  extern char *bind_address;
50  extern struct exclude_list_struct server_exclude_list;
51 -extern char *exclude_path_prefix;
52  extern char *config_file;
53  extern char *files_from;
54  
55  char *auth_user;
56  
57 +/* Length of lp_path() string when in daemon mode & not chrooted, else 0. */
58 +unsigned int module_dirlen = 0;
59 +
60  /**
61   * Run a client connected to an rsyncd.  The alternative to this
62   * function for remote-shell connections is do_cmd().
63 @@ -310,26 +312,33 @@ static int rsync_module(int f_in, int f_
64         /* TODO: Perhaps take a list of gids, and make them into the
65          * supplementary groups. */
66  
67 -       exclude_path_prefix = use_chroot? "" : lp_path(i);
68 -       if (*exclude_path_prefix == '/' && !exclude_path_prefix[1])
69 -               exclude_path_prefix = "";
70 +       if (use_chroot) {
71 +               module_dirlen = 0;
72 +               set_excludes_dir("/", 1);
73 +       } else {
74 +               module_dirlen = strlen(lp_path(i));
75 +               set_excludes_dir(lp_path(i), module_dirlen);
76 +       }
77 +
78 +       p = lp_filter(i);
79 +       add_exclude(&server_exclude_list, p,
80 +                   XFLG_WORD_SPLIT | XFLG_ABS_PATH);
81  
82         p = lp_include_from(i);
83         add_exclude_file(&server_exclude_list, p,
84 -                        XFLG_FATAL_ERRORS | XFLG_DEF_INCLUDE);
85 +                        XFLG_FATAL_ERRORS | XFLG_ABS_PATH | XFLG_DEF_INCLUDE);
86  
87         p = lp_include(i);
88         add_exclude(&server_exclude_list, p,
89 -                   XFLG_WORD_SPLIT | XFLG_DEF_INCLUDE);
90 +                   XFLG_WORD_SPLIT | XFLG_ABS_PATH | XFLG_DEF_INCLUDE);
91  
92         p = lp_exclude_from(i);
93         add_exclude_file(&server_exclude_list, p,
94 -                        XFLG_FATAL_ERRORS);
95 +                        XFLG_FATAL_ERRORS | XFLG_ABS_PATH | XFLG_DEF_EXCLUDE);
96  
97         p = lp_exclude(i);
98 -       add_exclude(&server_exclude_list, p, XFLG_WORD_SPLIT);
99 -
100 -       exclude_path_prefix = NULL;
101 +       add_exclude(&server_exclude_list, p,
102 +                   XFLG_WORD_SPLIT | XFLG_ABS_PATH | XFLG_DEF_EXCLUDE);
103  
104         log_init();
105  
106 --- orig/exclude.c      2005-01-22 22:48:52
107 +++ exclude.c   2005-01-23 07:33:21
108 @@ -30,15 +30,68 @@ extern int verbose;
109  extern int eol_nulls;
110  extern int list_only;
111  extern int recurse;
112 +extern int io_error;
113 +extern int sanitize_paths;
114 +extern int protocol_version;
115  
116  extern char curr_dir[];
117 +extern unsigned int curr_dir_len;
118 +extern unsigned int module_dirlen;
119  
120  struct exclude_list_struct exclude_list = { 0, 0, "" };
121 -struct exclude_list_struct local_exclude_list = { 0, 0, "per-dir .cvsignore " };
122  struct exclude_list_struct server_exclude_list = { 0, 0, "server " };
123 -char *exclude_path_prefix = NULL;
124  
125 -/** Build an exclude structure given an exclude pattern. */
126 +/* The dirbuf is set by push_local_excludes() to the current subdirectory
127 + * relative to curr_dir that is being processed.  The path always has a
128 + * trailing slash appended, and the variable dirbuf_len contains the length
129 + * of this path prefix.  The path is always absolute. */
130 +static char dirbuf[MAXPATHLEN+1];
131 +static unsigned int dirbuf_len = 0;
132 +static int dirbuf_depth;
133 +
134 +/* This is True when we're scanning parent dirs for per-dir merge-files. */
135 +static BOOL parent_dirscan = False;
136 +
137 +/* This array contains a list of all the currently active per-dir merge
138 + * files.  This makes it easier to save the appropriate values when we
139 + * "push" down into each subdirectory. */
140 +static struct exclude_struct **mergelist_parents;
141 +static int mergelist_cnt = 0;
142 +static int mergelist_size = 0;
143 +
144 +/* Each exclude_list_struct describes a singly-linked list by keeping track
145 + * of both the head and tail pointers.  The list is slightly unusual in that
146 + * a parent-dir's content can be appended to the end of the local list in a
147 + * special way:  the last item in the local list has its "next" pointer set
148 + * to point to the inherited list, but the local list's tail pointer points
149 + * at the end of the local list.  Thus, if the local list is empty, the head
150 + * will be pointing at the inherited content but the tail will be NULL.  To
151 + * help you visualize this, here are the possible list arrangements:
152 + *
153 + * Completely Empty                     Local Content Only
154 + * ==================================   ====================================
155 + * head -> NULL                         head -> Local1 -> Local2 -> NULL
156 + * tail -> NULL                         tail -------------^
157 + *
158 + * Inherited Content Only               Both Local and Inherited Content
159 + * ==================================   ====================================
160 + * head -> Parent1 -> Parent2 -> NULL   head -> L1 -> L2 -> P1 -> P2 -> NULL
161 + * tail -> NULL                         tail ---------^
162 + *
163 + * This means that anyone wanting to traverse the whole list to use it just
164 + * needs to start at the head and use the "next" pointers until it goes
165 + * NULL.  To add new local content, we insert the item after the tail item
166 + * and update the tail (obviously, if "tail" was NULL, we insert it at the
167 + * head).  To clear the local list, WE MUST NOT FREE THE INHERITED CONTENT
168 + * because it is shared between the current list and our parent list(s).
169 + * The easiest way to handle this is to simply truncate the list after the
170 + * tail item and then free the local list from the head.  When inheriting
171 + * the list for a new local dir, we just save off the exclude_list_struct
172 + * values (so we can pop back to them later) and set the tail to NULL.
173 + */
174 +
175 +/* Build an exclude structure given an exclude pattern. The value in "pat"
176 + * is not null-terminated. */
177  static void make_exclude(struct exclude_list_struct *listp, const char *pat,
178                          unsigned int pat_len, unsigned int mflags)
179  {
180 @@ -46,23 +99,45 @@ static void make_exclude(struct exclude_
181         const char *cp;
182         unsigned int ex_len;
183  
184 +       if (verbose > 2) {
185 +               rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s)\n",
186 +                       who_am_i(), (int)pat_len, pat,
187 +                       mflags & MATCHFLG_MERGE_FILE ? "per-dir-merge"
188 +                       : mflags & MATCHFLG_INCLUDE ? "include" : "exclude",
189 +                       listp->debug_type);
190 +       }
191 +
192 +       if (mflags & MATCHFLG_MERGE_FILE) {
193 +               int i;
194 +               /* If the local merge file was already mentioned, don't
195 +                * add it again. */
196 +               for (i = 0; i < mergelist_cnt; i++) {
197 +                       struct exclude_struct *ex = mergelist_parents[i];
198 +                       if (strlen(ex->pattern) == pat_len
199 +                           && memcmp(ex->pattern, pat, pat_len) == 0)
200 +                               return;
201 +               }
202 +       }
203 +
204         ret = new(struct exclude_struct);
205         if (!ret)
206                 out_of_memory("make_exclude");
207  
208         memset(ret, 0, sizeof ret[0]);
209  
210 -       if (exclude_path_prefix)
211 -               mflags |= MATCHFLG_ABS_PATH;
212 -       if (exclude_path_prefix && *pat == '/')
213 -               ex_len = strlen(exclude_path_prefix);
214 -       else
215 +       if (mflags & MATCHFLG_ABS_PATH) {
216 +               if (*pat != '/') {
217 +                       mflags &= ~MATCHFLG_ABS_PATH;
218 +                       ex_len = 0;
219 +               } else
220 +                       ex_len = dirbuf_len - module_dirlen - 1;
221 +       } else
222                 ex_len = 0;
223         ret->pattern = new_array(char, ex_len + pat_len + 1);
224         if (!ret->pattern)
225                 out_of_memory("make_exclude");
226         if (ex_len)
227 -               memcpy(ret->pattern, exclude_path_prefix, ex_len);
228 +               memcpy(ret->pattern, dirbuf + module_dirlen, ex_len);
229         strlcpy(ret->pattern + ex_len, pat, pat_len + 1);
230         pat_len += ex_len;
231  
232 @@ -81,14 +156,40 @@ static void make_exclude(struct exclude_
233                 mflags |= MATCHFLG_DIRECTORY;
234         }
235  
236 -       for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++)
237 -               ret->slash_cnt++;
238 +       if (mflags & MATCHFLG_MERGE_FILE) {
239 +               struct exclude_list_struct *lp
240 +                   = new_array(struct exclude_list_struct, 1);
241 +               if (!lp)
242 +                       out_of_memory("make_exclude");
243 +               lp->head = lp->tail = NULL;
244 +               if ((cp = strrchr(ret->pattern, '/')) != NULL)
245 +                       cp++;
246 +               else
247 +                       cp = ret->pattern;
248 +               if (asprintf(&lp->debug_type, " (per-dir %s)", cp) < 0)
249 +                       out_of_memory("make_exclude");
250 +               ret->u.mergelist = lp;
251 +               if (mergelist_cnt == mergelist_size) {
252 +                       mergelist_size += 5;
253 +                       mergelist_parents = realloc_array(mergelist_parents,
254 +                                               struct exclude_struct *,
255 +                                               mergelist_size);
256 +                       if (!mergelist_parents)
257 +                               out_of_memory("make_exclude");
258 +               }
259 +               mergelist_parents[mergelist_cnt++] = ret;
260 +       } else {
261 +               for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++)
262 +                       ret->u.slash_cnt++;
263 +       }
264  
265         ret->match_flags = mflags;
266  
267 -       if (!listp->tail)
268 +       if (!listp->tail) {
269 +               ret->next = listp->head;
270                 listp->head = listp->tail = ret;
271 -       else {
272 +       } else {
273 +               ret->next = listp->tail->next;
274                 listp->tail->next = ret;
275                 listp->tail = ret;
276         }
277 @@ -96,22 +197,263 @@ static void make_exclude(struct exclude_
278  
279  static void free_exclude(struct exclude_struct *ex)
280  {
281 +       if (ex->match_flags & MATCHFLG_MERGE_FILE) {
282 +               free(ex->u.mergelist->debug_type);
283 +               free(ex->u.mergelist);
284 +               mergelist_cnt--;
285 +       }
286         free(ex->pattern);
287         free(ex);
288  }
289  
290 -void clear_exclude_list(struct exclude_list_struct *listp)
291 +static void clear_exclude_list(struct exclude_list_struct *listp)
292  {
293 -       struct exclude_struct *ent, *next;
294 -
295 -       for (ent = listp->head; ent; ent = next) {
296 -               next = ent->next;
297 -               free_exclude(ent);
298 +       if (listp->tail) {
299 +               struct exclude_struct *ent, *next;
300 +               /* Truncate any inherited items from the local list. */
301 +               listp->tail->next = NULL;
302 +               /* Now free everything that is left. */
303 +               for (ent = listp->head; ent; ent = next) {
304 +                       next = ent->next;
305 +                       free_exclude(ent);
306 +               }
307         }
308  
309         listp->head = listp->tail = NULL;
310  }
311  
312 +/* This returns an expanded (absolute) filename for the merge-file name if
313 + * the name has any slashes in it OR if the parent_dirscan var is True;
314 + * otherwise it returns the original merge_file name.  If the len_ptr value
315 + * is non-NULL the merge_file name is limited by the referenced length
316 + * value and will be updated with the length of the resulting name.  We
317 + * always return a name that is null terminated, even if the merge_file
318 + * name was not. */
319 +static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr,
320 +                             unsigned int prefix_skip)
321 +{
322 +       static char buf[MAXPATHLEN];
323 +       char *fn, tmpbuf[MAXPATHLEN];
324 +       unsigned int fn_len;
325 +
326 +       if (!parent_dirscan && *merge_file != '/') {
327 +               /* Return the name unchanged it doesn't have any slashes. */
328 +               if (len_ptr) {
329 +                       const char *p = merge_file + *len_ptr;
330 +                       while (--p > merge_file && *p != '/') {}
331 +                       if (p == merge_file) {
332 +                               strlcpy(buf, merge_file, *len_ptr + 1);
333 +                               return buf;
334 +                       }
335 +               } else if (strchr(merge_file, '/') == NULL)
336 +                       return (char *)merge_file;
337 +       }
338 +
339 +       fn = *merge_file == '/' ? buf : tmpbuf;
340 +       if (sanitize_paths) {
341 +               const char *r = prefix_skip ? "/" : NULL;
342 +               /* null-terminate the name if it isn't already */
343 +               if (len_ptr && merge_file[*len_ptr]) {
344 +                       char *to = fn == buf ? tmpbuf : buf;
345 +                       strlcpy(to, merge_file, *len_ptr + 1);
346 +                       merge_file = to;
347 +               }
348 +               if (!sanitize_path(fn, merge_file, r, dirbuf_depth)) {
349 +                       rprintf(FERROR, "merge-file name overflows: %s\n",
350 +                               merge_file);
351 +                       return NULL;
352 +               }
353 +       } else {
354 +               strlcpy(fn, merge_file, len_ptr ? *len_ptr + 1 : MAXPATHLEN);
355 +               clean_fname(fn, 1);
356 +       }
357 +       
358 +       fn_len = strlen(fn);
359 +       if (fn == buf)
360 +               goto done;
361 +
362 +       if (dirbuf_len + fn_len >= MAXPATHLEN) {
363 +               rprintf(FERROR, "merge-file name overflows: %s\n", fn);
364 +               return NULL;
365 +       }
366 +       memcpy(buf, dirbuf + prefix_skip, dirbuf_len - prefix_skip);
367 +       memcpy(buf + dirbuf_len - prefix_skip, fn, fn_len + 1);
368 +       fn_len = clean_fname(buf, 1);
369 +
370 +    done:
371 +       if (len_ptr)
372 +               *len_ptr = fn_len;
373 +       return buf;
374 +}
375 +
376 +/* Sets the dirbuf and dirbuf_len values. */
377 +void set_excludes_dir(const char *dir, unsigned int dirlen)
378 +{
379 +       unsigned int len;
380 +       if (*dir != '/') {
381 +               memcpy(dirbuf, curr_dir, curr_dir_len);
382 +               dirbuf[curr_dir_len] = '/';
383 +               len = curr_dir_len + 1;
384 +               if (len + dirlen >= MAXPATHLEN)
385 +                       dirlen = 0;
386 +       } else
387 +               len = 0;
388 +       memcpy(dirbuf + len, dir, dirlen);
389 +       dirbuf[dirlen + len] = '\0';
390 +       dirbuf_len = clean_fname(dirbuf, 1);
391 +       if (dirbuf_len > 1 && dirbuf[dirbuf_len-1] == '.'
392 +           && dirbuf[dirbuf_len-2] == '/')
393 +               dirbuf_len -= 2;
394 +       dirbuf[dirbuf_len++] = '/';
395 +       dirbuf[dirbuf_len] = '\0';
396 +       if (sanitize_paths)
397 +               dirbuf_depth = count_dir_elements(dirbuf + module_dirlen);
398 +}
399 +
400 +/* This routine takes a per-dir merge-file entry and finishes its setup.
401 + * If the name has a path portion then we check to see if it refers to a
402 + * parent directory of the first transfer dir.  If it does, we scan all the
403 + * dirs from that point through the parent dir of the transfer dir looking
404 + * for the per-dir merge-file in each one. */
405 +static BOOL setup_merge_file(struct exclude_struct *ex,
406 +                            struct exclude_list_struct *lp, int flags)
407 +{
408 +       char buf[MAXPATHLEN];
409 +       char *x, *y, *pat = ex->pattern;
410 +       unsigned int len;
411 +
412 +       if (!(x = parse_merge_name(pat, NULL, 0)) || *x != '/')
413 +               return 0;
414 +
415 +       y = strrchr(x, '/');
416 +       *y = '\0';
417 +       ex->pattern = strdup(y+1);
418 +       if (!*x)
419 +               x = "/";
420 +       if (*x == '/')
421 +               strlcpy(buf, x, MAXPATHLEN);
422 +       else
423 +               pathjoin(buf, MAXPATHLEN, dirbuf, x);
424 +
425 +       len = clean_fname(buf, 1);
426 +       if (len != 1 && len < MAXPATHLEN-1) {
427 +               buf[len++] = '/';
428 +               buf[len] = '\0';
429 +       }
430 +       /* This ensures that the specified dir is a parent of the transfer. */
431 +       for (x = buf, y = dirbuf; *x && *x == *y; x++, y++) {}
432 +       if (*x)
433 +               y += strlen(y); /* nope -- skip the scan */
434 +
435 +       parent_dirscan = True;
436 +       while (*y) {
437 +               char save[MAXPATHLEN];
438 +               strlcpy(save, y, MAXPATHLEN);
439 +               *y = '\0';
440 +               dirbuf_len = y - dirbuf;
441 +               strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf));
442 +               add_exclude_file(lp, buf, flags | XFLG_ABS_PATH);
443 +               if (ex->match_flags & MATCHFLG_NO_INHERIT)
444 +                       lp->head = NULL;
445 +               lp->tail = NULL;
446 +               strlcpy(y, save, MAXPATHLEN);
447 +               while ((*x++ = *y++) != '/') {}
448 +       }
449 +       parent_dirscan = False;
450 +       free(pat);
451 +       return 1;
452 +}
453 +
454 +/* Each time rsync changes to a new directory it call this function to
455 + * handle all the per-dir merge-files.  The "dir" value is the current path
456 + * relative to curr_dir (which might not be null-terminated).  We copy it
457 + * into dirbuf so that we can easily append a file name on the end. */
458 +void *push_local_excludes(const char *dir, unsigned int dirlen)
459 +{
460 +       struct exclude_list_struct *ap, *push;
461 +       int i;
462 +
463 +       set_excludes_dir(dir, dirlen);
464 +
465 +       push = new_array(struct exclude_list_struct, mergelist_cnt);
466 +       if (!push)
467 +               out_of_memory("push_local_excludes");
468 +
469 +       for (i = 0, ap = push; i < mergelist_cnt; i++) {
470 +               memcpy(ap++, mergelist_parents[i]->u.mergelist,
471 +                      sizeof (struct exclude_list_struct));
472 +       }
473 +
474 +       /* Note: add_exclude_file() might increase mergelist_cnt, so keep
475 +        * this loop separate from the above loop. */
476 +       for (i = 0; i < mergelist_cnt; i++) {
477 +               struct exclude_struct *ex = mergelist_parents[i];
478 +               struct exclude_list_struct *lp = ex->u.mergelist;
479 +               int flags = 0;
480 +
481 +               if (verbose > 2) {
482 +                       rprintf(FINFO, "[%s] pushing exclude list%s\n",
483 +                               who_am_i(), lp->debug_type);
484 +               }
485 +
486 +               lp->tail = NULL; /* Switch any local rules to inherited. */
487 +               if (ex->match_flags & MATCHFLG_NO_INHERIT)
488 +                       lp->head = NULL;
489 +               if (ex->match_flags & MATCHFLG_WORD_SPLIT)
490 +                       flags |= XFLG_WORD_SPLIT;
491 +               if (ex->match_flags & MATCHFLG_NO_PREFIXES)
492 +                       flags |= XFLG_NO_PREFIXES;
493 +               if (ex->match_flags & MATCHFLG_INCLUDE)
494 +                       flags |= XFLG_DEF_INCLUDE;
495 +               else if (ex->match_flags & MATCHFLG_NO_PREFIXES)
496 +                       flags |= XFLG_DEF_EXCLUDE;
497 +
498 +               if (ex->match_flags & MATCHFLG_FINISH_SETUP) {
499 +                       ex->match_flags &= ~MATCHFLG_FINISH_SETUP;
500 +                       if (setup_merge_file(ex, lp, flags))
501 +                               set_excludes_dir(dir, dirlen);
502 +               }
503 +
504 +               if (strlcpy(dirbuf + dirbuf_len, ex->pattern,
505 +                   MAXPATHLEN - dirbuf_len) < MAXPATHLEN - dirbuf_len)
506 +                       add_exclude_file(lp, dirbuf, flags | XFLG_ABS_PATH);
507 +               else {
508 +                       io_error |= IOERR_GENERAL;
509 +                       rprintf(FINFO,
510 +                           "cannot add local excludes in long-named directory %s\n",
511 +                           full_fname(dirbuf));
512 +               }
513 +               dirbuf[dirbuf_len] = '\0';
514 +       }
515 +
516 +       return (void*)push;
517 +}
518 +
519 +void pop_local_excludes(void *mem)
520 +{
521 +       struct exclude_list_struct *ap, *pop = (struct exclude_list_struct*)mem;
522 +       int i;
523 +
524 +       for (i = mergelist_cnt; i-- > 0; ) {
525 +               struct exclude_struct *ex = mergelist_parents[i];
526 +               struct exclude_list_struct *lp = ex->u.mergelist;
527 +
528 +               if (verbose > 2) {
529 +                       rprintf(FINFO, "[%s] popping exclude list%s\n",
530 +                               who_am_i(), lp->debug_type);
531 +               }
532 +
533 +               clear_exclude_list(lp);
534 +       }
535 +
536 +       for (i = 0, ap = pop; i < mergelist_cnt; i++) {
537 +               memcpy(mergelist_parents[i]->u.mergelist, ap++,
538 +                      sizeof (struct exclude_list_struct));
539 +       }
540 +
541 +       free(pop);
542 +}
543 +
544  static int check_one_exclude(char *name, struct exclude_struct *ex,
545                               int name_is_dir)
546  {
547 @@ -125,13 +467,14 @@ static int check_one_exclude(char *name,
548         /* If the pattern does not have any slashes AND it does not have
549          * a "**" (which could match a slash), then we just match the
550          * name portion of the path. */
551 -       if (!ex->slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
552 +       if (!ex->u.slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
553                 if ((p = strrchr(name,'/')) != NULL)
554                         name = p+1;
555         }
556         else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/'
557 -           && curr_dir[1]) {
558 -               pathjoin(full_name, sizeof full_name, curr_dir + 1, name);
559 +           && curr_dir_len > module_dirlen + 1) {
560 +               pathjoin(full_name, sizeof full_name,
561 +                        curr_dir + module_dirlen + 1, name);
562                 name = full_name;
563         }
564  
565 @@ -148,9 +491,9 @@ static int check_one_exclude(char *name,
566         if (ex->match_flags & MATCHFLG_WILD) {
567                 /* A non-anchored match with an infix slash and no "**"
568                  * needs to match the last slash_cnt+1 name elements. */
569 -               if (!match_start && ex->slash_cnt
570 +               if (!match_start && ex->u.slash_cnt
571                     && !(ex->match_flags & MATCHFLG_WILD2)) {
572 -                       int cnt = ex->slash_cnt + 1;
573 +                       int cnt = ex->u.slash_cnt + 1;
574                         for (p = name + strlen(name) - 1; p >= name; p--) {
575                                 if (*p == '/' && !--cnt)
576                                         break;
577 @@ -202,12 +545,11 @@ static void report_exclude_result(char c
578          * case we add it back in here. */
579  
580         if (verbose >= 2) {
581 -               rprintf(FINFO, "[%s] %scluding %s %s because of %spattern %s%s\n",
582 +               rprintf(FINFO, "[%s] %scluding %s %s because of pattern %s%s%s\n",
583                         who_am_i(),
584                         ent->match_flags & MATCHFLG_INCLUDE ? "in" : "ex",
585 -                       name_is_dir ? "directory" : "file", name, type,
586 -                       ent->pattern,
587 -                       ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "");
588 +                       name_is_dir ? "directory" : "file", name, ent->pattern,
589 +                       ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "", type);
590         }
591  }
592  
593 @@ -221,6 +563,13 @@ int check_exclude(struct exclude_list_st
594         struct exclude_struct *ent;
595  
596         for (ent = listp->head; ent; ent = ent->next) {
597 +               if (ent->match_flags & MATCHFLG_MERGE_FILE) {
598 +                       int rc = check_exclude(ent->u.mergelist, name,
599 +                                              name_is_dir);
600 +                       if (rc)
601 +                               return rc;
602 +                       continue;
603 +               }
604                 if (check_one_exclude(name, ent, name_is_dir)) {
605                         report_exclude_result(name, ent, name_is_dir,
606                                               listp->debug_type);
607 @@ -236,32 +585,102 @@ int check_exclude(struct exclude_list_st
608   * be '\0' terminated, so use the returned length to limit the string.
609   * Also, be sure to add this length to the returned pointer before passing
610   * it back to ask for the next token.  This routine parses the "!" (list-
611 - * clearing) token and (if xflags does NOT contain XFLG_WORDS_ONLY) the
612 + * clearing) token and (if xflags does NOT contain XFLG_NO_PREFIXES) the
613   * +/- prefixes for overriding the include/exclude mode.  The *flag_ptr
614   * value will also be set to the MATCHFLG_* bits for the current token.
615   */
616 -static const char *get_exclude_tok(const char *p, unsigned int *len_ptr,
617 -                                  unsigned int *flag_ptr, int xflags)
618 +static const char *get_exclude_tok(const char *p, int xflags,
619 +                       unsigned int *len_ptr, unsigned int *flag_ptr)
620  {
621         const unsigned char *s = (const unsigned char *)p;
622         unsigned int len, mflags = 0;
623 +       int empty_pat_is_OK = 0;
624  
625         if (xflags & XFLG_WORD_SPLIT) {
626                 /* Skip over any initial whitespace. */
627                 while (isspace(*s))
628                         s++;
629 -               /* Update for "!" check. */
630 +               /* Update to point to real start of rule. */
631                 p = (const char *)s;
632         }
633 +       if (!*s)
634 +               return NULL;
635  
636 -       /* Is this a '+' or '-' followed by a space (not whitespace)? */
637 -       if (!(xflags & XFLG_WORDS_ONLY)
638 +       /* Figure out what kind of a filter rule "s" is pointing at. */
639 +       if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE))) {
640 +               char *mods = "";
641 +               switch (*s) {
642 +               case ':':
643 +                       mflags |= MATCHFLG_PERDIR_MERGE
644 +                               | MATCHFLG_FINISH_SETUP;
645 +                       /* FALL THROUGH */
646 +               case '.':
647 +                       mflags |= MATCHFLG_MERGE_FILE;
648 +                       mods = "-+Cens";
649 +                       break;
650 +               case '+':
651 +                       mflags |= MATCHFLG_INCLUDE;
652 +                       break;
653 +               case '-':
654 +                       break;
655 +               case '!':
656 +                       mflags |= MATCHFLG_CLEAR_LIST;
657 +                       mods = NULL;
658 +                       break;
659 +               default:
660 +                       rprintf(FERROR, "Unknown filter rule: %s\n", p);
661 +                       exit_cleanup(RERR_SYNTAX);
662 +               }
663 +               while (mods && *++s && *s != ' ' && *s != '=' && *s != '_') {
664 +                       if (strchr(mods, *s) == NULL) {
665 +                               if (xflags & XFLG_WORD_SPLIT && isspace(*s)) {
666 +                                       s--;
667 +                                       break;
668 +                               }
669 +                               rprintf(FERROR,
670 +                                       "unknown option '%c' in filter rule: %s\n",
671 +                                       *s, p);
672 +                               exit_cleanup(RERR_SYNTAX);
673 +                       }
674 +                       switch (*s) {
675 +                       case '-':
676 +                               mflags |= MATCHFLG_NO_PREFIXES;
677 +                               break;
678 +                       case '+':
679 +                               mflags |= MATCHFLG_NO_PREFIXES
680 +                                       | MATCHFLG_INCLUDE;
681 +                               break;
682 +                       case 'C':
683 +                               empty_pat_is_OK = 1;
684 +                               mflags |= MATCHFLG_NO_PREFIXES
685 +                                       | MATCHFLG_WORD_SPLIT
686 +                                       | MATCHFLG_NO_INHERIT;
687 +                               break;
688 +                       case 'e':
689 +                               mflags |= MATCHFLG_EXCLUDE_SELF;
690 +                               break;
691 +                       case 'n':
692 +                               mflags |= MATCHFLG_NO_INHERIT;
693 +                               break;
694 +                       case 's':
695 +                               mflags |= MATCHFLG_WORD_SPLIT;
696 +                               break;
697 +                       }
698 +               }
699 +               if (*s)
700 +                       s++;
701 +       } else if (!(xflags & XFLG_NO_PREFIXES)
702             && (*s == '-' || *s == '+') && s[1] == ' ') {
703                 if (*s == '+')
704                         mflags |= MATCHFLG_INCLUDE;
705                 s += 2;
706 -       } else if (xflags & XFLG_DEF_INCLUDE)
707 -               mflags |= MATCHFLG_INCLUDE;
708 +       } else {
709 +               if (xflags & XFLG_DEF_INCLUDE)
710 +                       mflags |= MATCHFLG_INCLUDE;
711 +               if (*s == '!')
712 +                       mflags |= MATCHFLG_CLEAR_LIST; /* Tentative! */
713 +       }
714 +
715         if (xflags & XFLG_DIRECTORY)
716                 mflags |= MATCHFLG_DIRECTORY;
717  
718 @@ -274,8 +693,21 @@ static const char *get_exclude_tok(const
719         } else
720                 len = strlen(s);
721  
722 -       if (*p == '!' && len == 1)
723 -               mflags |= MATCHFLG_CLEAR_LIST;
724 +       if (mflags & MATCHFLG_CLEAR_LIST) {
725 +               if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE)) && len) {
726 +                       rprintf(FERROR,
727 +                               "'!' rule has trailing characters: %s\n", p);
728 +                       exit_cleanup(RERR_SYNTAX);
729 +               }
730 +               if (len > 1)
731 +                       mflags &= ~MATCHFLG_CLEAR_LIST;
732 +       } else if (!len && !empty_pat_is_OK) {
733 +               rprintf(FERROR, "unexpected end of filter rule: %s\n", p);
734 +               exit_cleanup(RERR_SYNTAX);
735 +       }
736 +
737 +       if (xflags & XFLG_ABS_PATH)
738 +               mflags |= MATCHFLG_ABS_PATH;
739  
740         *len_ptr = len;
741         *flag_ptr = mflags;
742 @@ -287,35 +719,71 @@ void add_exclude(struct exclude_list_str
743                  int xflags)
744  {
745         unsigned int pat_len, mflags;
746 -       const char *cp;
747 +       const char *cp, *p;
748  
749         if (!pattern)
750                 return;
751  
752 -       cp = pattern;
753 -       pat_len = 0;
754         while (1) {
755 -               cp = get_exclude_tok(cp + pat_len, &pat_len, &mflags, xflags);
756 -               if (!pat_len)
757 +               /* Remember that the returned string is NOT '\0' terminated! */
758 +               cp = get_exclude_tok(pattern, xflags, &pat_len, &mflags);
759 +               if (!cp)
760                         break;
761 +               if (pat_len >= MAXPATHLEN) {
762 +                       rprintf(FERROR, "discarding over-long exclude: %s\n",
763 +                               cp);
764 +                       continue;
765 +               }
766 +               pattern = cp + pat_len;
767  
768                 if (mflags & MATCHFLG_CLEAR_LIST) {
769                         if (verbose > 2) {
770                                 rprintf(FINFO,
771 -                                       "[%s] clearing %sexclude list\n",
772 +                                       "[%s] clearing exclude list%s\n",
773                                         who_am_i(), listp->debug_type);
774                         }
775                         clear_exclude_list(listp);
776                         continue;
777                 }
778  
779 -               make_exclude(listp, cp, pat_len, mflags);
780 -
781 -               if (verbose > 2) {
782 -                       rprintf(FINFO, "[%s] add_exclude(%.*s, %s%sclude)\n",
783 -                               who_am_i(), (int)pat_len, cp, listp->debug_type,
784 -                               mflags & MATCHFLG_INCLUDE ? "in" : "ex");
785 +               if (!pat_len) {
786 +                       cp = ".cvsignore";
787 +                       pat_len = 10;
788 +               }
789 +
790 +               if (mflags & MATCHFLG_MERGE_FILE) {
791 +                       unsigned int len = pat_len;
792 +                       if (mflags & MATCHFLG_EXCLUDE_SELF) {
793 +                               const char *name = strrchr(cp, '/');
794 +                               if (name)
795 +                                       len -= ++name - cp;
796 +                               else
797 +                                       name = cp;
798 +                               make_exclude(listp, name, len, 0);
799 +                               mflags &= ~MATCHFLG_EXCLUDE_SELF;
800 +                               len = pat_len;
801 +                       }
802 +                       if (mflags & MATCHFLG_PERDIR_MERGE) {
803 +                               if (parent_dirscan) {
804 +                                       if (!(p = parse_merge_name(cp, &len, module_dirlen)))
805 +                                               continue;
806 +                                       make_exclude(listp, p, len, mflags);
807 +                                       continue;
808 +                               }
809 +                       } else {
810 +                               int flgs = XFLG_FATAL_ERRORS;
811 +                               if (!(p = parse_merge_name(cp, &len, 0)))
812 +                                       continue;
813 +                               if (mflags & MATCHFLG_INCLUDE)
814 +                                       flgs |= XFLG_DEF_INCLUDE;
815 +                               else if (mflags & MATCHFLG_NO_PREFIXES)
816 +                                       flgs |= XFLG_DEF_EXCLUDE;
817 +                               add_exclude_file(listp, p, flgs);
818 +                               continue;
819 +                       }
820                 }
821 +
822 +               make_exclude(listp, cp, pat_len, mflags);
823         }
824  }
825  
826 @@ -324,7 +792,7 @@ void add_exclude_file(struct exclude_lis
827                       int xflags)
828  {
829         FILE *fp;
830 -       char line[MAXPATHLEN+3]; /* Room for "x " prefix and trailing slash. */
831 +       char line[MAXPATHLEN+11]; /* Room for prefix chars and trailing slash. */
832         char *eob = line + sizeof line - 1;
833         int word_split = xflags & XFLG_WORD_SPLIT;
834  
835 @@ -338,13 +806,19 @@ void add_exclude_file(struct exclude_lis
836         if (!fp) {
837                 if (xflags & XFLG_FATAL_ERRORS) {
838                         rsyserr(FERROR, errno,
839 -                               "failed to open %s file %s",
840 -                               xflags & XFLG_DEF_INCLUDE ? "include" : "exclude",
841 -                               fname);
842 +                               "failed to open %sclude file %s",
843 +                               xflags & XFLG_DEF_INCLUDE ? "in" : "ex",
844 +                               safe_fname(fname));
845                         exit_cleanup(RERR_FILEIO);
846                 }
847                 return;
848         }
849 +       dirbuf[dirbuf_len] = '\0';
850 +
851 +       if (verbose > 2) {
852 +               rprintf(FINFO, "[%s] add_exclude_file(%s,%d)\n",
853 +                       who_am_i(), safe_fname(fname), xflags);
854 +       }
855  
856         while (1) {
857                 char *s = line;
858 @@ -386,7 +860,7 @@ void send_exclude_list(int f)
859         /* This is a complete hack - blame Rusty.  FIXME!
860          * Remove this hack when older rsyncs (below 2.6.4) are gone. */
861         if (list_only == 1 && !recurse)
862 -               add_exclude(&exclude_list, "/*/*", 0);
863 +               add_exclude(&exclude_list, "/*/*", XFLG_DEF_EXCLUDE);
864  
865         for (ent = exclude_list.head; ent; ent = ent->next) {
866                 unsigned int l;
867 @@ -400,10 +874,34 @@ void send_exclude_list(int f)
868                         p[l] = '\0';
869                 }
870  
871 -               if (ent->match_flags & MATCHFLG_INCLUDE) {
872 +               if (ent->match_flags & MATCHFLG_PERDIR_MERGE) {
873 +                       char buf[32], *op = buf;
874 +                       if (protocol_version < 29) {
875 +                               rprintf(FERROR,
876 +                                       "remote rsync is too old to understand per-directory filter files.\n");
877 +                               exit_cleanup(RERR_SYNTAX);
878 +                       }
879 +                       *op++ = ':';
880 +                       if (ent->match_flags & MATCHFLG_WORD_SPLIT)
881 +                               *op++ = 's';
882 +                       if (ent->match_flags & MATCHFLG_NO_INHERIT)
883 +                               *op++ = 'n';
884 +                       if (ent->match_flags & MATCHFLG_EXCLUDE_SELF)
885 +                               *op++ = 'e';
886 +                       if (ent->match_flags & MATCHFLG_NO_PREFIXES) {
887 +                               if (ent->match_flags & MATCHFLG_INCLUDE)
888 +                                       *op++ = '+';
889 +                               else
890 +                                       *op++ = '-';
891 +                       }
892 +                       *op++ = ' ';
893 +                       write_int(f, l + (op - buf));
894 +                       write_buf(f, buf, op - buf);
895 +               } else if (ent->match_flags & MATCHFLG_INCLUDE) {
896                         write_int(f, l + 2);
897                         write_buf(f, "+ ", 2);
898 -               } else if (*p == '-' || *p == '+') {
899 +               } else if (protocol_version >= 29
900 +                   || ((*p == '-' || *p == '+') && p[1] == ' ')) {
901                         write_int(f, l + 2);
902                         write_buf(f, "- ", 2);
903                 } else
904 @@ -417,14 +915,15 @@ void send_exclude_list(int f)
905  
906  void recv_exclude_list(int f)
907  {
908 -       char line[MAXPATHLEN+3]; /* Room for "x " prefix and trailing slash. */
909 +       char line[MAXPATHLEN+11]; /* Room for prefix and trailing slash. */
910 +       unsigned int xflags = protocol_version >= 29 ? 0 : XFLG_DEF_EXCLUDE;
911         unsigned int l;
912  
913         while ((l = read_int(f)) != 0) {
914                 if (l >= sizeof line)
915                         overflow("recv_exclude_list");
916                 read_sbuf(f, line, l);
917 -               add_exclude(&exclude_list, line, 0);
918 +               add_exclude(&exclude_list, line, xflags);
919         }
920  }
921  
922 @@ -441,18 +940,18 @@ static char default_cvsignore[] = 
923  
924  void add_cvs_excludes(void)
925  {
926 +       static unsigned int cvs_flags = XFLG_WORD_SPLIT | XFLG_NO_PREFIXES
927 +                                     | XFLG_DEF_EXCLUDE;
928         char fname[MAXPATHLEN];
929         char *p;
930  
931 -       add_exclude(&exclude_list, default_cvsignore,
932 -                   XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
933 +       add_exclude(&exclude_list, ":C", 0);
934 +       add_exclude(&exclude_list, default_cvsignore, cvs_flags);
935  
936         if ((p = getenv("HOME"))
937             && pathjoin(fname, sizeof fname, p, ".cvsignore") < sizeof fname) {
938 -               add_exclude_file(&exclude_list, fname,
939 -                                XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
940 +               add_exclude_file(&exclude_list, fname, cvs_flags);
941         }
942  
943 -       add_exclude(&exclude_list, getenv("CVSIGNORE"),
944 -                   XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
945 +       add_exclude(&exclude_list, getenv("CVSIGNORE"), cvs_flags);
946  }
947 --- orig/flist.c        2005-01-24 00:18:21
948 +++ flist.c     2004-08-12 18:59:28
949 @@ -40,11 +40,10 @@ extern int module_id;
950  extern int ignore_errors;
951  extern int numeric_ids;
952  
953 -extern int cvs_exclude;
954 -
955  extern int recurse;
956  extern int xfer_dirs;
957  extern char curr_dir[MAXPATHLEN];
958 +extern unsigned int curr_dir_len;
959  extern int filesfrom_fd;
960  
961  extern int one_file_system;
962 @@ -67,7 +66,6 @@ extern int list_only;
963  
964  extern struct exclude_list_struct exclude_list;
965  extern struct exclude_list_struct server_exclude_list;
966 -extern struct exclude_list_struct local_exclude_list;
967  
968  int io_error;
969  
970 @@ -223,8 +221,6 @@ int link_stat(const char *path, STRUCT_S
971   */
972  static int check_exclude_file(char *fname, int is_dir, int exclude_level)
973  {
974 -       int rc;
975 -
976  #if 0 /* This currently never happens, so avoid a useless compare. */
977         if (exclude_level == NO_EXCLUDES)
978                 return 0;
979 @@ -246,10 +242,7 @@ static int check_exclude_file(char *fnam
980         if (exclude_level != ALL_EXCLUDES)
981                 return 0;
982         if (exclude_list.head
983 -           && (rc = check_exclude(&exclude_list, fname, is_dir)) != 0)
984 -               return rc < 0;
985 -       if (local_exclude_list.head
986 -           && check_exclude(&local_exclude_list, fname, is_dir) < 0)
987 +           && check_exclude(&exclude_list, fname, is_dir) < 0)
988                 return 1;
989         return 0;
990  }
991 @@ -983,15 +976,7 @@ void send_file_name(int f, struct file_l
992  
993         if (recursive && S_ISDIR(file->mode)
994             && !(file->flags & FLAG_MOUNT_POINT)) {
995 -               struct exclude_list_struct last_list = local_exclude_list;
996 -               local_exclude_list.head = local_exclude_list.tail = NULL;
997                 send_directory(f, flist, f_name_to(file, fbuf));
998 -               if (verbose > 2) {
999 -                       rprintf(FINFO, "[%s] popping %sexclude list\n",
1000 -                               who_am_i(), local_exclude_list.debug_type);
1001 -               }
1002 -               clear_exclude_list(&local_exclude_list);
1003 -               local_exclude_list = last_list;
1004         }
1005  }
1006  
1007 @@ -1004,6 +989,7 @@ static void send_directory(int f, struct
1008         struct dirent *di;
1009         char fname[MAXPATHLEN];
1010         unsigned int offset;
1011 +       void *save_excludes;
1012         char *p;
1013  
1014         d = opendir(dir);
1015 @@ -1027,18 +1013,7 @@ static void send_directory(int f, struct
1016                 offset++;
1017         }
1018  
1019 -       if (cvs_exclude) {
1020 -               if (strlcpy(p, ".cvsignore", MAXPATHLEN - offset)
1021 -                   < MAXPATHLEN - offset) {
1022 -                       add_exclude_file(&local_exclude_list, fname,
1023 -                                        XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
1024 -               } else {
1025 -                       io_error |= IOERR_GENERAL;
1026 -                       rprintf(FINFO,
1027 -                               "cannot cvs-exclude in long-named directory %s\n",
1028 -                               full_fname(fname));
1029 -               }
1030 -       }
1031 +       save_excludes = push_local_excludes(fname, offset);
1032  
1033         for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
1034                 char *dname = d_name(di);
1035 @@ -1060,6 +1035,8 @@ static void send_directory(int f, struct
1036                 rsyserr(FERROR, errno, "readdir(%s)", dir);
1037         }
1038  
1039 +       pop_local_excludes(save_excludes);
1040 +
1041         closedir(d);
1042  }
1043  
1044 @@ -1079,6 +1056,7 @@ struct file_list *send_file_list(int f, 
1045         char *p, *dir, olddir[sizeof curr_dir];
1046         char lastpath[MAXPATHLEN] = "";
1047         struct file_list *flist;
1048 +       BOOL need_first_push = True;
1049         int64 start_write;
1050         int use_ff_fd = 0;
1051  
1052 @@ -1099,6 +1077,10 @@ struct file_list *send_file_list(int f, 
1053                                 exit_cleanup(RERR_FILESELECT);
1054                         }
1055                         use_ff_fd = 1;
1056 +                       if (curr_dir_len < MAXPATHLEN - 1) {
1057 +                               push_local_excludes(curr_dir, curr_dir_len);
1058 +                               need_first_push = False;
1059 +                       }
1060                 }
1061         }
1062  
1063 @@ -1135,6 +1117,15 @@ struct file_list *send_file_list(int f, 
1064                 } else if (recurse > 0)
1065                         recurse = 0;
1066  
1067 +               if (need_first_push) {
1068 +                       if ((p = strrchr(fname, '/')) != NULL) {
1069 +                               if (*++p && strcmp(p, ".") != 0)
1070 +                                       push_local_excludes(fname, p - fname);
1071 +                       } else if (strcmp(fname, ".") != 0)
1072 +                               push_local_excludes(fname, 0);
1073 +                       need_first_push = False;
1074 +               }
1075 +
1076                 if (link_stat(fname, &st, keep_dirlinks) != 0) {
1077                         if (f != -1) {
1078                                 io_error |= IOERR_GENERAL;
1079 --- orig/loadparm.c     2005-01-01 21:11:00
1080 +++ loadparm.c  2005-01-16 19:48:52
1081 @@ -133,6 +133,7 @@ typedef struct
1082         char *auth_users;
1083         char *secrets_file;
1084         BOOL strict_modes;
1085 +       char *filter;
1086         char *exclude;
1087         char *exclude_from;
1088         char *include;
1089 @@ -175,6 +176,7 @@ static service sDefault =
1090         NULL,    /* auth users */
1091         NULL,    /* secrets file */
1092         True,   /* strict modes */
1093 +       NULL,    /* filter */
1094         NULL,    /* exclude */
1095         NULL,    /* exclude from */
1096         NULL,    /* include */
1097 @@ -294,6 +296,7 @@ static struct parm_struct parm_table[] =
1098    {"auth users",       P_STRING,  P_LOCAL,  &sDefault.auth_users,  NULL,   0},
1099    {"secrets file",     P_STRING,  P_LOCAL,  &sDefault.secrets_file,NULL,   0},
1100    {"strict modes",     P_BOOL,    P_LOCAL,  &sDefault.strict_modes,NULL,   0},
1101 +  {"filter",           P_STRING,  P_LOCAL,  &sDefault.filter,      NULL,   0},
1102    {"exclude",          P_STRING,  P_LOCAL,  &sDefault.exclude,     NULL,   0},
1103    {"exclude from",     P_STRING,  P_LOCAL,  &sDefault.exclude_from,NULL,   0},
1104    {"include",          P_STRING,  P_LOCAL,  &sDefault.include,     NULL,   0},
1105 @@ -378,6 +381,7 @@ FN_LOCAL_STRING(lp_hosts_deny, hosts_den
1106  FN_LOCAL_STRING(lp_auth_users, auth_users)
1107  FN_LOCAL_STRING(lp_secrets_file, secrets_file)
1108  FN_LOCAL_BOOL(lp_strict_modes, strict_modes)
1109 +FN_LOCAL_STRING(lp_filter, filter)
1110  FN_LOCAL_STRING(lp_exclude, exclude)
1111  FN_LOCAL_STRING(lp_exclude_from, exclude_from)
1112  FN_LOCAL_STRING(lp_include, include)
1113 --- orig/options.c      2005-01-24 00:18:21
1114 +++ options.c   2005-01-23 07:33:55
1115 @@ -146,6 +146,7 @@ int list_only = 0;
1116  char *batch_name = NULL;
1117  
1118  static int daemon_opt;   /* sets am_daemon after option error-reporting */
1119 +static int F_option_cnt = 0;
1120  static int modify_window_set;
1121  static char *dest_option = NULL;
1122  static char *max_size_arg;
1123 @@ -294,6 +295,9 @@ void usage(enum logcode F)
1124    rprintf(F," -P                          equivalent to --partial --progress\n");
1125    rprintf(F," -z, --compress              compress file data\n");
1126    rprintf(F," -C, --cvs-exclude           auto ignore files in the same way CVS does\n");
1127 +  rprintf(F," -f, --filter=RULE           add a file-filtering RULE\n");
1128 +  rprintf(F," -F                          same as --filter=': /.rsync-filter'\n");
1129 +  rprintf(F,"                             repeated: --filter='- .rsync-filter'\n");
1130    rprintf(F,"     --exclude=PATTERN       exclude files matching PATTERN\n");
1131    rprintf(F,"     --exclude-from=FILE     exclude patterns listed in FILE\n");
1132    rprintf(F,"     --include=PATTERN       don't exclude files matching PATTERN\n");
1133 @@ -324,7 +328,7 @@ void usage(enum logcode F)
1134  }
1135  
1136  enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
1137 -      OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST,
1138 +      OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST,
1139        OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW,
1140        OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_TIMEOUT, OPT_MAX_SIZE,
1141        OPT_REFUSED_BASE = 9000};
1142 @@ -346,6 +350,7 @@ static struct poptOption long_options[] 
1143    {"delete-excluded",  0,  POPT_ARG_NONE,   &delete_excluded, 0, 0, 0 },
1144    {"force",            0,  POPT_ARG_NONE,   &force_delete, 0, 0, 0 },
1145    {"numeric-ids",      0,  POPT_ARG_NONE,   &numeric_ids, 0, 0, 0 },
1146 +  {"filter",          'f', POPT_ARG_STRING, 0, OPT_FILTER, 0, 0 },
1147    {"exclude",          0,  POPT_ARG_STRING, 0, OPT_EXCLUDE, 0, 0 },
1148    {"include",          0,  POPT_ARG_STRING, 0, OPT_INCLUDE, 0, 0 },
1149    {"exclude-from",     0,  POPT_ARG_STRING, 0, OPT_EXCLUDE_FROM, 0, 0 },
1150 @@ -398,6 +403,7 @@ static struct poptOption long_options[] 
1151    {"ignore-errors",    0,  POPT_ARG_NONE,   &ignore_errors, 0, 0, 0 },
1152    {"blocking-io",      0,  POPT_ARG_VAL,    &blocking_io, 1, 0, 0 },
1153    {"no-blocking-io",   0,  POPT_ARG_VAL,    &blocking_io, 0, 0, 0 },
1154 +  {0,                 'F', POPT_ARG_NONE,   0, 'F', 0, 0 },
1155    {0,                 'P', POPT_ARG_NONE,   0, 'P', 0, 0 },
1156    {"port",             0,  POPT_ARG_INT,    &rsync_port, 0, 0, 0 },
1157    {"log-format",       0,  POPT_ARG_STRING, &log_format, 0, 0, 0 },
1158 @@ -619,10 +625,15 @@ int parse_arguments(int *argc, const cha
1159                         modify_window_set = 1;
1160                         break;
1161  
1162 -               case OPT_EXCLUDE:
1163 +               case OPT_FILTER:
1164                         add_exclude(&exclude_list, poptGetOptArg(pc), 0);
1165                         break;
1166  
1167 +               case OPT_EXCLUDE:
1168 +                       add_exclude(&exclude_list, poptGetOptArg(pc),
1169 +                                   XFLG_DEF_EXCLUDE);
1170 +                       break;
1171 +
1172                 case OPT_INCLUDE:
1173                         add_exclude(&exclude_list, poptGetOptArg(pc),
1174                                     XFLG_DEF_INCLUDE);
1175 @@ -640,8 +651,8 @@ int parse_arguments(int *argc, const cha
1176                                         goto options_rejected;
1177                         }
1178                         add_exclude_file(&exclude_list, arg, XFLG_FATAL_ERRORS
1179 -                                        | (opt == OPT_INCLUDE_FROM
1180 -                                         ? XFLG_DEF_INCLUDE : 0));
1181 +                               | (opt == OPT_INCLUDE_FROM ? XFLG_DEF_INCLUDE
1182 +                                                          : XFLG_DEF_EXCLUDE));
1183                         break;
1184  
1185                 case 'h':
1186 @@ -665,6 +676,19 @@ int parse_arguments(int *argc, const cha
1187                         am_sender = 1;
1188                         break;
1189  
1190 +               case 'F':
1191 +                       switch (++F_option_cnt) {
1192 +                       case 1:
1193 +                               add_exclude(&exclude_list,
1194 +                                           ": /.rsync-filter", 0);
1195 +                               break;
1196 +                       case 2:
1197 +                               add_exclude(&exclude_list,
1198 +                                           "- .rsync-filter", 0);
1199 +                               break;
1200 +                       }
1201 +                       break;
1202 +
1203                 case 'P':
1204                         do_progress = 1;
1205                         keep_partial = 1;
1206 @@ -971,7 +995,7 @@ int parse_arguments(int *argc, const cha
1207                                 partial_dir = NULL;
1208                         else if (*partial_dir != '/') {
1209                                 add_exclude(&exclude_list, partial_dir,
1210 -                                           XFLG_DIRECTORY);
1211 +                                           XFLG_DIRECTORY | XFLG_DEF_EXCLUDE);
1212                         }
1213                         keep_partial = 1;
1214                 }
1215 --- orig/rsync.h        2005-01-21 00:35:26
1216 +++ rsync.h     2005-01-17 00:16:04
1217 @@ -109,9 +109,11 @@
1218  
1219  #define XFLG_FATAL_ERRORS      (1<<0)
1220  #define XFLG_DEF_INCLUDE       (1<<1)
1221 -#define XFLG_WORDS_ONLY        (1<<2)
1222 +#define XFLG_DEF_EXCLUDE       (1<<2)
1223  #define XFLG_WORD_SPLIT        (1<<3)
1224  #define XFLG_DIRECTORY         (1<<4)
1225 +#define XFLG_NO_PREFIXES       (1<<5)
1226 +#define XFLG_ABS_PATH          (1<<6)
1227  
1228  #define PERMS_REPORT           (1<<0)
1229  #define PERMS_SKIP_MTIME       (1<<1)
1230 @@ -507,11 +509,21 @@ struct map_struct {
1231  #define MATCHFLG_INCLUDE       (1<<4) /* this is an include, not an exclude */
1232  #define MATCHFLG_DIRECTORY     (1<<5) /* this matches only directories */
1233  #define MATCHFLG_CLEAR_LIST    (1<<6) /* this item is the "!" token */
1234 +#define MATCHFLG_WORD_SPLIT    (1<<7) /* split rules on whitespace */
1235 +#define MATCHFLG_NO_INHERIT    (1<<8) /* don't inherit these rules */
1236 +#define MATCHFLG_NO_PREFIXES   (1<<9) /* parse no prefixes from patterns */
1237 +#define MATCHFLG_MERGE_FILE    (1<<10)/* specifies a file to merge */
1238 +#define MATCHFLG_PERDIR_MERGE  (1<<11)/* merge-file is searched per-dir */
1239 +#define MATCHFLG_EXCLUDE_SELF  (1<<12)/* merge-file name should be excluded */
1240 +#define MATCHFLG_FINISH_SETUP  (1<<13)/* per-dir merge file needs setup */
1241  struct exclude_struct {
1242         struct exclude_struct *next;
1243         char *pattern;
1244         unsigned int match_flags;
1245 -       int slash_cnt;
1246 +       union {
1247 +               int slash_cnt;
1248 +               struct exclude_list_struct *mergelist;
1249 +       } u;
1250  };
1251  
1252  struct exclude_list_struct {
1253 --- orig/rsync.yo       2005-01-24 00:18:21
1254 +++ rsync.yo    2005-01-21 19:56:38
1255 @@ -362,6 +362,9 @@ verb(
1256   -P                          equivalent to --partial --progress
1257   -z, --compress              compress file data
1258   -C, --cvs-exclude           auto ignore files in the same way CVS does
1259 + -f, --filter=RULE           add a file-filtering RULE
1260 + -F                          same as --filter=': /.rsync-filter'
1261 +                             repeated: --filter='- .rsync-filter'
1262       --exclude=PATTERN       exclude files matching PATTERN
1263       --exclude-from=FILE     exclude patterns listed in FILE
1264       --include=PATTERN       don't exclude files matching PATTERN
1265 @@ -764,14 +767,41 @@ Finally, any file is ignored if it is in
1266  .cvsignore file and matches one of the patterns listed therein.
1267  See the bf(cvs(1)) manual for more information.
1268  
1269 -dit(bf(--exclude=PATTERN)) This option allows you to selectively exclude
1270 -certain files from the list of files to be transferred. This is most
1271 -useful in combination with a recursive transfer.
1272 +dit(bf(-f, --filter=RULE)) This option allows you to add rules to selectively
1273 +exclude certain files from the list of files to be transferred. This is
1274 +most useful in combination with a recursive transfer.
1275  
1276 -You may use as many --exclude options on the command line as you like
1277 +You may use as many --filter options on the command line as you like
1278  to build up the list of files to exclude.
1279  
1280 -See the EXCLUDE PATTERNS section for detailed information on this option.
1281 +See the FILTER RULES section for detailed information on this option.
1282 +
1283 +dit(bf(-F)) The -F option is a shorthand for adding two --filter rules to
1284 +your command.  The first time it is used is a shorthand for this rule:
1285 +
1286 +verb(
1287 +  --filter=': /.rsync-filter'
1288 +)
1289 +
1290 +This tells rsync to look for per-directory .rsync-filter files that have
1291 +been sprinkled through the hierarchy and use their rules to filter the
1292 +files in the transfer.  If -F is repeated, it is a shorthand for this
1293 +rule:
1294 +
1295 +verb(
1296 +  --filter='- .rsync-filter'
1297 +)
1298 +
1299 +This filters out the .rsync-filter files themselves from the transfer.
1300 +
1301 +See the FILTER RULES section for detailed information on how these options
1302 +work.
1303 +
1304 +dit(bf(--exclude=PATTERN)) This option is a simplified form of the
1305 +--filter option that defaults to an exclude rule and does not allow
1306 +the full rule-parsing syntax of normal filter rules.
1307 +
1308 +See the FILTER RULES section for detailed information on this option.
1309  
1310  dit(bf(--exclude-from=FILE)) This option is similar to the --exclude
1311  option, but instead it adds all exclude patterns listed in the file
1312 @@ -779,11 +809,11 @@ FILE to the exclude list.  Blank lines i
1313  ';' or '#' are ignored.
1314  If em(FILE) is bf(-) the list will be read from standard input.
1315  
1316 -dit(bf(--include=PATTERN)) This option tells rsync to not exclude the
1317 -specified pattern of filenames. This is useful as it allows you to
1318 -build up quite complex exclude/include rules.
1319 +dit(bf(--include=PATTERN)) This option is a simplified form of the
1320 +--filter option that defaults to an include rule and does not allow
1321 +the full rule-parsing syntax of normal filter rules.
1322  
1323 -See the EXCLUDE PATTERNS section for detailed information on this option.
1324 +See the FILTER RULES section for detailed information on this option.
1325  
1326  dit(bf(--include-from=FILE)) This specifies a list of include patterns
1327  from a file.
1328 @@ -828,7 +858,8 @@ was located on the remote "src" host.
1329  
1330  dit(bf(-0, --from0)) This tells rsync that the filenames it reads from a
1331  file are terminated by a null ('\0') character, not a NL, CR, or CR+LF.
1332 -This affects --exclude-from, --include-from, and --files-from.
1333 +This affects --exclude-from, --include-from, --files-from, and any
1334 +merged files specified in a --filter rule.
1335  It does not affect --cvs-exclude (since all names read from a .cvsignore
1336  file are split on whitespace).
1337  
1338 @@ -967,8 +998,8 @@ If the partial-dir value is not an absol
1339  will prevent partial-dir files from being transferred and also prevent the
1340  untimely deletion of partial-dir items on the receiving side.  An example:
1341  the above --partial-dir option would add an "--exclude=.rsync-partial/"
1342 -rule at the end of any other include/exclude rules.  Note that if you are
1343 -supplying your own include/exclude rules, you may need to manually insert a
1344 +rule at the end of any other filter rules.  Note that if you are
1345 +supplying your own filter rules, you may need to manually insert a
1346  rule for this directory exclusion somewhere higher up in the list so that
1347  it has a high enough priority to be effective (e.g., if your rules specify
1348  a trailing --exclude=* rule, the auto-added rule will be ineffective).
1349 @@ -1125,30 +1156,322 @@ page describing the options available fo
1350  
1351  enddit()
1352  
1353 -manpagesection(EXCLUDE PATTERNS)
1354 -
1355 -The exclude and include patterns specified to rsync allow for flexible
1356 -selection of which files to transfer and which files to skip.
1357 +manpagesection(FILTER RULES)
1358  
1359 -Rsync builds an ordered list of include/exclude options as specified on
1360 -the command line. Rsync checks each file and directory 
1361 -name against each exclude/include pattern in turn. The first matching
1362 -pattern is acted on. If it is an exclude pattern, then that file is
1363 -skipped. If it is an include pattern then that filename is not
1364 -skipped. If no matching include/exclude pattern is found then the
1365 +The filter rules allow for flexible selection of which files to transfer
1366 +(include) and which files to skip (exclude).  The rules either directly
1367 +specify include/exclude patterns or they specify a way to acquire more
1368 +include/exclude patterns (e.g. to read them from a file).
1369 +
1370 +As the list of files/directories to transfer is built, rsync checks each
1371 +name to be transferred against the list of include/exclude patterns in
1372 +turn, and the first matching pattern is acted on:  if it is an exclude
1373 +pattern, then that file is skipped; if it is an include pattern then that
1374 +filename is not skipped; if no matching pattern is found, then the
1375  filename is not skipped.
1376  
1377 -The filenames matched against the exclude/include patterns are relative
1378 -to the "root of the transfer".  If you think of the transfer as a
1379 -subtree of names that are being sent from sender to receiver, the root
1380 -is where the tree starts to be duplicated in the destination directory.
1381 -This root governs where patterns that start with a / match (see below).
1382 +Rsync builds an ordered list of filter rules as specified on the
1383 +command-line.  Filter rules have the following syntax:
1384 +
1385 +itemize(
1386 +  it() x RULE
1387 +  it() xMODIFIERS RULE
1388 +  it() !
1389 +)
1390 +
1391 +The 'x' is a single-letter that specifies the kind of rule to create.  It
1392 +can have trailing modifiers, and is separated from the RULE by one of the
1393 +following characters: a single space, an equal-sign (=), or an underscore
1394 +(_).  Here are the available rule prefixes:
1395 +
1396 +verb(
1397 +  -  specifies an exclude pattern.
1398 +  +  specifies an include pattern.
1399 +  .  specifies a merge-file to read for more rules.
1400 +  :  specifies a per-directory merge-file.
1401 +  !  clears the current include/exclude list
1402 +)
1403 +
1404 +Note that the --include/--exclude command-line options do not allow the
1405 +full range of rule parsing as described above -- they only allow the
1406 +specification of include/exclude patterns and the "!" token (not to
1407 +mention the comment lines when reading rules from a file).  If a pattern
1408 +does not begin with "- " (dash, space) or "+ " (plus, space), then the
1409 +rule will be interpreted as if "+ " (for an include option) or "- " (for
1410 +an exclude option) were prefixed to the string.  A --filter option, on
1411 +the other hand, must always contain one of the prefixes above.
1412 +
1413 +Note also that the --filter, --include, and --exclude options take one
1414 +rule/pattern each. To add multiple ones, you can repeat the options on
1415 +the command-line, use the merge-file syntax of the --filter option, or
1416 +the --include-from/--exclude-from options.
1417 +
1418 +When rules are being read from a file, empty lines are ignored, as are
1419 +comment lines that start with a "#".
1420 +
1421 +manpagesection(INCLUDE/EXCLUDE PATTERN RULES)
1422 +
1423 +You can include and exclude files by specifing patterns using the "+" and
1424 +"-" filter rules (as introduced in the FILTER RULES section above).  These
1425 +rules specify a pattern that is matched against the names of the files
1426 +that are going to be transferred.  These patterns can take several forms:
1427 +
1428 +itemize(
1429 +
1430 +  it() if the pattern starts with a / then it is anchored to a
1431 +  particular spot in the hierarchy of files, otherwise it is matched
1432 +  against the end of the pathname.  This is similar to a leading ^ in
1433 +  regular expressions.
1434 +  Thus "/foo" would match a file called "foo" at either the "root of the
1435 +  transfer" (for a global rule) or in the merge-file's directory (for a
1436 +  per-directory rule).
1437 +  An unqualified "foo" would match any file or directory named "foo"
1438 +  anywhere in the tree because the algorithm is applied recursively from
1439 +  the
1440 +  top down; it behaves as if each path component gets a turn at being the
1441 +  end of the file name.  Even the unanchored "sub/foo" would match at
1442 +  any point in the hierarchy where a "foo" was found within a directory
1443 +  named "sub".  See the section on ANCHORING INCLUDE/EXCLUDE PATTERNS for
1444 +  a full discussion of how to specify a pattern that matches at the root
1445 +  of the transfer.
1446 +
1447 +  it() if the pattern ends with a / then it will only match a
1448 +  directory, not a file, link, or device.
1449 +
1450 +  it() if the pattern contains a wildcard character from the set
1451 +  *?[ then expression matching is applied using the shell filename
1452 +  matching rules. Otherwise a simple string match is used.
1453 +
1454 +  it() the double asterisk pattern "**" will match slashes while a
1455 +  single asterisk pattern "*" will stop at slashes.
1456 +
1457 +  it() if the pattern contains a / (not counting a trailing /) or a "**"
1458 +  then it is matched against the full pathname, including any leading
1459 +  directories. If the pattern doesn't contain a / or a "**", then it is
1460 +  matched only against the final component of the filename.
1461 +  (Remember that the algorithm is applied recursively so "full filename"
1462 +  can actually be any portion of a path fomr the starting directory on
1463 +  down.)
1464 +
1465 +)
1466 +
1467 +Note that, when using the --recursive (-r) option (which is implied by
1468 +-a), every subcomponent of every path is visited from the top down, so
1469 +include/exclude patterns get applied recursively to each subcomponent's
1470 +full name (e.g. to include "/foo/bar/baz" the subcomponents "/foo" and
1471 +"/foo/bar" must not be excluded).
1472 +The exclude patterns actually short-circuit the directory traversal stage
1473 +when rsync finds the files to send.  If a pattern excludes a particular
1474 +parent directory, it can render a deeper include pattern ineffectual
1475 +because rsync did not descend through that excluded section of the
1476 +hierarchy.  This is particularly important when using a trailing '*' rule.
1477 +For instance, this won't work:
1478 +
1479 +verb(
1480 +    + /some/path/this-file-will-not-be-found
1481 +    + /file-is-included
1482 +    - *
1483 +)
1484 +
1485 +This fails because the parent directory "some" is excluded by the '*'
1486 +rule, so rsync never visits any of the files in the "some" or "some/path"
1487 +directories.  One solution is to ask for all directories in the hierarchy
1488 +to be included by using a single rule: "+_*/" (put it somewhere before the
1489 +"-_*" rule).  Another solution is to add specific include rules for all
1490 +the parent dirs that need to be visited.  For instance, this set of rules
1491 +works fine:
1492 +
1493 +verb(
1494 +    + /some/
1495 +    + /some/path/
1496 +    + /some/path/this-file-is-found
1497 +    + /file-also-included
1498 +    - *
1499 +)
1500 +
1501 +Here are some examples of exclude/include matching:
1502 +
1503 +itemize(
1504 +  it() "- *.o" would exclude all filenames matching *.o
1505 +  it() "- /foo" would exclude a file called foo in the transfer-root directory
1506 +  it() "- foo/" would exclude any directory called foo
1507 +  it() "- /foo/*/bar" would exclude any file called bar two
1508 +  levels below a directory called foo in the transfer-root directory
1509 +  it() "- /foo/**/bar" would exclude any file called bar two
1510 +  or more levels below a directory called foo in the transfer-root directory
1511 +  it() The combination of "+ */", "+ *.c", and "- *" would include all 
1512 +  directories and C source files but nothing else.
1513 +  it() The combination of "+ foo/", "+ foo/bar.c", and "- *" would include
1514 +  only the foo directory and foo/bar.c (the foo directory must be
1515 +  explicitly included or it would be excluded by the "*")
1516 +)
1517 +
1518 +manpagesection(MERGE-FILE FILTER RULES)
1519 +
1520 +You can merge whole files into your filter rules by specifying either a
1521 +"." or a ":" filter rule (as introduced in the FILTER RULES section
1522 +above).
1523 +
1524 +There are two kinds of merged files -- single-instance ('.') and
1525 +per-directory (':').  A single-instance merge file is read one time, and
1526 +its rules are incorporated into the filter list in the place of the "."
1527 +rule.  For per-directory merge files, rsync will scan every directory that
1528 +it traverses for the named file, merging its contents when the file exists
1529 +into the current list of inherited rules.  These per-directory rule files
1530 +must be created on the sending side because it is the sending side that is
1531 +being scanned for the available files to transfer.  These rule files may
1532 +also need to be transferred to the receiving side if you want them to
1533 +affect what files don't get deleted (see PER-DIRECTORY RULES AND DELETE
1534 +below).
1535 +
1536 +Some examples:
1537 +
1538 +verb(
1539 +    . /etc/rsync/default.rules
1540 +    : .per-dir-filter
1541 +    :n- .non-inherited-per-dir-excludes
1542 +)
1543 +
1544 +The following modifiers are accepted after the "." or ":":
1545 +
1546 +itemize(
1547 +  it() A "-" specifies that the file should consist of only exclude
1548 +  patterns, with no other rule-parsing except for the list-clearing
1549 +  token ("!").
1550 +
1551 +  it() A "+" specifies that the file should consist of only include
1552 +  patterns, with no other rule-parsing except for the list-clearing
1553 +  token ("!").
1554 +
1555 +  it() A "C" is a shorthand for the modifiers "sn-", which makes the
1556 +  parsing compatible with the way CVS parses their exclude files.  If no
1557 +  filename is specified, ".cvsignore" is assumed.
1558 +
1559 +  it() A "e" will exclude the merge-file from the transfer; e.g.
1560 +  ":e_.rules" is like ":_.rules" and "-_.rules".
1561 +
1562 +  it() An "n" specifies that the rules are not inherited by subdirectories.
1563 +
1564 +  it() An "s" specifies that the rules are split on all whitespace instead
1565 +  of the normal line-splitting.  This also turns off comments.  Note: the
1566 +  space that separates the prefix from the rule is treated specially, so
1567 +  "- foo + bar" is parsed as two rules (assuming that "-" or "+" was not
1568 +  specified to turn off the parsing of prefixes).
1569 +)
1570 +
1571 +Per-directory rules are inherited in all subdirectories of the directory
1572 +where the merge-file was found unless the 'n' modifier was used.  Each
1573 +subdirectory's rules are prefixed to the inherited per-directory rules
1574 +from its parents, which gives the newest rules a higher priority than the
1575 +inherited rules.  The entire set of per-dir rules is grouped together in
1576 +the spot where the merge-file was specified, so it is possible to override
1577 +per-dir rules via a rule that got specified earlier in the list of global
1578 +rules.  When the list-clearing rule ("!") is read from a per-directory
1579 +file, it only clears the inherited rules for the current merge file.
1580 +
1581 +Another way to prevent a single per-dir rule from being inherited is to
1582 +anchor it with a leading slash.  Anchored rules in a per-directory
1583 +merge-file are relative to the merge-file's directory, so a pattern "/foo"
1584 +would only match the file "foo" in the directory where the per-dir filter
1585 +file was found.
1586 +
1587 +Here's an example filter file which you'd specify via --filter=". file":
1588 +
1589 +verb(
1590 +    . /home/user/.global-filter
1591 +    - *.gz
1592 +    : .rules
1593 +    + *.[ch]
1594 +    - *.o
1595 +)
1596 +
1597 +This will merge the contents of the /home/user/.global-filter file at the
1598 +start of the list and also turns the ".rules" filename into a per-directory
1599 +filter file.  All rules read-in prior to the start of the directory scan
1600 +follow the global anchoring rules (i.e. a leading slash matches at the root
1601 +of the transfer).
1602 +
1603 +If a per-directory merge-file is specified with a path that is a parent
1604 +directory of the first transfer directory, rsync will scan all the parent
1605 +dirs from that starting point to the transfer directory for the indicated
1606 +per-directory file.  For instance, here is a common filter (see -F):
1607 +
1608 +verb(
1609 +  --filter=': /.rsync-filter'
1610 +)
1611 +
1612 +That rule tells rsync to scan for the file .rsync-filter in all
1613 +directories from the root down through the parent directory of the
1614 +transfer prior to the start of the normal directory scan of the file in
1615 +the directories that are sent as a part of the transfer.  (Note: for an
1616 +rsync daemon, the root is always the same as the module's "path".)
1617 +
1618 +Some examples of this pre-scanning for per-directory files:
1619 +
1620 +verb(
1621 +  rsync -avF /src/path/ /dest/dir
1622 +  rsync -av --filter=': ../../.rsync-filter' /src/path/ /dest/dir
1623 +  rsync -av --fitler=': .rsync-filter' /src/path/ /dest/dir
1624 +)
1625 +
1626 +The first two commands above will look for ".rsync-filter" in "/" and
1627 +"/src" before the normal scan begins looking for the file in "/src/path"
1628 +and its subdirectories.  The last command avoids the parent-dir scan
1629 +and only looks for the ".rsync-filter" files in each directory that is
1630 +a part of the transfer.
1631 +
1632 +If you want to include the contents of a ".cvsignore" in your patterns,
1633 +you should use the rule ":C" -- this is a short-hand for the rule
1634 +":sn-_.cvsignore", and ensures that the .cvsignore file's contents are
1635 +interpreted according to the same parsing rules that CVS uses.  You can
1636 +use this to affect where the --cvs-exclude (-C) option's inclusion of the
1637 +per-directory .cvsignore file gets placed into your rules by putting a
1638 +":C" wherever you like in your filter rules.  Without this, rsync would
1639 +add the per-dir rule for the .cvignore file at the end of all your other
1640 +rules (giving it a lower priority than your command-line rules).  For
1641 +example:
1642 +
1643 +verb(
1644 +  cat <<EOT | rsync -avC --filter='. -' a/ b
1645 +  + foo.o
1646 +  :C
1647 +  - *.old
1648 +  EOT
1649 +
1650 +  rsync -avC --include=foo.o -f :C --exclude='*.old' a/ b
1651 +)
1652 +
1653 +Both of the above rsync commands are identical.  Each one will merge all
1654 +the per-directory .cvsignore rules in the middle of the list rather than
1655 +at the end.  This allows their dir-specific rules to supersede the rules
1656 +that follow the :C instead of being subservient to all your rules.  (The
1657 +global rules taken from the $HOME/.cvsignore file and from $CVSIGNORE are
1658 +not repositioned from their spot at the end of your rules, however -- feel
1659 +free to manually include $HOME/.cvsignore elsewhere in your rules.)
1660 +
1661 +manpagesection(LIST-CLEARING FILTER RULE)
1662 +
1663 +You can clear the current include/exclude list by using the "!" filter
1664 +rule (as introduced in the FILTER RULES section above).  The "current"
1665 +list is either the global list of rules (if the rule is encountered while
1666 +parsing the filter options) or a set of per-directory rules (which are
1667 +inherited in their own sub-list, so a subdirectory can use this to clear
1668 +out the parent's rules).
1669 +
1670 +manpagesection(ANCHORING INCLUDE/EXCLUDE PATTERNS)
1671 +
1672 +As mentioned earlier, global include/exclude patterns are anchored at the
1673 +"root of the transfer" (as opposed to per-directory patterns, which are
1674 +anchored at the merge-file's directory).  If you think of the transfer as
1675 +a subtree of names that are being sent from sender to receiver, the
1676 +transfer-root is where the tree starts to be duplicated in the destination
1677 +directory.  This root governs where patterns that start with a / match.
1678  
1679  Because the matching is relative to the transfer-root, changing the
1680  trailing slash on a source path or changing your use of the --relative
1681  option affects the path you need to use in your matching (in addition to
1682  changing how much of the file tree is duplicated on the destination
1683 -system).  The following examples demonstrate this.
1684 +host).  The following examples demonstrate this.
1685  
1686  Let's say that we want to match two source files, one with an absolute
1687  path of "/home/me/foo/bar", and one with a path of "/home/you/bar/baz".
1688 @@ -1180,115 +1503,59 @@ verb(
1689     Target file: /dest/you/bar/baz
1690  )
1691  
1692 -The easiest way to see what name you should include/exclude is to just
1693 +The easiest way to see what name you should filter is to just
1694  look at the output when using --verbose and put a / in front of the name
1695  (use the --dry-run option if you're not yet ready to copy any files).
1696  
1697 -Note that, when using the --recursive (-r) option (which is implied by -a),
1698 -every subcomponent of
1699 -every path is visited from the top down, so include/exclude patterns get
1700 -applied recursively to each subcomponent's full name (e.g. to include
1701 -"/foo/bar/baz" the subcomponents "/foo" and "/foo/bar" must not be excluded).
1702 -The exclude patterns actually short-circuit the directory traversal stage
1703 -when rsync finds the files to send.  If a pattern excludes a particular
1704 -parent directory, it can render a deeper include pattern ineffectual
1705 -because rsync did not descend through that excluded section of the
1706 -hierarchy.
1707 -
1708 -Note also that the --include and --exclude options take one pattern
1709 -each. To add multiple patterns use the --include-from and
1710 ---exclude-from options or multiple --include and --exclude options. 
1711 +manpagesection(PER-DIRECTORY RULES AND DELETE)
1712  
1713 -The patterns can take several forms. The rules are:
1714 +Without a delete option, per-directory rules are only relevant on the
1715 +sending side, so you can feel free to exclude the merge files themselves
1716 +without affecting the transfer.  To make this easy, the 'e' modifier adds
1717 +this exclude for you, as seen in these two equivalent commands:
1718  
1719 -itemize(
1720 -
1721 -  it() if the pattern starts with a / then it is matched against the
1722 -  start of the filename, otherwise it is matched against the end of
1723 -  the filename.
1724 -  This is the equivalent of a leading ^ in regular expressions.
1725 -  Thus "/foo" would match a file called "foo" at the transfer-root
1726 -  (see above for how this is different from the filesystem-root).
1727 -  On the other hand, "foo" would match any file called "foo"
1728 -  anywhere in the tree because the algorithm is applied recursively from
1729 -  top down; it behaves as if each path component gets a turn at being the
1730 -  end of the file name.
1731 -
1732 -  it() if the pattern ends with a / then it will only match a
1733 -  directory, not a file, link, or device.
1734 -
1735 -  it() if the pattern contains a wildcard character from the set
1736 -  *?[ then expression matching is applied using the shell filename
1737 -  matching rules. Otherwise a simple string match is used.
1738 -
1739 -  it() the double asterisk pattern "**" will match slashes while a
1740 -  single asterisk pattern "*" will stop at slashes.
1741 +verb(
1742 +  rsync -av --filter=': .excl' --exclude=.excl host:src/dir /dest
1743 +  rsync -av --filter=':e .excl' host:src/dir /dest
1744 +)
1745  
1746 -  it() if the pattern contains a / (not counting a trailing /) or a "**"
1747 -  then it is matched against the full filename, including any leading
1748 -  directory. If the pattern doesn't contain a / or a "**", then it is
1749 -  matched only against the final component of the filename.  Again,
1750 -  remember that the algorithm is applied recursively so "full filename" can
1751 -  actually be any portion of a path below the starting directory.
1752 -
1753 -  it() if the pattern starts with "+ " (a plus followed by a space)
1754 -  then it is always considered an include pattern, even if specified as
1755 -  part of an exclude option. The prefix is discarded before matching.
1756 -
1757 -  it() if the pattern starts with "- " (a minus followed by a space)
1758 -  then it is always considered an exclude pattern, even if specified as
1759 -  part of an include option. The prefix is discarded before matching.
1760 -
1761 -  it() if the pattern is a single exclamation mark ! then the current
1762 -  include/exclude list is reset, removing all previously defined patterns.
1763 -)
1764 -
1765 -The +/- rules are most useful in a list that was read from a file, allowing
1766 -you to have a single exclude list that contains both include and exclude
1767 -options in the proper order.
1768 -
1769 -Remember that the matching occurs at every step in the traversal of the
1770 -directory hierarchy, so you must be sure that all the parent directories of
1771 -the files you want to include are not excluded.  This is particularly
1772 -important when using a trailing '*' rule.  For instance, this won't work:
1773 +However, if you want to do a delete on the receiving side AND you want some
1774 +files to be excluded from being deleted, you'll need to be sure that the
1775 +receiving side knows what files to exclude.  The easiest way is to include
1776 +the per-directory merge files in the transfer and use --delete-after,
1777 +because this ensures that the receiving side gets all the same exclude
1778 +rules as the sending side before it tries to delete anything:
1779  
1780  verb(
1781 -    + /some/path/this-file-will-not-be-found
1782 -    + /file-is-included
1783 -    - *
1784 +  rsync -avF --delete-after host:src/dir /dest
1785  )
1786  
1787 -This fails because the parent directory "some" is excluded by the '*' rule,
1788 -so rsync never visits any of the files in the "some" or "some/path"
1789 -directories.  One solution is to ask for all directories in the hierarchy
1790 -to be included by using a single rule: --include='*/' (put it somewhere
1791 -before the --exclude='*' rule).  Another solution is to add specific
1792 -include rules for all the parent dirs that need to be visited.  For
1793 -instance, this set of rules works fine:
1794 +However, if the merge files are not a part of the transfer, you'll need to
1795 +either specify some global exclude rules (i.e. specified on the command
1796 +line), or you'll need to maintain your own per-directory merge files on
1797 +the receiving side.  An example of the first is this (assume that the
1798 +remote .rules files exclude themselves):
1799  
1800  verb(
1801 -    + /some/
1802 -    + /some/path/
1803 -    + /some/path/this-file-is-found
1804 -    + /file-also-included
1805 -    - *
1806 +  rsync -av --filter=': .rules' --filter='. /my/extra.rules'
1807 +    --delete host:src/dir /dest
1808  )
1809  
1810 -Here are some examples of exclude/include matching:
1811 +In the above example the extra.rules file can affect both sides of the
1812 +transfer, but (on the sending side) the rules are subservient to the rules
1813 +merged from the .rules files because they were specified after the
1814 +per-directory merge rule.
1815 +
1816 +In one final example, the remote side is excluding the .rsync-filter
1817 +files from the transfer, but we want to use our own .rsync-filter files
1818 +to control what gets deleted on the receiving side.  To do this we must
1819 +specifically exclude the per-directory merge files (so that they don't get
1820 +deleted) and then put rules into the local files to control what else
1821 +should not get deleted.  Like one of these commands:
1822  
1823 -itemize(
1824 -  it() --exclude "*.o" would exclude all filenames matching *.o
1825 -  it() --exclude "/foo" would exclude a file called foo in the transfer-root directory
1826 -  it() --exclude "foo/" would exclude any directory called foo
1827 -  it() --exclude "/foo/*/bar" would exclude any file called bar two
1828 -  levels below a directory called foo in the transfer-root directory
1829 -  it() --exclude "/foo/**/bar" would exclude any file called bar two
1830 -  or more levels below a directory called foo in the transfer-root directory
1831 -  it() --include "*/" --include "*.c" --exclude "*" would include all 
1832 -  directories and C source files
1833 -  it() --include "foo/" --include "foo/bar.c" --exclude "*" would include
1834 -  only foo/bar.c (the foo/ directory must be explicitly included or
1835 -  it would be excluded by the "*")
1836 +verb(
1837 +  rsync -av --filter=':e /.rsync-filter' --delete host:src/dir /dest
1838 +  rsync -avFF --delete host:src/dir /dest
1839  )
1840  
1841  manpagesection(BATCH MODE)
1842 @@ -1457,7 +1724,7 @@ it. The most common cause is incorrectly
1843  scripts (such as .cshrc or .profile) that contain output statements
1844  for non-interactive logins.
1845  
1846 -If you are having trouble debugging include and exclude patterns, then
1847 +If you are having trouble debugging filter patterns, then
1848  try specifying the -vv option.  At this level of verbosity rsync will
1849  show why each individual file is included or excluded.
1850  
1851 --- orig/rsyncd.conf.yo 2005-01-01 21:11:01
1852 +++ rsyncd.conf.yo      2005-01-17 06:49:39
1853 @@ -208,6 +208,16 @@ file transfers to and from that module s
1854  was run as root. This complements the "uid" option. The default is gid -2,
1855  which is normally the group "nobody".
1856  
1857 +dit(bf(filter)) The "filter" option allows you to specify a space-separated
1858 +list of filter rules that the server will not allow to be read or written.
1859 +This is only superficially equivalent to the client specifying these
1860 +patterns with the --filter option.  Only one "filter" option may be
1861 +specified, but it may contain as many rules as you like, including
1862 +merge-file rules.  Note that per-directory merge-file rules do not provide
1863 +as much protection as global rules, but they can be used to make --delete
1864 +work better when a client downloads the server's files (if the per-dir
1865 +merge files are included in the transfer).
1866 +
1867  dit(bf(exclude)) The "exclude" option allows you to specify a
1868  space-separated list of patterns that the server will not allow to be read
1869  or written.  This is only superficially equivalent to the client
1870 --- orig/testsuite/exclude.test 2004-05-29 21:25:45
1871 +++ testsuite/exclude.test      2005-01-17 06:19:10
1872 @@ -23,19 +23,50 @@ export HOME CVSIGNORE
1873  makepath "$fromdir/foo/down/to/you"
1874  makepath "$fromdir/bar/down/to/foo/too"
1875  makepath "$fromdir/mid/for/foo/and/that/is/who"
1876 +cat >"$fromdir/.excl" <<EOF
1877 +- down
1878 +: .excl-temp
1879 +!
1880 +- .excl
1881 +- *.bak
1882 +- *.old
1883 +- *.junk
1884 +EOF
1885  echo kept >"$fromdir/foo/file1"
1886  echo removed >"$fromdir/foo/file2"
1887  echo cvsout >"$fromdir/foo/file2.old"
1888 +cat >"$fromdir/foo/.excl" <<EOF
1889 ++ .excl
1890 +- file1
1891 +EOF
1892 +cat >"$fromdir/bar/.excl" <<EOF
1893 +- home-cvs-exclude
1894 +: .excl2
1895 ++ to
1896 +EOF
1897  echo cvsout >"$fromdir/bar/down/to/home-cvs-exclude"
1898 +cat >"$fromdir/bar/down/to/.excl2" <<EOF
1899 +- .excl2
1900 +EOF
1901  echo keeper >"$fromdir/bar/down/to/foo/file1"
1902  echo cvsout >"$fromdir/bar/down/to/foo/file1.bak"
1903  echo gone >"$fromdir/bar/down/to/foo/file3"
1904  echo lost >"$fromdir/bar/down/to/foo/file4"
1905  echo cvsout >"$fromdir/bar/down/to/foo/file4.junk"
1906  echo smashed >"$fromdir/bar/down/to/foo/to"
1907 +cat >"$fromdir/bar/down/to/foo/.excl2" <<EOF
1908 ++ *.junk
1909 +EOF
1910 +# This one should be ineffectual
1911 +cat >"$fromdir/mid/.excl2" <<EOF
1912 +- extra
1913 +EOF
1914  echo cvsout >"$fromdir/mid/one-in-one-out"
1915  echo one-in-one-out >"$fromdir/mid/.cvsignore"
1916  echo cvsin >"$fromdir/mid/one-for-all"
1917 +cat >"$fromdir/mid/.excl" <<EOF
1918 +:C
1919 +EOF
1920  echo cvsin >"$fromdir/mid/for/one-in-one-out"
1921  echo expunged >"$fromdir/mid/for/foo/extra"
1922  echo retained >"$fromdir/mid/for/foo/keep"
1923 @@ -45,6 +76,7 @@ ln -s too "$fromdir/bar/down/to/foo/sym"
1924  
1925  excl="$scratchdir/exclude-from"
1926  cat >"$excl" <<EOF
1927 +!
1928  # If the second line of these two lines does anything, it's a bug.
1929  + **/bar
1930  - /bar
1931 @@ -82,7 +114,8 @@ $RSYNC -av --existing --include='*/' --e
1932  
1933  # Now, test if rsync excludes the same files.
1934  
1935 -checkit "$RSYNC -avv --exclude-from=\"$excl\" \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
1936 +checkit "$RSYNC -avv --exclude-from=\"$excl\" \
1937 +    --delete \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
1938  
1939  # Modify the chk dir by removing cvs-ignored files and then tweaking the dir times.
1940  
1941 @@ -97,7 +130,26 @@ $RSYNC -av --existing --include='*/' --e
1942  # Now, test if rsync excludes the same files, this time with --cvs-exclude
1943  # and --delete-excluded.
1944  
1945 -checkit "$RSYNC -avvC --exclude-from=\"$excl\" \
1946 +checkit "$RSYNC -avvC --filter=\". $excl\" \
1947 +    --delete-excluded \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
1948 +
1949 +# Modify the chk dir for our merge-exclude test and then tweak the dir times.
1950 +
1951 +rm "$chkdir"/.excl
1952 +rm "$chkdir"/foo/file1
1953 +rm "$chkdir"/bar/.excl
1954 +rm "$chkdir"/bar/down/to/.excl2
1955 +rm "$chkdir"/bar/down/to/foo/.excl2
1956 +rm "$chkdir"/mid/.excl
1957 +cp -p "$fromdir"/bar/down/to/foo/*.junk "$chkdir"/bar/down/to/foo
1958 +cp -p "$fromdir"/bar/down/to/foo/to "$chkdir"/bar/down/to/foo
1959 +
1960 +$RSYNC -av --existing --include='*/' --exclude='*' "$fromdir/" "$chkdir/"
1961 +
1962 +# Now, test if rsync excludes the same files, this time with a merge-exclude
1963 +# file.
1964 +
1965 +checkit "sed '/!/d' \"$excl\" | $RSYNC -avv -f :_.excl -f ._- \
1966      --delete-excluded \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir"
1967  
1968  # The script would have aborted on error, so getting here means we've won.