Commit | Line | Data |
---|---|---|
8a529471 WD |
1 | After applying this patch and running configure, you MUST run this |
2 | command before "make": | |
3 | ||
4 | make proto | |
5 | ||
524989ae WD |
6 | This patch adds the ability to merge rules into your excludes/includes |
7 | using a ". FILE" idiom. If you specify a name without slashes, that | |
8 | filename will be looked for in every subdirectory that rsync visits, | |
9 | and its rules will affect the current directory and its subdirectories. | |
a55d21aa WD |
10 | |
11 | For example: | |
12 | ||
13 | rsync -av --exclude='. .excl' from/ to | |
14 | ||
15 | The above will look for a file named ".excl" in every directory of the | |
524989ae WD |
16 | hierarchy that rsync visits, and it will exclude (by default) names |
17 | based on the rules found therein. If one of the .excl files contains | |
18 | this: | |
a55d21aa WD |
19 | |
20 | + *.c | |
524989ae WD |
21 | . .excl2 |
22 | . ./.excl3 | |
23 | *.o | |
a55d21aa | 24 | |
524989ae WD |
25 | Then the file ".excl2" will also be read in the current dir, and all |
26 | subdirs of the current dir. The file ".excl3" would just be read in | |
27 | for the current dir because its name contained a slash. | |
a55d21aa WD |
28 | |
29 | ..wayne.. | |
30 | ||
7cb7ae4e | 31 | --- exclude.c 14 May 2004 21:23:41 -0000 1.76 |
d1e7c1c8 WD |
32 | +++ exclude.c 15 May 2004 07:01:28 -0000 |
33 | @@ -30,32 +30,89 @@ extern int verbose; | |
a55d21aa WD |
34 | extern int eol_nulls; |
35 | extern int list_only; | |
36 | extern int recurse; | |
37 | +extern int io_error; | |
38 | +extern int sanitize_paths; | |
39 | ||
40 | extern char curr_dir[]; | |
41 | ||
42 | -struct exclude_list_struct exclude_list = { 0, 0, "" }; | |
524989ae | 43 | -struct exclude_list_struct local_exclude_list = { 0, 0, "per-dir .cvsignore " }; |
a55d21aa | 44 | -struct exclude_list_struct server_exclude_list = { 0, 0, "server " }; |
7cb7ae4e WD |
45 | +struct exclude_list_struct exclude_list = { 0, 0, 0, "" }; |
46 | +struct exclude_list_struct server_exclude_list = { 0, 0, 0, "server " }; | |
a55d21aa WD |
47 | char *exclude_path_prefix = NULL; |
48 | ||
d1e7c1c8 WD |
49 | -/** Build an exclude structure given a exclude pattern */ |
50 | -static void make_exclude(struct exclude_list_struct *listp, const char *pattern, | |
51 | - int pat_len, int include) | |
524989ae | 52 | +struct exclude_list_root { |
d1e7c1c8 | 53 | + struct exclude_list_struct *array; |
524989ae WD |
54 | + int cnt; |
55 | +} local_lists; | |
56 | + | |
a55d21aa WD |
57 | +static char dirbuf[MAXPATHLEN]; |
58 | +static unsigned int dirbuf_offset = 0; | |
a55d21aa | 59 | + |
d1e7c1c8 WD |
60 | +/* Each exclude_list_struct describes a singly-linked list by keeping track |
61 | + * of both the head and tail pointers. The list is slightly unusual in that | |
62 | + * a parent-dir's content can be appended to the end of the local list in a | |
63 | + * special way: the last item in the local list has its "next" pointer set | |
64 | + * to point to the parent's list, but the local list's tail pointer points | |
65 | + * at the end of the local list. Thus, if the local list is empty, the head | |
66 | + * will be pointing at the parent content but the tail will be NULL. To | |
67 | + * help you visualize this, here are the possible list arrangements: | |
68 | + * | |
69 | + * Completely Empty Local Content Only | |
70 | + * ================================== ==================================== | |
71 | + * head -> NULL head -> Local1 -> Local2 -> NULL | |
72 | + * tail -> NULL tail -------------^ | |
73 | + * | |
74 | + * Inherited Content Only Both Local and Inherited Content | |
75 | + * ================================== ==================================== | |
76 | + * head -> Parent1 -> Parent2 -> NULL head -> L1 -> L2 -> P1 -> P2 -> NULL | |
77 | + * tail -> NULL tail ---------^ | |
78 | + * | |
79 | + * This means that anyone wanting to traverse the whole list to USE it just | |
80 | + * needs to start at the head and use the "next" pointers until it goes | |
81 | + * NULL. To add new local content, we insert the item after the tail item | |
82 | + * and update the tail (obviously, if "tail" was NULL, we insert it at the | |
83 | + * head). To clear the local list, WE MUST NOT FREE THE INHERITED CONTENT | |
84 | + * because it is shared between the current list and all our parent list(s). | |
85 | + * The easiest way to avoid this is to simply truncate the list after the | |
86 | + * tail item and free the local list from the head. When inheriting the | |
87 | + * list for a new local dir, we just save off the exclude_list_struct values | |
88 | + * and set the tail to NULL. | |
89 | + */ | |
90 | + | |
91 | +/** Build an exclude structure given an exclude pattern. */ | |
524989ae | 92 | +static void make_exclude(struct exclude_list_struct *listp, const char *pat, |
a55d21aa WD |
93 | + unsigned int pat_len, int mflags) |
94 | { | |
95 | struct exclude_struct *ret; | |
96 | const char *cp; | |
97 | - int ex_len; | |
98 | + unsigned int ex_len; | |
99 | + | |
524989ae | 100 | + if (mflags & MATCHFLG_MERGE_FILE) { |
a55d21aa WD |
101 | + struct exclude_struct *ex; |
102 | + /* If the local include file was already mentioned, don't | |
524989ae | 103 | + * add it again. */ |
a55d21aa | 104 | + for (ex = listp->head; ex; ex = ex->next) { |
524989ae | 105 | + if ((ex->match_flags & MATCHFLG_MERGE_FILE) |
a55d21aa | 106 | + && strlen(ex->pattern) == pat_len |
524989ae | 107 | + && strncmp(ex->pattern, pat, pat_len) == 0) |
a55d21aa WD |
108 | + return; |
109 | + } | |
524989ae WD |
110 | + if ((pat_len == 10 || (pat_len > 10 && pat[pat_len-11] == '/')) |
111 | + && strncmp(pat+pat_len-10, ".cvsignore", 10) == 0) { | |
a55d21aa WD |
112 | + mflags |= MATCHFLG_CVSIGNORE; |
113 | + mflags &= ~MATCHFLG_INCLUDE; | |
114 | + } else | |
115 | + mflags &= ~MATCHFLG_CVSIGNORE; | |
116 | + } | |
117 | ||
118 | ret = new(struct exclude_struct); | |
119 | if (!ret) | |
120 | out_of_memory("make_exclude"); | |
121 | ||
122 | memset(ret, 0, sizeof ret[0]); | |
123 | - ret->include = include; | |
124 | ||
125 | if (exclude_path_prefix) | |
126 | - ret->match_flags |= MATCHFLG_ABS_PATH; | |
524989ae | 127 | - if (exclude_path_prefix && *pattern == '/') |
a55d21aa | 128 | + mflags |= MATCHFLG_ABS_PATH; |
524989ae | 129 | + if (exclude_path_prefix && *pat == '/') |
a55d21aa WD |
130 | ex_len = strlen(exclude_path_prefix); |
131 | else | |
524989ae | 132 | ex_len = 0; |
d1e7c1c8 | 133 | @@ -64,33 +121,52 @@ static void make_exclude(struct exclude_ |
524989ae WD |
134 | out_of_memory("make_exclude"); |
135 | if (ex_len) | |
136 | memcpy(ret->pattern, exclude_path_prefix, ex_len); | |
137 | - strlcpy(ret->pattern + ex_len, pattern, pat_len + 1); | |
138 | + strlcpy(ret->pattern + ex_len, pat, pat_len + 1); | |
a55d21aa WD |
139 | pat_len += ex_len; |
140 | ||
141 | if (strpbrk(ret->pattern, "*[?")) { | |
142 | - ret->match_flags |= MATCHFLG_WILD; | |
143 | + mflags |= MATCHFLG_WILD; | |
144 | if ((cp = strstr(ret->pattern, "**")) != NULL) { | |
145 | - ret->match_flags |= MATCHFLG_WILD2; | |
146 | + mflags |= MATCHFLG_WILD2; | |
147 | /* If the pattern starts with **, note that. */ | |
148 | if (cp == ret->pattern) | |
149 | - ret->match_flags |= MATCHFLG_WILD2_PREFIX; | |
150 | + mflags |= MATCHFLG_WILD2_PREFIX; | |
151 | } | |
152 | } | |
153 | ||
154 | if (pat_len > 1 && ret->pattern[pat_len-1] == '/') { | |
155 | ret->pattern[pat_len-1] = 0; | |
156 | - ret->directory = 1; | |
157 | + mflags |= MATCHFLG_DIRECTORY; | |
158 | } | |
159 | ||
160 | for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++) | |
161 | ret->slash_cnt++; | |
162 | ||
7cb7ae4e WD |
163 | - if (!listp->tail) |
164 | + if (!listp->tail) { | |
165 | + ret->next = listp->head; | |
a55d21aa | 166 | listp->head = listp->tail = ret; |
7cb7ae4e | 167 | - else { |
7cb7ae4e WD |
168 | + } else { |
169 | + ret->next = listp->tail->next; | |
abe86b1f WD |
170 | listp->tail->next = ret; |
171 | listp->tail = ret; | |
172 | } | |
a55d21aa | 173 | + |
524989ae | 174 | + if (mflags & MATCHFLG_MERGE_FILE) { |
a55d21aa | 175 | + struct exclude_list_struct *lp; |
524989ae | 176 | + int ndx = local_lists.cnt++; |
d1e7c1c8 | 177 | + local_lists.array = realloc_array(local_lists.array, |
524989ae | 178 | + struct exclude_list_struct, local_lists.cnt); |
d1e7c1c8 | 179 | + if (!local_lists.array) |
a55d21aa | 180 | + out_of_memory("make_exclude"); |
d1e7c1c8 | 181 | + lp = &local_lists.array[ndx]; |
7cb7ae4e | 182 | + lp->head = lp->tail = NULL; |
524989ae | 183 | + if (asprintf(&lp->debug_type, "per-dir %s ", ret->pattern) < 0) |
a55d21aa WD |
184 | + out_of_memory("make_exclude"); |
185 | + lp->parent = ret; | |
186 | + ret->slash_cnt = ndx; | |
abe86b1f | 187 | + } |
a55d21aa WD |
188 | + |
189 | + ret->match_flags = mflags; | |
190 | } | |
191 | ||
192 | static void free_exclude(struct exclude_struct *ex) | |
d1e7c1c8 | 193 | @@ -99,18 +175,90 @@ static void free_exclude(struct exclude_ |
a55d21aa WD |
194 | free(ex); |
195 | } | |
196 | ||
197 | -void free_exclude_list(struct exclude_list_struct *listp) | |
198 | +static void free_exclude_list(struct exclude_list_struct *listp) | |
199 | { | |
7cb7ae4e WD |
200 | - struct exclude_struct *ent, *next; |
201 | - | |
202 | - for (ent = listp->head; ent; ent = next) { | |
203 | - next = ent->next; | |
204 | - free_exclude(ent); | |
205 | + if (listp->tail) { | |
206 | + struct exclude_struct *ent, *next; | |
207 | + /* Truncate any extra parent items from local list. */ | |
208 | + listp->tail->next = NULL; | |
209 | + for (ent = listp->head; ent; ent = next) { | |
210 | + next = ent->next; | |
211 | + free_exclude(ent); | |
212 | + } | |
524989ae WD |
213 | } |
214 | ||
7cb7ae4e WD |
215 | listp->head = listp->tail = NULL; |
216 | } | |
a55d21aa | 217 | |
a55d21aa WD |
218 | +void *push_local_excludes(char *fname, unsigned int offset) |
219 | +{ | |
220 | + int i; | |
524989ae WD |
221 | + struct exclude_list_root *push = new_array(struct exclude_list_root, 1); |
222 | + | |
223 | + if (!push) | |
a55d21aa WD |
224 | + out_of_memory("push_local_excludes"); |
225 | + | |
524989ae | 226 | + push->cnt = local_lists.cnt; |
d1e7c1c8 WD |
227 | + push->array = new_array(struct exclude_list_struct, local_lists.cnt); |
228 | + if (!push->array) | |
524989ae | 229 | + out_of_memory("push_local_excludes"); |
a55d21aa | 230 | + |
d1e7c1c8 | 231 | + memcpy(push->array, local_lists.array, |
524989ae WD |
232 | + sizeof (struct exclude_list_struct) * local_lists.cnt); |
233 | + | |
d1e7c1c8 WD |
234 | + /* Make it easy to construct the full path for a merge-file that was |
235 | + * specified with a relative path by saving off the current dir. */ | |
a55d21aa WD |
236 | + memcpy(dirbuf, fname, offset); |
237 | + dirbuf_offset = offset; | |
238 | + | |
524989ae | 239 | + for (i = 0; i < local_lists.cnt; i++) { |
d1e7c1c8 | 240 | + struct exclude_list_struct *listp = &local_lists.array[i]; |
a55d21aa WD |
241 | + char *file = listp->parent->pattern; |
242 | + int flags; | |
524989ae WD |
243 | + |
244 | + if (verbose > 2) { | |
245 | + rprintf(FINFO, "[%s] pushing %sexclude list\n", | |
246 | + who_am_i(), listp->debug_type); | |
247 | + } | |
a55d21aa | 248 | + if (listp->parent->match_flags & MATCHFLG_CVSIGNORE) { |
ee1af13c | 249 | + flags = XFLG_WORD_SPLIT | XFLG_WORDS_ONLY; |
7cb7ae4e | 250 | + listp->head = NULL; /* Subdirs don't get our rules. */ |
a55d21aa | 251 | + } else { |
524989ae WD |
252 | + flags = listp->parent->match_flags & MATCHFLG_INCLUDE |
253 | + ? XFLG_DEF_INCLUDE : 0; | |
a55d21aa | 254 | + } |
7cb7ae4e | 255 | + listp->tail = NULL; |
a55d21aa | 256 | + if (strlcpy(fname + offset, file, MAXPATHLEN - offset) |
524989ae | 257 | + < MAXPATHLEN - offset) |
a55d21aa | 258 | + add_exclude_file(listp, fname, flags); |
524989ae | 259 | + else { |
a55d21aa WD |
260 | + io_error |= IOERR_GENERAL; |
261 | + rprintf(FINFO, | |
262 | + "cannot add local excludes in long-named directory %s\n", | |
263 | + full_fname(fname)); | |
264 | + } | |
265 | + } | |
266 | + | |
524989ae | 267 | + return (void*)push; |
a55d21aa WD |
268 | +} |
269 | + | |
270 | +void pop_local_excludes(void *mem) | |
271 | +{ | |
272 | + int i; | |
524989ae WD |
273 | + |
274 | + for (i = 0; i < local_lists.cnt; i++) { | |
d1e7c1c8 | 275 | + struct exclude_list_struct *listp = &local_lists.array[i]; |
524989ae WD |
276 | + if (verbose > 2) { |
277 | + rprintf(FINFO, "[%s] popping %sexclude list\n", | |
278 | + who_am_i(), listp->debug_type); | |
279 | + } | |
a55d21aa WD |
280 | + free_exclude_list(listp); |
281 | + } | |
d1e7c1c8 | 282 | + free(local_lists.array); |
524989ae | 283 | + local_lists = *(struct exclude_list_root*)mem; |
a55d21aa | 284 | + free(mem); |
7cb7ae4e WD |
285 | +} |
286 | + | |
a55d21aa | 287 | static int check_one_exclude(char *name, struct exclude_struct *ex, |
7cb7ae4e WD |
288 | int name_is_dir) |
289 | { | |
d1e7c1c8 | 290 | @@ -134,7 +282,8 @@ static int check_one_exclude(char *name, |
a55d21aa WD |
291 | |
292 | if (!name[0]) return 0; | |
293 | ||
294 | - if (ex->directory && !name_is_dir) return 0; | |
295 | + if ((ex->match_flags & MATCHFLG_DIRECTORY) && !name_is_dir) | |
296 | + return 0; | |
297 | ||
298 | if (*pattern == '/') { | |
299 | match_start = 1; | |
d1e7c1c8 | 300 | @@ -201,9 +350,11 @@ static void report_exclude_result(char c |
a55d21aa WD |
301 | |
302 | if (verbose >= 2) { | |
303 | rprintf(FINFO, "[%s] %scluding %s %s because of %spattern %s%s\n", | |
304 | - who_am_i(), ent->include ? "in" : "ex", | |
305 | + who_am_i(), | |
306 | + ent->match_flags & MATCHFLG_INCLUDE ? "in" : "ex", | |
307 | name_is_dir ? "directory" : "file", name, type, | |
308 | - ent->pattern, ent->directory ? "/" : ""); | |
309 | + ent->pattern, | |
310 | + ent->match_flags & MATCHFLG_DIRECTORY ? "/" : ""); | |
311 | } | |
312 | } | |
313 | ||
d1e7c1c8 | 314 | @@ -217,10 +368,18 @@ int check_exclude(struct exclude_list_st |
a55d21aa WD |
315 | struct exclude_struct *ent; |
316 | ||
317 | for (ent = listp->head; ent; ent = ent->next) { | |
524989ae | 318 | + if (ent->match_flags & MATCHFLG_MERGE_FILE) { |
a55d21aa | 319 | + struct exclude_list_struct *lp |
d1e7c1c8 | 320 | + = &local_lists.array[ent->slash_cnt]; |
a55d21aa WD |
321 | + int rc = check_exclude(lp, name, name_is_dir); |
322 | + if (rc) | |
323 | + return rc; | |
324 | + continue; | |
325 | + } | |
326 | if (check_one_exclude(name, ent, name_is_dir)) { | |
327 | report_exclude_result(name, ent, name_is_dir, | |
328 | listp->debug_type); | |
329 | - return ent->include ? 1 : -1; | |
330 | + return (ent->match_flags & MATCHFLG_INCLUDE) ? 1 : -1; | |
331 | } | |
332 | } | |
333 | ||
d1e7c1c8 | 334 | @@ -236,11 +395,11 @@ int check_exclude(struct exclude_list_st |
a55d21aa WD |
335 | * *incl_ptr value will be 1 for an include, 0 for an exclude, and -1 for |
336 | * the list-clearing "!" token. | |
337 | */ | |
338 | -static const char *get_exclude_tok(const char *p, int *len_ptr, int *incl_ptr, | |
339 | +static const char *get_exclude_tok(const char *p, int *len_ptr, int *flag_ptr, | |
340 | int xflags) | |
341 | { | |
342 | const unsigned char *s = (const unsigned char *)p; | |
343 | - int len; | |
344 | + int len, mflags = 0; | |
345 | ||
346 | if (xflags & XFLG_WORD_SPLIT) { | |
347 | /* Skip over any initial whitespace. */ | |
d1e7c1c8 | 348 | @@ -250,13 +409,19 @@ static const char *get_exclude_tok(const |
a55d21aa WD |
349 | p = (const char *)s; |
350 | } | |
351 | ||
352 | - /* Is this a '+' or '-' followed by a space (not whitespace)? */ | |
353 | + /* Is this a +/-/. followed by a space (not whitespace)? */ | |
ee1af13c | 354 | if (!(xflags & XFLG_WORDS_ONLY) |
a55d21aa WD |
355 | - && (*s == '-' || *s == '+') && s[1] == ' ') { |
356 | - *incl_ptr = *s == '+'; | |
357 | + && (*s == '-' || *s == '+' || *s == '.') && s[1] == ' ') { | |
358 | + if (*s == '+') | |
359 | + mflags |= MATCHFLG_INCLUDE; | |
360 | + else if (*s == '.') { | |
524989ae | 361 | + mflags |= MATCHFLG_MERGE_FILE; |
a55d21aa WD |
362 | + if (xflags & XFLG_DEF_INCLUDE) |
363 | + mflags |= MATCHFLG_INCLUDE; | |
364 | + } | |
365 | s += 2; | |
366 | - } else | |
367 | - *incl_ptr = xflags & XFLG_DEF_INCLUDE; | |
368 | + } else if (xflags & XFLG_DEF_INCLUDE) | |
369 | + mflags |= MATCHFLG_INCLUDE; | |
370 | ||
371 | if (xflags & XFLG_WORD_SPLIT) { | |
372 | const unsigned char *cp = s; | |
d1e7c1c8 | 373 | @@ -268,9 +433,10 @@ static const char *get_exclude_tok(const |
a55d21aa WD |
374 | len = strlen(s); |
375 | ||
ee1af13c | 376 | if (*p == '!' && len == 1 && !(xflags & XFLG_WORDS_ONLY)) |
a55d21aa WD |
377 | - *incl_ptr = -1; |
378 | + mflags |= MATCHFLG_CLEAR_LIST; | |
379 | ||
380 | *len_ptr = len; | |
381 | + *flag_ptr = mflags; | |
382 | return (const char *)s; | |
383 | } | |
384 | ||
d1e7c1c8 | 385 | @@ -278,7 +444,7 @@ static const char *get_exclude_tok(const |
a55d21aa WD |
386 | void add_exclude(struct exclude_list_struct *listp, const char *pattern, |
387 | int xflags) | |
388 | { | |
389 | - int pat_len, incl; | |
390 | + int pat_len, mflags; | |
391 | const char *cp; | |
392 | ||
393 | if (!pattern) | |
d1e7c1c8 | 394 | @@ -287,27 +453,49 @@ void add_exclude(struct exclude_list_str |
a55d21aa WD |
395 | cp = pattern; |
396 | pat_len = 0; | |
397 | while (1) { | |
398 | - cp = get_exclude_tok(cp + pat_len, &pat_len, &incl, xflags); | |
399 | + cp = get_exclude_tok(cp + pat_len, &pat_len, &mflags, xflags); | |
400 | if (!pat_len) | |
401 | break; | |
524989ae | 402 | - /* If we got the special "!" token, clear the list. */ |
7cb7ae4e | 403 | - if (incl < 0) { |
524989ae WD |
404 | + if (mflags & MATCHFLG_CLEAR_LIST) { |
405 | if (verbose > 2) { | |
7cb7ae4e WD |
406 | rprintf(FINFO, |
407 | "[%s] clearing %sexclude list\n", | |
408 | who_am_i(), listp->debug_type); | |
409 | } | |
410 | free_exclude_list(listp); | |
411 | - } else { | |
412 | - make_exclude(listp, cp, pat_len, incl); | |
413 | - | |
414 | - if (verbose > 2) { | |
a55d21aa WD |
415 | - rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s)\n", |
416 | - who_am_i(), pat_len, cp, | |
417 | - listp->debug_type, | |
418 | - incl ? "include" : "exclude"); | |
419 | + continue; | |
420 | + } | |
524989ae | 421 | + if (mflags & MATCHFLG_MERGE_FILE) { |
a55d21aa WD |
422 | + char name[MAXPATHLEN]; |
423 | + if ((unsigned) pat_len >= sizeof name) | |
524989ae | 424 | + continue; /* XXX complain? */ |
a55d21aa | 425 | + strlcpy(name, cp, pat_len+1); |
524989ae | 426 | + if (strchr(name, '/') != NULL) { |
a55d21aa WD |
427 | + if (sanitize_paths) |
428 | + sanitize_path(name, curr_dir); | |
429 | + if (*name == '/') | |
430 | + cp = name; | |
431 | + else { | |
432 | + if (strlcpy(dirbuf + dirbuf_offset, | |
433 | + name, MAXPATHLEN - dirbuf_offset) | |
434 | + >= MAXPATHLEN - dirbuf_offset) | |
524989ae | 435 | + continue; /* XXX complain? */ |
a55d21aa WD |
436 | + cp = dirbuf; |
437 | + } | |
438 | + add_exclude_file(listp, cp, | |
439 | + xflags | XFLG_FATAL_ERRORS); | |
440 | + continue; | |
441 | } | |
442 | } | |
524989ae | 443 | + |
a55d21aa WD |
444 | + make_exclude(listp, cp, pat_len, mflags); |
445 | + | |
446 | + if (verbose > 2) { | |
447 | + rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s%sclude)\n", | |
448 | + who_am_i(), pat_len, cp, listp->debug_type, | |
524989ae | 449 | + mflags & MATCHFLG_MERGE_FILE ? "FILE " : "", |
a55d21aa WD |
450 | + mflags & MATCHFLG_INCLUDE ? "in" : "ex"); |
451 | + } | |
452 | } | |
453 | } | |
454 | ||
d1e7c1c8 | 455 | @@ -383,15 +571,19 @@ void send_exclude_list(int f) |
a55d21aa WD |
456 | l = strlcpy(p, ent->pattern, sizeof p); |
457 | if (l == 0 || l >= MAXPATHLEN) | |
458 | continue; | |
459 | - if (ent->directory) { | |
460 | + if (ent->match_flags & MATCHFLG_DIRECTORY) { | |
461 | p[l++] = '/'; | |
462 | p[l] = '\0'; | |
463 | } | |
464 | ||
465 | - if (ent->include) { | |
466 | + if (ent->match_flags & MATCHFLG_INCLUDE) { | |
467 | write_int(f, l + 2); | |
468 | write_buf(f, "+ ", 2); | |
469 | - } else if ((*p == '-' || *p == '+') && p[1] == ' ') { | |
524989ae | 470 | + } else if (ent->match_flags & MATCHFLG_MERGE_FILE) { |
a55d21aa WD |
471 | + write_int(f, l + 2); |
472 | + write_buf(f, ". ", 2); | |
473 | + } else if ((*p == '-' || *p == '+' || *p == '.') | |
474 | + && p[1] == ' ') { | |
475 | write_int(f, l + 2); | |
476 | write_buf(f, "- ", 2); | |
477 | } else | |
d1e7c1c8 | 478 | @@ -432,6 +624,7 @@ void add_cvs_excludes(void) |
a55d21aa WD |
479 | char fname[MAXPATHLEN]; |
480 | char *p; | |
481 | ||
482 | + add_exclude(&exclude_list, ". .cvsignore", 0); | |
483 | add_exclude(&exclude_list, default_cvsignore, | |
ee1af13c | 484 | XFLG_WORD_SPLIT | XFLG_WORDS_ONLY); |
a55d21aa | 485 | |
7cb7ae4e | 486 | --- flist.c 14 May 2004 21:23:41 -0000 1.222 |
d1e7c1c8 | 487 | +++ flist.c 15 May 2004 07:01:28 -0000 |
a55d21aa WD |
488 | @@ -39,8 +39,6 @@ extern int module_id; |
489 | extern int ignore_errors; | |
490 | extern int numeric_ids; | |
491 | ||
492 | -extern int cvs_exclude; | |
493 | - | |
494 | extern int recurse; | |
495 | extern char curr_dir[MAXPATHLEN]; | |
496 | extern char *files_from; | |
7cb7ae4e | 497 | @@ -65,7 +63,6 @@ extern int write_batch; |
a55d21aa WD |
498 | |
499 | extern struct exclude_list_struct exclude_list; | |
500 | extern struct exclude_list_struct server_exclude_list; | |
501 | -extern struct exclude_list_struct local_exclude_list; | |
502 | ||
503 | int io_error; | |
504 | ||
7cb7ae4e | 505 | @@ -210,8 +207,6 @@ int link_stat(const char *path, STRUCT_S |
a55d21aa WD |
506 | */ |
507 | static int check_exclude_file(char *fname, int is_dir, int exclude_level) | |
508 | { | |
509 | - int rc; | |
510 | - | |
511 | #if 0 /* This currently never happens, so avoid a useless compare. */ | |
512 | if (exclude_level == NO_EXCLUDES) | |
513 | return 0; | |
7cb7ae4e | 514 | @@ -233,10 +228,7 @@ static int check_exclude_file(char *fnam |
a55d21aa WD |
515 | if (exclude_level != ALL_EXCLUDES) |
516 | return 0; | |
517 | if (exclude_list.head | |
518 | - && (rc = check_exclude(&exclude_list, fname, is_dir)) != 0) | |
519 | - return rc < 0; | |
520 | - if (local_exclude_list.head | |
521 | - && check_exclude(&local_exclude_list, fname, is_dir) < 0) | |
522 | + && check_exclude(&exclude_list, fname, is_dir) < 0) | |
523 | return 1; | |
524 | return 0; | |
525 | } | |
7cb7ae4e | 526 | @@ -946,15 +938,7 @@ void send_file_name(int f, struct file_l |
a55d21aa WD |
527 | |
528 | if (recursive && S_ISDIR(file->mode) | |
529 | && !(file->flags & FLAG_MOUNT_POINT)) { | |
530 | - struct exclude_list_struct last_list = local_exclude_list; | |
531 | - local_exclude_list.head = local_exclude_list.tail = NULL; | |
532 | send_directory(f, flist, f_name_to(file, fbuf)); | |
7cb7ae4e WD |
533 | - if (verbose > 2) { |
534 | - rprintf(FINFO, "[%s] popping %sexclude list\n", | |
535 | - who_am_i(), local_exclude_list.debug_type); | |
536 | - } | |
a55d21aa WD |
537 | - free_exclude_list(&local_exclude_list); |
538 | - local_exclude_list = last_list; | |
539 | } | |
540 | } | |
541 | ||
7cb7ae4e | 542 | @@ -965,6 +949,7 @@ static void send_directory(int f, struct |
a55d21aa WD |
543 | struct dirent *di; |
544 | char fname[MAXPATHLEN]; | |
545 | unsigned int offset; | |
546 | + void *save_excludes; | |
547 | char *p; | |
548 | ||
549 | d = opendir(dir); | |
7cb7ae4e | 550 | @@ -989,18 +974,7 @@ static void send_directory(int f, struct |
a55d21aa WD |
551 | offset++; |
552 | } | |
553 | ||
554 | - if (cvs_exclude) { | |
555 | - if (strlcpy(p, ".cvsignore", MAXPATHLEN - offset) | |
556 | - < MAXPATHLEN - offset) { | |
557 | - add_exclude_file(&local_exclude_list, fname, | |
ee1af13c | 558 | - XFLG_WORD_SPLIT | XFLG_WORDS_ONLY); |
a55d21aa WD |
559 | - } else { |
560 | - io_error |= IOERR_GENERAL; | |
561 | - rprintf(FINFO, | |
562 | - "cannot cvs-exclude in long-named directory %s\n", | |
563 | - full_fname(fname)); | |
564 | - } | |
565 | - } | |
566 | + save_excludes = push_local_excludes(fname, offset); | |
567 | ||
568 | for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) { | |
569 | char *dname = d_name(di); | |
7cb7ae4e | 570 | @@ -1021,6 +995,8 @@ static void send_directory(int f, struct |
a55d21aa WD |
571 | rprintf(FERROR, "readdir(%s): (%d) %s\n", |
572 | dir, errno, strerror(errno)); | |
573 | } | |
574 | + | |
575 | + pop_local_excludes(save_excludes); | |
576 | ||
577 | closedir(d); | |
578 | } | |
7cb7ae4e | 579 | --- rsync.h 13 May 2004 18:51:22 -0000 1.203 |
d1e7c1c8 | 580 | +++ rsync.h 15 May 2004 07:01:29 -0000 |
7cb7ae4e | 581 | @@ -493,18 +493,21 @@ struct map_struct { |
a55d21aa WD |
582 | #define MATCHFLG_WILD2 (1<<1) /* pattern has '**' */ |
583 | #define MATCHFLG_WILD2_PREFIX (1<<2) /* pattern starts with '**' */ | |
584 | #define MATCHFLG_ABS_PATH (1<<3) /* path-match on absolute path */ | |
585 | +#define MATCHFLG_INCLUDE (1<<4) /* this is an include, not an exclude */ | |
586 | +#define MATCHFLG_CLEAR_LIST (1<<5) /* this item is the "!" token */ | |
587 | +#define MATCHFLG_DIRECTORY (1<<6) /* this matches only directories */ | |
524989ae | 588 | +#define MATCHFLG_MERGE_FILE (1<<7) /* specifies a file to merge */ |
a55d21aa WD |
589 | +#define MATCHFLG_CVSIGNORE (1<<8) /* parse this as a .cvsignore file */ |
590 | struct exclude_struct { | |
591 | struct exclude_struct *next; | |
592 | char *pattern; | |
593 | int match_flags; | |
594 | - int include; | |
595 | - int directory; | |
596 | int slash_cnt; | |
597 | }; | |
598 | ||
599 | struct exclude_list_struct { | |
600 | - struct exclude_struct *head; | |
601 | - struct exclude_struct *tail; | |
602 | + struct exclude_struct *head, *tail; | |
7cb7ae4e | 603 | + struct exclude_struct *parent; |
a55d21aa WD |
604 | char *debug_type; |
605 | }; | |
606 | ||
ea238f1c | 607 | --- rsync.yo 7 May 2004 00:18:37 -0000 1.169 |
d1e7c1c8 | 608 | +++ rsync.yo 15 May 2004 07:01:29 -0000 |
ea238f1c | 609 | @@ -1075,6 +1075,72 @@ itemize( |
524989ae WD |
610 | it would be excluded by the "*") |
611 | ) | |
612 | ||
613 | +manpagesection(MERGING EXCLUDE FILES) | |
614 | + | |
615 | +You can merge whole files into an exclude file using a rule that starts | |
616 | +with a ". " (a dot followed by a space) and has a filename in place of the | |
617 | +pattern. There are two types of merge rules, single-instance and | |
618 | +per-directory: | |
619 | + | |
620 | +itemize( | |
621 | + it() If the filename has no slashes in it, it is a per-directory merge; | |
622 | + rsync scans every directory that is traversed and merges the named file's | |
623 | + contents (when it exists), putting the contents of each subdirectory's | |
624 | + file at the start of this per-directory sub-list (so subdirectories | |
625 | + inherit the contents of their parent directories by default, but each | |
626 | + subdirectory's rules have precedence over the parent's rules). | |
627 | + | |
628 | + it() If a filename has a slash in it, it is a single-instance merge; the | |
629 | + named file's contents will be merged into the current exclude file, | |
630 | + replacing the merge rule. Thus, you should use the name ./foo instead of | |
631 | + foo if you don't want to scan for "foo" in all the subdirectories of the | |
632 | + current directory. | |
633 | +) | |
634 | + | |
635 | +Note also that you can eliminate all the inherited rules for the current | |
636 | +per-directory ruleset by putting the list-clearing token (!) in the file. | |
637 | +This clears only the rules of the current per-directory sub-list (up | |
638 | +through the token) and only for the current directory and its | |
639 | +subdirectories. | |
640 | + | |
641 | +Here's an example. Specify the file that holds this set of rules via a | |
642 | +normal --exclude-from option: | |
643 | + | |
644 | +verb( | |
645 | + . /home/user/.global_excludes | |
646 | + - *.gz | |
647 | + . .excl | |
648 | + + *.[ch] | |
649 | + - *.o | |
650 | +) | |
651 | + | |
652 | +This will merge the contents of the /home/user/.global_excludes file at the | |
653 | +start of the list and also turns the ".excl" filename into a per-directory | |
654 | +exclude file whose local contents will be merged into the list in place of | |
655 | +the .excl line. | |
656 | + | |
657 | +Additionally, you can affect where the --cvs-exclude (-C) option's | |
658 | +inclusion of a per-directory .cvsignore file gets placed into your rules by | |
659 | +adding an explicit a merge rule for ".cvsignore". For instance, specifying | |
660 | +this: | |
661 | + | |
662 | +verb( | |
663 | + rsync -avC --exclude='. .cvsignore' --exclude-from=foo a/ b | |
664 | +) | |
665 | + | |
666 | +will merge all the per-directory .cvsignore rules at the start of your list | |
667 | +rather than at the end. This allows their dir-specific rules to supersede | |
668 | +your rules instead of being subservient to them. (The global rules taken | |
669 | +from the $HOME/.cvsignore file and from $CVSIGNORE are not affected by | |
670 | +this.) | |
671 | + | |
672 | +Note also that the parsing of any merge-file named ".cvsignore" is always | |
673 | +done in a CVS-compatible manner (even if -C wasn't specified) -- i.e. the | |
674 | +rules are always exclude rules (even when specified by an include option), | |
675 | +they are split on whitespace, no special prefixes or list-clearing tokens | |
676 | +are honored, and (for per-directory files) subdirectories don't inherit the | |
677 | +parent directory's rules. | |
678 | + | |
679 | manpagesection(BATCH MODE) | |
680 | ||
681 | bf(Note:) Batch mode should be considered experimental in this version |