A few more improvements.
[rsync/rsync-patches.git] / perdir-exclude-from.diff
1 --- exclude.c   22 Apr 2004 22:17:15 -0000      1.71
2 +++ exclude.c   22 Apr 2004 10:32:52 -0000
3 @@ -27,15 +27,17 @@
4  #include "rsync.h"
5  
6  extern int verbose;
7 +extern int protocol_version;
8  extern int eol_nulls;
9  extern int list_only;
10  extern int recurse;
11  
12  extern char curr_dir[];
13  
14 -struct exclude_list_struct exclude_list = { 0, 0, "" };
15 -struct exclude_list_struct local_exclude_list = { 0, 0, "local-cvsignore " };
16 -struct exclude_list_struct server_exclude_list = { 0, 0, "server " };
17 +struct exclude_list_struct exclude_list = { 0, 0, 0, "" };
18 +struct exclude_list_struct perdir_exclude_list = { 0, 0, 0, "perdir " };
19 +struct exclude_list_struct local_exclude_list = { 0, 0, 0, "local-cvsignore " };
20 +struct exclude_list_struct server_exclude_list = { 0, 0, 0, "server " };
21  char *exclude_path_prefix = NULL;
22  
23  /** Build an exclude structure given a exclude pattern */
24 @@ -85,6 +87,8 @@ static void make_exclude(struct exclude_
25         for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++)
26                 ret->slash_cnt++;
27  
28 +       ret->next = listp->extra;
29 +
30         if (!listp->tail)
31                 listp->head = listp->tail = ret;
32         else {
33 @@ -108,12 +112,26 @@ void free_exclude_list(struct exclude_li
34                         who_am_i(), listp->debug_type);
35         }
36  
37 +       if (listp->extra) {
38 +               if (listp->tail)
39 +                       listp->tail->next = NULL;
40 +               else
41 +                       listp->head = NULL;
42 +       }
43 +
44         for (ent = listp->head; ent; ent = next) {
45                 next = ent->next;
46                 free_exclude(ent);
47         }
48  
49 -       listp->head = listp->tail = NULL;
50 +       clear_exclude_list(listp, NULL);
51 +}
52 +
53 +void clear_exclude_list(struct exclude_list_struct *listp,
54 +                       struct exclude_struct *extra)
55 +{
56 +       listp->head = listp->extra = extra;
57 +       listp->tail = NULL;
58  }
59  
60  static int check_one_exclude(char *name, struct exclude_struct *ex,
61 @@ -215,22 +233,25 @@ static void report_exclude_result(char c
62  
63  /*
64   * Return true if file NAME is defined to be excluded by the specified
65 - * exclude list.  Returns -1 for an exclude, 1 for an include, and 0 if
66 - * no match.
67 + * exclude list.  Returns -1 for an exclude, 0 for a normal include, or 1
68 + * for a super include.
69   */
70 -int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir)
71 +int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir,
72 +                 int current_exclusion)
73  {
74         struct exclude_struct *ent;
75  
76         for (ent = listp->head; ent; ent = ent->next) {
77 +               if (current_exclusion < 0 && ent->include < 2)
78 +                       continue;
79                 if (check_one_exclude(name, ent, name_is_dir)) {
80                         report_exclude_result(name, ent, name_is_dir,
81                                               listp->debug_type);
82 -                       return ent->include ? 1 : -1;
83 +                       return ent->include - 1;
84                 }
85         }
86  
87 -       return 0;
88 +       return current_exclusion;
89  }
90  
91  
92 @@ -256,10 +277,10 @@ static const char *get_exclude_tok(const
93                 p = (const char *)s;
94         }
95  
96 -       /* Is this a '+' or '-' followed by a space (not whitespace)? */
97 +       /* Is this a +/-/& followed by a space (not whitespace)? */
98         if (!(xflags & XFLG_NO_PREFIXES)
99 -           && (*s == '-' || *s == '+') && s[1] == ' ') {
100 -               *incl_ptr = *s == '+';
101 +           && (*s == '-' || *s == '+' || *s == '&') && s[1] == ' ') {
102 +               *incl_ptr = *s == '+' ? 1 : *s == '&' ? 2 : 0;
103                 s += 2;
104         } else
105                 *incl_ptr = xflags & XFLG_DEF_INCLUDE;
106 @@ -391,8 +412,12 @@ void send_exclude_list(int f)
107  
108                 if (ent->include) {
109                         write_int(f, l + 2);
110 -                       write_buf(f, "+ ", 2);
111 -               } else if ((*p == '-' || *p == '+') && p[1] == ' ') {
112 +                       if (ent->include > 1 && protocol_version >= 28)
113 +                               write_buf(f, "& ", 2);
114 +                       else
115 +                               write_buf(f, "+ ", 2);
116 +               } else if ((*p == '-' || *p == '+' || *p == '&')
117 +                   && p[1] == ' ') {
118                         write_int(f, l + 2);
119                         write_buf(f, "- ", 2);
120                 } else
121 --- flist.c     22 Apr 2004 22:17:15 -0000      1.216
122 +++ flist.c     22 Apr 2004 10:32:52 -0000
123 @@ -40,6 +40,7 @@ extern int ignore_errors;
124  extern int numeric_ids;
125  
126  extern int cvs_exclude;
127 +extern const char *perdir_exclude_from;
128  
129  extern int recurse;
130  extern char curr_dir[MAXPATHLEN];
131 @@ -66,6 +67,7 @@ extern int write_batch;
132  
133  extern struct exclude_list_struct exclude_list;
134  extern struct exclude_list_struct server_exclude_list;
135 +extern struct exclude_list_struct perdir_exclude_list;
136  extern struct exclude_list_struct local_exclude_list;
137  
138  int io_error;
139 @@ -211,7 +213,9 @@ int link_stat(const char *path, STRUCT_S
140   */
141  static int check_exclude_file(char *fname, int is_dir, int exclude_level)
142  {
143 -       int rc;
144 +       static struct exclude_list_struct *elist[] = {
145 +           &exclude_list, &perdir_exclude_list, &local_exclude_list, NULL };
146 +       int i, rc;
147  
148  #if 0 /* This currently never happens, so avoid a useless compare. */
149         if (exclude_level == NO_EXCLUDES)
150 @@ -229,17 +233,18 @@ static int check_exclude_file(char *fnam
151                 }
152         }
153         if (server_exclude_list.head
154 -           && check_exclude(&server_exclude_list, fname, is_dir) < 0)
155 +           && check_exclude(&server_exclude_list, fname, is_dir, 0) < 0)
156                 return 1;
157         if (exclude_level != ALL_EXCLUDES)
158                 return 0;
159 -       if (exclude_list.head
160 -           && (rc = check_exclude(&exclude_list, fname, is_dir)) != 0)
161 -               return rc < 0;
162 -       if (local_exclude_list.head
163 -           && check_exclude(&local_exclude_list, fname, is_dir) < 0)
164 -               return 1;
165 -       return 0;
166 +       for (i = 0, rc = 0; elist[i]; i++) {
167 +               if (!elist[i]->head)
168 +                       continue;
169 +               rc = check_exclude(elist[i], fname, is_dir, rc);
170 +               if (rc > 0)
171 +                       return 0;
172 +       }
173 +       return rc < 0;
174  }
175  
176  /* used by the one_file_system code */
177 @@ -947,8 +952,12 @@ void send_file_name(int f, struct file_l
178         if (recursive && S_ISDIR(file->mode)
179             && !(file->flags & FLAG_MOUNT_POINT)) {
180                 struct exclude_list_struct last_list = local_exclude_list;
181 -               local_exclude_list.head = local_exclude_list.tail = NULL;
182 +               struct exclude_list_struct sub_list = perdir_exclude_list;
183 +               clear_exclude_list(&local_exclude_list, NULL);
184 +               clear_exclude_list(&perdir_exclude_list, sub_list.head);
185                 send_directory(f, flist, f_name_to(file, fbuf));
186 +               free_exclude_list(&perdir_exclude_list);
187 +               perdir_exclude_list = sub_list;
188                 free_exclude_list(&local_exclude_list);
189                 local_exclude_list = last_list;
190         }
191 @@ -994,6 +1003,18 @@ static void send_directory(int f, struct
192                         io_error |= IOERR_GENERAL;
193                         rprintf(FINFO,
194                                 "cannot cvs-exclude in long-named directory %s\n",
195 +                               full_fname(fname));
196 +               }
197 +       }
198 +
199 +       if (perdir_exclude_from) {
200 +               if (strlcpy(p, perdir_exclude_from, MAXPATHLEN - offset)
201 +                   < MAXPATHLEN - offset)
202 +                       add_exclude_file(&perdir_exclude_list, fname, 0);
203 +               else {
204 +                       io_error |= IOERR_GENERAL;
205 +                       rprintf(FINFO,
206 +                               "cannot perdir-exclude in long-named directory %s\n",
207                                 full_fname(fname));
208                 }
209         }
210 --- options.c   17 Apr 2004 17:07:23 -0000      1.147
211 +++ options.c   22 Apr 2004 10:32:53 -0000
212 @@ -70,6 +70,7 @@ int am_server = 0;
213  int am_sender = 0;
214  int am_generator = 0;
215  char *files_from = NULL;
216 +char *perdir_exclude_from = NULL;
217  int filesfrom_fd = -1;
218  char *remote_filesfrom_file = NULL;
219  int eol_nulls = 0;
220 @@ -273,6 +274,7 @@ void usage(enum logcode F)
221    rprintf(F,"     --exclude-from=FILE     exclude patterns listed in FILE\n");
222    rprintf(F,"     --include=PATTERN       don't exclude files matching PATTERN\n");
223    rprintf(F,"     --include-from=FILE     don't exclude patterns listed in FILE\n");
224 +  rprintf(F,"     --perdir-exclude-from=F look in each dir for exclude-file F\n");
225    rprintf(F,"     --files-from=FILE       read FILE for list of source-file names\n");
226    rprintf(F," -0  --from0                 all *-from file lists are delimited by nulls\n");
227    rprintf(F,"     --version               print version number\n");
228 @@ -335,6 +337,7 @@ static struct poptOption long_options[] 
229    {"dry-run",         'n', POPT_ARG_NONE,   &dry_run, 0, 0, 0 },
230    {"sparse",          'S', POPT_ARG_NONE,   &sparse_files, 0, 0, 0 },
231    {"cvs-exclude",     'C', POPT_ARG_NONE,   &cvs_exclude, 0, 0, 0 },
232 +  {"perdir-exclude-from",0,POPT_ARG_STRING, &perdir_exclude_from, 0, 0, 0 },
233    {"update",          'u', POPT_ARG_NONE,   &update_only, 0, 0, 0 },
234    {"links",           'l', POPT_ARG_NONE,   &preserve_links, 0, 0, 0 },
235    {"copy-links",      'L', POPT_ARG_NONE,   &copy_links, 0, 0, 0 },
236 --- proto.h     22 Apr 2004 09:58:09 -0000      1.189
237 +++ proto.h     22 Apr 2004 10:32:53 -0000
238 @@ -52,7 +52,10 @@ int daemon_main(void);
239  void setup_protocol(int f_out,int f_in);
240  int claim_connection(char *fname,int max_connections);
241  void free_exclude_list(struct exclude_list_struct *listp);
242 -int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir);
243 +void clear_exclude_list(struct exclude_list_struct *listp,
244 +                       struct exclude_struct *extra);
245 +int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir,
246 +                 int current_exclusion);
247  void add_exclude(struct exclude_list_struct *listp, const char *pattern,
248                  int xflags);
249  void add_exclude_file(struct exclude_list_struct *listp, const char *fname,
250 --- rsync.h     22 Apr 2004 09:58:24 -0000      1.198
251 +++ rsync.h     22 Apr 2004 10:32:53 -0000
252 @@ -502,6 +502,7 @@ struct exclude_struct {
253  struct exclude_list_struct {
254         struct exclude_struct *head;
255         struct exclude_struct *tail;
256 +       struct exclude_struct *extra;
257         char *debug_type;
258  };
259  
260 --- rsync.yo    22 Apr 2004 21:35:45 -0000      1.161
261 +++ rsync.yo    22 Apr 2004 10:32:54 -0000
262 @@ -331,6 +331,7 @@ verb(
263       --exclude-from=FILE     exclude patterns listed in FILE
264       --include=PATTERN       don't exclude files matching PATTERN
265       --include-from=FILE     don't exclude patterns listed in FILE
266 +     --perdir-exclude-from=F look in each dir for exclude file F
267       --files-from=FILE       read FILE for list of source-file names
268   -0  --from0                 all file lists are delimited by nulls
269       --version               print version number
270 @@ -675,6 +676,15 @@ dit(bf(--include-from=FILE)) This specif
271  from a file.
272  If em(FILE) is bf(-) the list will be read from standard input.
273  
274 +dit(bf(--perdir-exclude-from=FILE)) In any given directory, patterns
275 +listed in FILE (one per line) are excluded from the file lists
276 +associated with that directory and all of its subdirectories. Rules
277 +specified in a subdir take precedence over rules evaluated in a parent
278 +dir (think of each file's rules being prefixed to the prior rules).  If
279 +you don't want to inherit the parent-dir rules, start the FILE with a
280 +single exclamation mark ! to clear them out. You may prefix names with
281 +an explicit exclude, include, or super-include prefix (see below).
282 +
283  dit(bf(--files-from=FILE)) Using this option allows you to specify the
284  exact list of files to transfer (as read from the specified FILE or "-"
285  for stdin).  It also tweaks the default behavior of rsync to make
286 @@ -714,7 +724,8 @@ was located on the remote "src" host.
287  
288  dit(bf(-0, --from0)) This tells rsync that the filenames it reads from a
289  file are terminated by a null ('\0') character, not a NL, CR, or CR+LF.
290 -This affects --exclude-from, --include-from, and --files-from.
291 +This affects --exclude-from, --include-from, --perdir-exclude-from,
292 +and --files-from.
293  It does not affect --cvs-exclude (since all names read from a .cvsignore
294  file are split on whitespace).
295  
296 @@ -1001,6 +1012,12 @@ itemize(
297    it() if the pattern starts with "- " (a minus followed by a space)
298    then it is always considered an exclude pattern, even if specified as
299    part of an include option. The prefix is discarded before matching.
300 +
301 +  it() if the pattern starts with "& " (an ampersand followed by a space)
302 +  then it is considered a super-include pattern.  A super-include will
303 +  override an exclude from another exclude list (a normal include only
304 +  overrides an exclusion from further down in the same list).  The
305 +  prefix is discarded before matching.
306  
307    it() if the pattern is a single exclamation mark ! then the current
308    include/exclude list is reset, removing all previously defined patterns.
309 --- t_stub.c    22 Apr 2004 09:58:11 -0000      1.7
310 +++ t_stub.c    22 Apr 2004 10:32:54 -0000
311 @@ -46,7 +46,7 @@ struct exclude_list_struct server_exclud
312  }
313  
314   int check_exclude(UNUSED(struct exclude_list_struct *listp), UNUSED(char *name),
315 -                  UNUSED(int name_is_dir))
316 +                  UNUSED(int name_is_dir), UNUSED(int current_exclusion))
317  {
318         /* This function doesn't really get called in this test context, so
319          * just return 0. */
320 --- util.c      22 Apr 2004 22:17:15 -0000      1.138
321 +++ util.c      22 Apr 2004 10:32:54 -0000
322 @@ -476,7 +476,7 @@ static int exclude_server_path(char *arg
323         if (server_exclude_list.head) {
324                 for (s = arg; (s = strchr(s, '/')) != NULL; ) {
325                         *s = '\0';
326 -                       if (check_exclude(&server_exclude_list, arg, 1) < 0) {
327 +                       if (check_exclude(&server_exclude_list, arg, 1, 0) < 0) {
328                                 /* We must leave arg truncated! */
329                                 return 1;
330                         }