1 This patch adds the ability to use ". INSERT" files in excludes and
2 includes. If you specify a name without slashes, that filename will be
3 looked for in every directory and its rules will effect that directory
4 and its subdirectories. Insert rules found inside a per-directory
5 include file are always just read in (they don't create any new per-dir
6 include/exclude files).
10 rsync -av --exclude='. .excl' from/ to
12 The above will look for a file named ".excl" in every directory of the
13 sender and will exclude (by default) files based on the rules found
14 therein. If a file contains this:
20 Then the file "another.file" will also be read (from that one dir) and
21 its rules inserted in between the surrounding lines.
23 Additionally, you can affect where the -C option's inclusion of the
24 .cvsignore file gets inserted into your rules by mentioning it as an
25 insertion. For instance, specifying this:
27 rsync -avC --include=foo --exclude='. .cvsignore' --include='*.c' a/ b
29 This will insert all the .cvsignore rules in the middle of your rules
30 rather than at the end. This allows their dir-specific rules to
31 supersede your general rules instead of being subservient to them.
35 --- exclude.c 22 Apr 2004 22:17:15 -0000 1.71
36 +++ exclude.c 22 Apr 2004 23:45:51 -0000
37 @@ -30,31 +30,60 @@ extern int verbose;
42 +extern int sanitize_paths;
44 extern char curr_dir[];
46 -struct exclude_list_struct exclude_list = { 0, 0, "" };
47 -struct exclude_list_struct local_exclude_list = { 0, 0, "local-cvsignore " };
48 -struct exclude_list_struct server_exclude_list = { 0, 0, "server " };
49 +struct exclude_list_struct exclude_list = { 0, 0, 0, 0, "" };
50 +struct exclude_list_struct server_exclude_list = { 0, 0, 0, 0, "server " };
51 char *exclude_path_prefix = NULL;
53 +static struct exclude_list_struct *local_exclude_lists;
54 +static int local_exclude_list_cnt;
55 +static char dirbuf[MAXPATHLEN];
56 +static unsigned int dirbuf_offset = 0;
58 +static void clear_exclude_list(struct exclude_list_struct *listp,
59 + struct exclude_struct *extra)
61 + listp->head = listp->extra = extra;
65 /** Build an exclude structure given a exclude pattern */
66 static void make_exclude(struct exclude_list_struct *listp, const char *pattern,
67 - int pat_len, int include)
68 + unsigned int pat_len, int mflags)
70 struct exclude_struct *ret;
73 + unsigned int ex_len;
75 + if (mflags & MATCHFLG_INSERT_FILE) {
76 + struct exclude_struct *ex;
77 + /* If the local include file was already mentioned, don't
78 + * insert it again. */
79 + for (ex = listp->head; ex; ex = ex->next) {
80 + if ((ex->match_flags & MATCHFLG_INSERT_FILE)
81 + && strlen(ex->pattern) == pat_len
82 + && strncmp(ex->pattern, pattern, pat_len) == 0)
85 + if (pat_len == 10 && strncmp(pattern, ".cvsignore", 10) == 0) {
86 + mflags |= MATCHFLG_CVSIGNORE;
87 + mflags &= ~MATCHFLG_INCLUDE;
89 + mflags &= ~MATCHFLG_CVSIGNORE;
92 ret = new(struct exclude_struct);
94 out_of_memory("make_exclude");
96 memset(ret, 0, sizeof ret[0]);
97 - ret->include = include;
99 if (exclude_path_prefix)
100 - ret->match_flags |= MATCHFLG_ABS_PATH;
101 + mflags |= MATCHFLG_ABS_PATH;
102 if (exclude_path_prefix && *pattern == '/')
103 ex_len = strlen(exclude_path_prefix);
105 @@ -68,29 +97,49 @@ static void make_exclude(struct exclude_
108 if (strpbrk(ret->pattern, "*[?")) {
109 - ret->match_flags |= MATCHFLG_WILD;
110 + mflags |= MATCHFLG_WILD;
111 if ((cp = strstr(ret->pattern, "**")) != NULL) {
112 - ret->match_flags |= MATCHFLG_WILD2;
113 + mflags |= MATCHFLG_WILD2;
114 /* If the pattern starts with **, note that. */
115 if (cp == ret->pattern)
116 - ret->match_flags |= MATCHFLG_WILD2_PREFIX;
117 + mflags |= MATCHFLG_WILD2_PREFIX;
121 if (pat_len > 1 && ret->pattern[pat_len-1] == '/') {
122 ret->pattern[pat_len-1] = 0;
123 - ret->directory = 1;
124 + mflags |= MATCHFLG_DIRECTORY;
127 for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++)
130 + ret->next = listp->extra;
133 listp->head = listp->tail = ret;
135 listp->tail->next = ret;
139 + if (mflags & MATCHFLG_INSERT_FILE) {
140 + struct exclude_list_struct *lp;
141 + int ndx = local_exclude_list_cnt++;
142 + local_exclude_lists = realloc_array(local_exclude_lists,
143 + struct exclude_list_struct, local_exclude_list_cnt);
144 + if (!local_exclude_lists)
145 + out_of_memory("make_exclude");
146 + lp = &local_exclude_lists[ndx];
147 + clear_exclude_list(lp, NULL);
148 + if (asprintf(&lp->debug_type, "local %s ",
150 + out_of_memory("make_exclude");
152 + ret->slash_cnt = ndx;
155 + ret->match_flags = mflags;
158 static void free_exclude(struct exclude_struct *ex)
159 @@ -99,7 +148,7 @@ static void free_exclude(struct exclude_
163 -void free_exclude_list(struct exclude_list_struct *listp)
164 +static void free_exclude_list(struct exclude_list_struct *listp)
166 struct exclude_struct *ent, *next;
168 @@ -108,12 +157,72 @@ void free_exclude_list(struct exclude_li
169 who_am_i(), listp->debug_type);
172 + if (listp->extra) {
174 + listp->tail->next = NULL;
176 + listp->head = NULL;
179 for (ent = listp->head; ent; ent = next) {
184 - listp->head = listp->tail = NULL;
185 + clear_exclude_list(listp, NULL);
188 +void *push_local_excludes(char *fname, unsigned int offset)
191 + struct exclude_list_struct *mem = new_array(struct exclude_list_struct,
192 + local_exclude_list_cnt);
194 + out_of_memory("push_local_excludes");
196 + memcpy(mem, local_exclude_lists,
197 + sizeof (struct exclude_list_struct) * local_exclude_list_cnt);
199 + memcpy(dirbuf, fname, offset);
200 + dirbuf_offset = offset;
202 + for (i = 0; i < local_exclude_list_cnt; i++) {
203 + struct exclude_list_struct *listp = &local_exclude_lists[i];
204 + struct exclude_struct *extra;
205 + char *file = listp->parent->pattern;
207 + if (listp->parent->match_flags & MATCHFLG_CVSIGNORE) {
208 + flags = XFLG_WORD_SPLIT | XFLG_NO_PREFIXES;
212 + extra = listp->head; /* subdirs inherit our rules */
214 + clear_exclude_list(listp, extra);
215 + if (strlcpy(fname + offset, file, MAXPATHLEN - offset)
216 + < MAXPATHLEN - offset) {
217 + add_exclude_file(listp, fname, flags);
219 + io_error |= IOERR_GENERAL;
221 + "cannot add local excludes in long-named directory %s\n",
222 + full_fname(fname));
226 + return (void *) mem;
229 +void pop_local_excludes(void *mem)
232 + for (i = 0; i < local_exclude_list_cnt; i++) {
233 + struct exclude_list_struct *listp = &local_exclude_lists[i];
234 + free_exclude_list(listp);
236 + memcpy(local_exclude_lists, mem,
237 + sizeof (struct exclude_list_struct) * local_exclude_list_cnt);
241 static int check_one_exclude(char *name, struct exclude_struct *ex,
242 @@ -139,7 +248,8 @@ static int check_one_exclude(char *name,
244 if (!name[0]) return 0;
246 - if (ex->directory && !name_is_dir) return 0;
247 + if ((ex->match_flags & MATCHFLG_DIRECTORY) && !name_is_dir)
250 if (*pattern == '/') {
252 @@ -206,9 +316,11 @@ static void report_exclude_result(char c
255 rprintf(FINFO, "[%s] %scluding %s %s because of %spattern %s%s\n",
256 - who_am_i(), ent->include ? "in" : "ex",
258 + ent->match_flags & MATCHFLG_INCLUDE ? "in" : "ex",
259 name_is_dir ? "directory" : "file", name, type,
260 - ent->pattern, ent->directory ? "/" : "");
262 + ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "");
266 @@ -223,10 +335,18 @@ int check_exclude(struct exclude_list_st
267 struct exclude_struct *ent;
269 for (ent = listp->head; ent; ent = ent->next) {
270 + if (ent->match_flags & MATCHFLG_INSERT_FILE) {
271 + struct exclude_list_struct *lp
272 + = &local_exclude_lists[ent->slash_cnt];
273 + int rc = check_exclude(lp, name, name_is_dir);
278 if (check_one_exclude(name, ent, name_is_dir)) {
279 report_exclude_result(name, ent, name_is_dir,
281 - return ent->include ? 1 : -1;
282 + return (ent->match_flags & MATCHFLG_INCLUDE) ? 1 : -1;
286 @@ -242,11 +362,11 @@ int check_exclude(struct exclude_list_st
287 * *incl_ptr value will be 1 for an include, 0 for an exclude, and -1 for
288 * the list-clearing "!" token.
290 -static const char *get_exclude_tok(const char *p, int *len_ptr, int *incl_ptr,
291 +static const char *get_exclude_tok(const char *p, int *len_ptr, int *flag_ptr,
294 const unsigned char *s = (const unsigned char *)p;
296 + int len, mflags = 0;
298 if (xflags & XFLG_WORD_SPLIT) {
299 /* Skip over any initial whitespace. */
300 @@ -256,13 +376,19 @@ static const char *get_exclude_tok(const
304 - /* Is this a '+' or '-' followed by a space (not whitespace)? */
305 + /* Is this a +/-/. followed by a space (not whitespace)? */
306 if (!(xflags & XFLG_NO_PREFIXES)
307 - && (*s == '-' || *s == '+') && s[1] == ' ') {
308 - *incl_ptr = *s == '+';
309 + && (*s == '-' || *s == '+' || *s == '.') && s[1] == ' ') {
311 + mflags |= MATCHFLG_INCLUDE;
312 + else if (*s == '.') {
313 + mflags |= MATCHFLG_INSERT_FILE;
314 + if (xflags & XFLG_DEF_INCLUDE)
315 + mflags |= MATCHFLG_INCLUDE;
319 - *incl_ptr = xflags & XFLG_DEF_INCLUDE;
320 + } else if (xflags & XFLG_DEF_INCLUDE)
321 + mflags |= MATCHFLG_INCLUDE;
323 if (xflags & XFLG_WORD_SPLIT) {
324 const unsigned char *cp = s;
325 @@ -274,9 +400,10 @@ static const char *get_exclude_tok(const
328 if (*p == '!' && len == 1 && !(xflags & XFLG_NO_PREFIXES))
330 + mflags |= MATCHFLG_CLEAR_LIST;
333 + *flag_ptr = mflags;
334 return (const char *)s;
337 @@ -284,7 +411,7 @@ static const char *get_exclude_tok(const
338 void add_exclude(struct exclude_list_struct *listp, const char *pattern,
342 + int pat_len, mflags;
346 @@ -293,22 +420,44 @@ void add_exclude(struct exclude_list_str
350 - cp = get_exclude_tok(cp + pat_len, &pat_len, &incl, xflags);
351 + cp = get_exclude_tok(cp + pat_len, &pat_len, &mflags, xflags);
354 /* If we got the special "!" token, clear the list. */
356 + if (mflags & MATCHFLG_CLEAR_LIST) {
357 free_exclude_list(listp);
359 - make_exclude(listp, cp, pat_len, incl);
362 - rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s)\n",
363 - who_am_i(), pat_len, cp,
365 - incl ? "include" : "exclude");
368 + if (mflags & MATCHFLG_INSERT_FILE) {
369 + char name[MAXPATHLEN];
370 + if ((unsigned) pat_len >= sizeof name)
371 + continue; // XXX complain?
372 + strlcpy(name, cp, pat_len+1);
373 + if (listp->parent || strchr(name, '/') != NULL) {
374 + if (sanitize_paths)
375 + sanitize_path(name, curr_dir);
379 + if (strlcpy(dirbuf + dirbuf_offset,
380 + name, MAXPATHLEN - dirbuf_offset)
381 + >= MAXPATHLEN - dirbuf_offset)
382 + continue; // XXX complain?
385 + add_exclude_file(listp, cp,
386 + xflags | XFLG_FATAL_ERRORS);
390 + make_exclude(listp, cp, pat_len, mflags);
393 + rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s%sclude)\n",
394 + who_am_i(), pat_len, cp, listp->debug_type,
395 + mflags & MATCHFLG_INSERT_FILE ? "FILE " : "",
396 + mflags & MATCHFLG_INCLUDE ? "in" : "ex");
401 @@ -384,15 +533,19 @@ void send_exclude_list(int f)
402 l = strlcpy(p, ent->pattern, sizeof p);
403 if (l == 0 || l >= MAXPATHLEN)
405 - if (ent->directory) {
406 + if (ent->match_flags & MATCHFLG_DIRECTORY) {
411 - if (ent->include) {
412 + if (ent->match_flags & MATCHFLG_INCLUDE) {
414 write_buf(f, "+ ", 2);
415 - } else if ((*p == '-' || *p == '+') && p[1] == ' ') {
416 + } else if (ent->match_flags & MATCHFLG_INSERT_FILE) {
417 + write_int(f, l + 2);
418 + write_buf(f, ". ", 2);
419 + } else if ((*p == '-' || *p == '+' || *p == '.')
422 write_buf(f, "- ", 2);
424 @@ -433,6 +586,7 @@ void add_cvs_excludes(void)
425 char fname[MAXPATHLEN];
428 + add_exclude(&exclude_list, ". .cvsignore", 0);
429 add_exclude(&exclude_list, default_cvsignore,
430 XFLG_WORD_SPLIT | XFLG_NO_PREFIXES);
432 --- flist.c 22 Apr 2004 22:17:15 -0000 1.216
433 +++ flist.c 22 Apr 2004 23:45:51 -0000
434 @@ -39,8 +39,6 @@ extern int module_id;
435 extern int ignore_errors;
436 extern int numeric_ids;
438 -extern int cvs_exclude;
441 extern char curr_dir[MAXPATHLEN];
442 extern char *files_from;
443 @@ -66,7 +64,6 @@ extern int write_batch;
445 extern struct exclude_list_struct exclude_list;
446 extern struct exclude_list_struct server_exclude_list;
447 -extern struct exclude_list_struct local_exclude_list;
451 @@ -211,8 +208,6 @@ int link_stat(const char *path, STRUCT_S
453 static int check_exclude_file(char *fname, int is_dir, int exclude_level)
457 #if 0 /* This currently never happens, so avoid a useless compare. */
458 if (exclude_level == NO_EXCLUDES)
460 @@ -234,10 +229,7 @@ static int check_exclude_file(char *fnam
461 if (exclude_level != ALL_EXCLUDES)
463 if (exclude_list.head
464 - && (rc = check_exclude(&exclude_list, fname, is_dir)) != 0)
466 - if (local_exclude_list.head
467 - && check_exclude(&local_exclude_list, fname, is_dir) < 0)
468 + && check_exclude(&exclude_list, fname, is_dir) < 0)
472 @@ -946,11 +938,7 @@ void send_file_name(int f, struct file_l
474 if (recursive && S_ISDIR(file->mode)
475 && !(file->flags & FLAG_MOUNT_POINT)) {
476 - struct exclude_list_struct last_list = local_exclude_list;
477 - local_exclude_list.head = local_exclude_list.tail = NULL;
478 send_directory(f, flist, f_name_to(file, fbuf));
479 - free_exclude_list(&local_exclude_list);
480 - local_exclude_list = last_list;
484 @@ -961,6 +949,7 @@ static void send_directory(int f, struct
486 char fname[MAXPATHLEN];
488 + void *save_excludes;
492 @@ -985,18 +974,7 @@ static void send_directory(int f, struct
497 - if (strlcpy(p, ".cvsignore", MAXPATHLEN - offset)
498 - < MAXPATHLEN - offset) {
499 - add_exclude_file(&local_exclude_list, fname,
500 - XFLG_WORD_SPLIT | XFLG_NO_PREFIXES);
502 - io_error |= IOERR_GENERAL;
504 - "cannot cvs-exclude in long-named directory %s\n",
505 - full_fname(fname));
508 + save_excludes = push_local_excludes(fname, offset);
510 for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
511 char *dname = d_name(di);
512 @@ -1017,6 +995,8 @@ static void send_directory(int f, struct
513 rprintf(FERROR, "readdir(%s): (%d) %s\n",
514 dir, errno, strerror(errno));
517 + pop_local_excludes(save_excludes);
521 --- proto.h 22 Apr 2004 09:58:09 -0000 1.189
522 +++ proto.h 22 Apr 2004 23:45:51 -0000
523 @@ -51,7 +51,8 @@ int start_daemon(int f_in, int f_out);
524 int daemon_main(void);
525 void setup_protocol(int f_out,int f_in);
526 int claim_connection(char *fname,int max_connections);
527 -void free_exclude_list(struct exclude_list_struct *listp);
528 +void *push_local_excludes(char *fname, unsigned int offset);
529 +void pop_local_excludes(void *mem);
530 int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir);
531 void add_exclude(struct exclude_list_struct *listp, const char *pattern,
533 --- rsync.h 22 Apr 2004 09:58:24 -0000 1.198
534 +++ rsync.h 22 Apr 2004 23:45:52 -0000
535 @@ -490,18 +490,21 @@ struct map_struct {
536 #define MATCHFLG_WILD2 (1<<1) /* pattern has '**' */
537 #define MATCHFLG_WILD2_PREFIX (1<<2) /* pattern starts with '**' */
538 #define MATCHFLG_ABS_PATH (1<<3) /* path-match on absolute path */
539 +#define MATCHFLG_INCLUDE (1<<4) /* this is an include, not an exclude */
540 +#define MATCHFLG_CLEAR_LIST (1<<5) /* this item is the "!" token */
541 +#define MATCHFLG_DIRECTORY (1<<6) /* this matches only directories */
542 +#define MATCHFLG_INSERT_FILE (1<<7) /* specifies a file to insert */
543 +#define MATCHFLG_CVSIGNORE (1<<8) /* parse this as a .cvsignore file */
544 struct exclude_struct {
545 struct exclude_struct *next;
553 struct exclude_list_struct {
554 - struct exclude_struct *head;
555 - struct exclude_struct *tail;
556 + struct exclude_struct *head, *tail;
557 + struct exclude_struct *extra, *parent;