exclude.c 9.63 KB
Newer Older
1 2 3 4
/* -*- c-file-style: "linux" -*-
     
   Copyright (C) 1996-2001 by Andrew Tridgell <tridge@samba.org>
   Copyright (C) 1996 by Paul Mackerras
Andrew Tridgell's avatar
Andrew Tridgell committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

21 22
/* a lot of this stuff was originally derived from GNU tar, although
   it has now changed so much that it is hard to tell :) */
Andrew Tridgell's avatar
Andrew Tridgell committed
23

24 25
/* include/exclude cluestick added by Martin Pool <mbp@samba.org> */

Andrew Tridgell's avatar
Andrew Tridgell committed
26 27 28
#include "rsync.h"

extern int verbose;
29
extern int delete_mode;
Andrew Tridgell's avatar
Andrew Tridgell committed
30

31
static struct exclude_struct **exclude_list;
Andrew Tridgell's avatar
Andrew Tridgell committed
32

33
/* build an exclude structure given a exclude pattern */
34
static struct exclude_struct *make_exclude(const char *pattern, int include)
Andrew Tridgell's avatar
Andrew Tridgell committed
35
{
36
	struct exclude_struct *ret;
Andrew Tridgell's avatar
Andrew Tridgell committed
37

38 39
	ret = (struct exclude_struct *)malloc(sizeof(*ret));
	if (!ret) out_of_memory("make_exclude");
Andrew Tridgell's avatar
Andrew Tridgell committed
40

41 42 43 44 45 46 47 48 49 50 51 52 53
	memset(ret, 0, sizeof(*ret));

	if (strncmp(pattern,"- ",2) == 0) {
		pattern += 2;
	} else if (strncmp(pattern,"+ ",2) == 0) {
		ret->include = 1;
		pattern += 2;
	} else {
		ret->include = include;
	}

	ret->pattern = strdup(pattern);

54
	if (!ret->pattern) out_of_memory("make_exclude");
Andrew Tridgell's avatar
Andrew Tridgell committed
55

56 57
	if (strpbrk(pattern, "*[?")) {
	    ret->regular_exp = 1;
58 59 60 61 62 63 64 65 66 67 68
	    ret->fnmatch_flags = FNM_PATHNAME;
	    if (strstr(pattern, "**")) {
		    static int tested;
		    if (!tested) {
			    tested = 1;
			    if (fnmatch("a/b/*", "a/b/c/d", FNM_PATHNAME)==0) {
				    rprintf(FERROR,"WARNING: fnmatch FNM_PATHNAME is broken on your system\n");
			    }
		    }
		    ret->fnmatch_flags = 0;
	    }
69
	}
Andrew Tridgell's avatar
Andrew Tridgell committed
70

71 72 73 74
	if (strlen(pattern) > 1 && pattern[strlen(pattern)-1] == '/') {
		ret->pattern[strlen(pattern)-1] = 0;
		ret->directory = 1;
	}
Andrew Tridgell's avatar
Andrew Tridgell committed
75

76 77 78
	if (!strchr(ret->pattern,'/')) {
		ret->local = 1;
	}
Andrew Tridgell's avatar
 
Andrew Tridgell committed
79

80 81 82 83 84 85 86 87 88
	return ret;
}

static void free_exclude(struct exclude_struct *ex)
{
	free(ex->pattern);
	memset(ex,0,sizeof(*ex));
	free(ex);
}
Andrew Tridgell's avatar
Andrew Tridgell committed
89

90 91
static int check_one_exclude(char *name, struct exclude_struct *ex,
                             STRUCT_STAT *st)
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
{
	char *p;
	int match_start=0;
	char *pattern = ex->pattern;

	if (ex->local && (p=strrchr(name,'/')))
		name = p+1;

	if (!name[0]) return 0;

	if (ex->directory && !S_ISDIR(st->st_mode)) return 0;

	if (*pattern == '/' && *name != '/') {
		match_start = 1;
		pattern++;
	}

	if (ex->regular_exp) {
110
		if (fnmatch(pattern, name, ex->fnmatch_flags) == 0) {
111
			return 1;
112
		}
113 114
	} else {
		int l1 = strlen(name);
115
		int l2 = strlen(pattern);
116
		if (l2 <= l1 && 
117
		    strcmp(name+(l1-l2),pattern) == 0 &&
118
		    (l1==l2 || (!match_start && name[l1-(l2+1)] == '/'))) {
119
			return 1;
120
		}
121 122 123
	}

	return 0;
Andrew Tridgell's avatar
Andrew Tridgell committed
124 125 126
}


127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
static void report_exclude_result(char const *name,
                                  struct exclude_struct const *ent,
                                  STRUCT_STAT const *st)
{
        /* If a trailing slash is present to match only directories,
         * then it is stripped out by make_exclude.  So as a special
         * case we add it back in here. */
        
        if (verbose >= 2)
                rprintf(FINFO, "%s %s %s because of pattern %s%s\n",
                        ent->include ? "including" : "excluding",
                        S_ISDIR(st->st_mode) ? "directory" : "file",
                        name, ent->pattern,
                        ent->directory ? "/" : "");
}


/*
 * Return true if file NAME is defined to be excluded by either
 * LOCAL_EXCLUDE_LIST or the globals EXCLUDE_LIST.
 */
int check_exclude(char *name, struct exclude_struct **local_exclude_list,
149
		  STRUCT_STAT *st)
Andrew Tridgell's avatar
Andrew Tridgell committed
150
{
151
	int n;
152
        struct exclude_struct *ent;
Andrew Tridgell's avatar
Andrew Tridgell committed
153

154 155 156 157
	if (name && (name[0] == '.') && !name[1])
		/* never exclude '.', even if somebody does --exclude '*' */
		return 0;

158
	if (exclude_list) {
159 160 161 162 163 164 165
		for (n=0; exclude_list[n]; n++) {
                        ent = exclude_list[n];
			if (check_one_exclude(name, ent, st)) {
                                report_exclude_result(name, ent, st);
				return !ent->include;
                        }
                }
166
	}
Andrew Tridgell's avatar
Andrew Tridgell committed
167

168
	if (local_exclude_list) {
169
		for (n=0; local_exclude_list[n]; n++) {
Martin Pool's avatar
Martin Pool committed
170
                        ent = local_exclude_list[n];
171 172 173 174 175
			if (check_one_exclude(name, ent, st)) {
                                report_exclude_result(name, ent, st);
				return !ent->include;
                        }
                }
176
	}
Andrew Tridgell's avatar
Andrew Tridgell committed
177

178
	return 0;
Andrew Tridgell's avatar
Andrew Tridgell committed
179 180 181
}


182
void add_exclude_list(const char *pattern, struct exclude_struct ***list, int include)
Andrew Tridgell's avatar
Andrew Tridgell committed
183
{
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
	int len=0;
	if (list && *list)
		for (; (*list)[len]; len++) ;

	if (strcmp(pattern,"!") == 0) {
		if (verbose > 2)
			rprintf(FINFO,"clearing exclude list\n");
		while ((len)--) {
			free_exclude((*list)[len]);
		}
		free((*list));
		*list = NULL;
		return;
	}

199
	*list = (struct exclude_struct **)Realloc(*list,sizeof(struct exclude_struct *)*(len+2));
200 201 202 203
	
	if (!*list || !((*list)[len] = make_exclude(pattern, include)))
		out_of_memory("add_exclude");
	
204 205 206 207 208
	if (verbose > 2) {
		rprintf(FINFO,"add_exclude(%s,%s)\n",pattern,
			      include ? "include" : "exclude");
	}

209
	(*list)[len+1] = NULL;
Andrew Tridgell's avatar
Andrew Tridgell committed
210 211
}

212
void add_exclude(const char *pattern, int include)
Andrew Tridgell's avatar
Andrew Tridgell committed
213
{
214
	add_exclude_list(pattern,&exclude_list, include);
Andrew Tridgell's avatar
Andrew Tridgell committed
215 216
}

217
struct exclude_struct **make_exclude_list(const char *fname,
218 219
					  struct exclude_struct **list1,
					  int fatal, int include)
Andrew Tridgell's avatar
Andrew Tridgell committed
220
{
221 222 223 224 225
	struct exclude_struct **list=list1;
	FILE *f = fopen(fname,"r");
	char line[MAXPATHLEN];
	if (!f) {
		if (fatal) {
226 227 228 229
			rsyserr(FERROR, errno,
                                "failed to open %s file %s",
                                include ? "include" : "exclude",
                                fname);
230
			exit_cleanup(RERR_FILEIO);
231 232 233 234 235 236 237 238
		}
		return list;
	}

	while (fgets(line,MAXPATHLEN,f)) {
		int l = strlen(line);
		if (l && line[l-1] == '\n') l--;
		line[l] = 0;
239 240 241 242 243 244
		if (line[0] && (line[0] != ';') && (line[0] != '#')) {
			/* Skip lines starting with semicolon or pound.
			   It probably wouldn't cause any harm to not skip
			     them but there's no need to save them. */
			add_exclude_list(line,&list,include);
		}
245 246 247
	}
	fclose(f);
	return list;
Andrew Tridgell's avatar
Andrew Tridgell committed
248 249 250
}


251
void add_exclude_file(const char *fname, int fatal, int include)
Andrew Tridgell's avatar
Andrew Tridgell committed
252
{
253 254
	if (!fname || !*fname) return;

255
	exclude_list = make_exclude_list(fname,exclude_list,fatal,include);
Andrew Tridgell's avatar
Andrew Tridgell committed
256 257 258 259 260
}


void send_exclude_list(int f)
{
261 262
	int i;
	extern int remote_version;
263 264
	extern int list_only, recurse;

Martin Pool's avatar
Doc.  
Martin Pool committed
265 266 267 268
	/* This is a complete hack - blame Rusty.
	 *
	 * FIXME: This pattern shows up in the output of
	 * report_exclude_result(), which is not ideal. */
269 270 271
	if (list_only && !recurse) {
		add_exclude("/*/*", 0);
	}
272 273 274 275 276 277 278 279

	if (!exclude_list) {
		write_int(f,0);
		return;
	}

	for (i=0;exclude_list[i];i++) {
		int l;
280 281 282 283
		char pattern[MAXPATHLEN];

		strlcpy(pattern,exclude_list[i]->pattern,sizeof(pattern)); 
		if (exclude_list[i]->directory) strlcat(pattern,"/", sizeof(pattern));
284

285 286 287 288
		l = strlen(pattern);
		if (l == 0) continue;
		if (exclude_list[i]->include) {
			if (remote_version < 19) {
289
				rprintf(FERROR,"remote rsync does not support include syntax - aborting\n");
290
				exit_cleanup(RERR_UNSUPPORTED);
291
			}
292 293 294 295
			write_int(f,l+2);
			write_buf(f,"+ ",2);
		} else {
			write_int(f,l);
296 297 298 299 300
		}
		write_buf(f,pattern,l);
	}    

	write_int(f,0);
Andrew Tridgell's avatar
Andrew Tridgell committed
301 302 303 304 305
}


void recv_exclude_list(int f)
{
306
	char line[MAXPATHLEN];
307 308
	unsigned int l;

309 310 311 312 313
	while ((l=read_int(f))) {
		if (l >= MAXPATHLEN) overflow("recv_exclude_list");
		read_sbuf(f,line,l);
		add_exclude(line,0);
	}
Andrew Tridgell's avatar
Andrew Tridgell committed
314 315
}

316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
/* Get the next include/exclude arg from the string. It works in a similar way
** to strtok - initially an arg is sent over, from then on NULL. This
** routine takes into account any +/- in the strings and does not
** consider the space following it as a delimeter.
*/
char *get_exclude_tok(char *p)
{
	static char *s;
	static int more;
	char *t;

	if (p) {
		s=p;
		if (*p)
			more=1;
	}

	if (!more)
		return(NULL);
Andrew Tridgell's avatar
Andrew Tridgell committed
335

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
	/* Skip over any initial spaces */
	while(isspace(*s))
		s++;

	/* Are we at the end of the string? */
	if (*s) {
		/* remember the beginning of the token */
		t=s;

		/* Is this a '+' or '-' followed by a space (not whitespace)? */
		if ((*s=='+' || *s=='-') && *(s+1)==' ')
			s+=2;
	
		/* Skip to the next space or the end of the string */
		while(!isspace(*s) && *s!='\0')
			s++;
	} else {
		t=NULL;
	}

	/* Have we reached the end of the string? */
	if (*s)
		*s++='\0';
	else
		more=0;
	return(t);
}

	
365 366 367 368 369 370
void add_exclude_line(char *p)
{
	char *tok;
	if (!p || !*p) return;
	p = strdup(p);
	if (!p) out_of_memory("add_exclude_line");
371
 	for (tok=get_exclude_tok(p); tok; tok=get_exclude_tok(NULL))
372
		add_exclude(tok, 0);
373 374 375
	free(p);
}

376 377 378 379 380 381
void add_include_line(char *p)
{
	char *tok;
	if (!p || !*p) return;
	p = strdup(p);
	if (!p) out_of_memory("add_include_line");
382
	for (tok=get_exclude_tok(p); tok; tok=get_exclude_tok(NULL))
383 384 385 386
		add_exclude(tok, 1);
	free(p);
}

387

Andrew Tridgell's avatar
Andrew Tridgell committed
388 389 390 391 392 393 394 395 396 397 398
static char *cvs_ignore_list[] = {
  "RCS","SCCS","CVS","CVS.adm","RCSLOG","cvslog.*",
  "tags","TAGS",".make.state",".nse_depinfo",
  "*~", "#*", ".#*", ",*", "*.old", "*.bak", "*.BAK", "*.orig",
  "*.rej", ".del-*", "*.a", "*.o", "*.obj", "*.so", "*.Z", "*.elc", "*.ln",
  "core",NULL};



void add_cvs_excludes(void)
{
399 400 401
	char fname[MAXPATHLEN];
	char *p;
	int i;
Andrew Tridgell's avatar
Andrew Tridgell committed
402
  
403 404
	for (i=0; cvs_ignore_list[i]; i++)
		add_exclude(cvs_ignore_list[i], 0);
Andrew Tridgell's avatar
Andrew Tridgell committed
405

406
	if ((p=getenv("HOME")) && strlen(p) < (MAXPATHLEN-12)) {
407
		snprintf(fname,sizeof(fname), "%s/.cvsignore",p);
408 409
		add_exclude_file(fname,0,0);
	}
Andrew Tridgell's avatar
Andrew Tridgell committed
410

411
	add_exclude_line(getenv("CVSIGNORE"));
Andrew Tridgell's avatar
Andrew Tridgell committed
412
}