xref: /openbsd-src/usr.bin/rsync/rules.c (revision d9a51c353c88dac7b4a389c112b4cfe97b8e3a46)
1*d9a51c35Sjmc /*	$OpenBSD: rules.c,v 1.5 2022/12/26 19:16:02 jmc Exp $ */
22157a8e5Sclaudio /*
32157a8e5Sclaudio  * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
42157a8e5Sclaudio  *
52157a8e5Sclaudio  * Permission to use, copy, modify, and distribute this software for any
62157a8e5Sclaudio  * purpose with or without fee is hereby granted, provided that the above
72157a8e5Sclaudio  * copyright notice and this permission notice appear in all copies.
82157a8e5Sclaudio  *
92157a8e5Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
102157a8e5Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
112157a8e5Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
122157a8e5Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
132157a8e5Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
142157a8e5Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
152157a8e5Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
162157a8e5Sclaudio  */
1757987d16Sclaudio #include <err.h>
1857987d16Sclaudio #include <stdlib.h>
1957987d16Sclaudio #include <stdio.h>
2057987d16Sclaudio #include <string.h>
2157987d16Sclaudio 
2257987d16Sclaudio #include "extern.h"
2357987d16Sclaudio 
2457987d16Sclaudio struct rule {
2557987d16Sclaudio 	char			*pattern;
2657987d16Sclaudio 	enum rule_type		 type;
2757987d16Sclaudio #ifdef NOTYET
2857987d16Sclaudio 	unsigned int		 modifiers;
2957987d16Sclaudio #endif
3057987d16Sclaudio 	short			 numseg;
3157987d16Sclaudio 	unsigned char		 anchored;
3257987d16Sclaudio 	unsigned char		 fileonly;
3357987d16Sclaudio 	unsigned char		 nowild;
3457987d16Sclaudio 	unsigned char		 onlydir;
3557987d16Sclaudio 	unsigned char		 leadingdir;
3657987d16Sclaudio };
3757987d16Sclaudio 
3857987d16Sclaudio static struct rule	*rules;
3957987d16Sclaudio static size_t		 numrules;	/* number of rules */
4057987d16Sclaudio static size_t		 rulesz;	/* available size */
4157987d16Sclaudio 
4257987d16Sclaudio /* up to protocol 29 filter rules only support - + ! and no modifiers */
4357987d16Sclaudio 
4457987d16Sclaudio const struct command {
4557987d16Sclaudio 	enum rule_type		type;
4657987d16Sclaudio 	char			sopt;
4757987d16Sclaudio 	const char		*lopt;
4857987d16Sclaudio } commands[] = {
4957987d16Sclaudio 	{ RULE_EXCLUDE,		'-',	"exclude" },
5057987d16Sclaudio 	{ RULE_INCLUDE,		'+',	"include" },
5157987d16Sclaudio 	{ RULE_CLEAR,		'!',	"clear" },
5257987d16Sclaudio #ifdef NOTYET
5357987d16Sclaudio 	{ RULE_MERGE,		'.',	"merge" },
5457987d16Sclaudio 	{ RULE_DIR_MERGE,	':',	"dir-merge" },
5557987d16Sclaudio 	{ RULE_SHOW,		'S',	"show" },
5657987d16Sclaudio 	{ RULE_HIDE,		'H',	"hide" },
5757987d16Sclaudio 	{ RULE_PROTECT,		'P',	"protect" },
5857987d16Sclaudio 	{ RULE_RISK,		'R',	"risk" },
5957987d16Sclaudio #endif
6057987d16Sclaudio 	{ 0 }
6157987d16Sclaudio };
6257987d16Sclaudio 
6357987d16Sclaudio #ifdef NOTYET
6457987d16Sclaudio #define MOD_ABSOLUTE			0x0001
6557987d16Sclaudio #define MOD_NEGATE			0x0002
6657987d16Sclaudio #define MOD_CVSEXCLUDE			0x0004
6757987d16Sclaudio #define MOD_SENDING			0x0008
6857987d16Sclaudio #define MOD_RECEIVING			0x0010
6957987d16Sclaudio #define MOD_PERISHABLE			0x0020
7057987d16Sclaudio #define MOD_XATTR			0x0040
7157987d16Sclaudio #define MOD_MERGE_EXCLUDE		0x0080
7257987d16Sclaudio #define MOD_MERGE_INCLUDE		0x0100
7357987d16Sclaudio #define MOD_MERGE_CVSCOMPAT		0x0200
7457987d16Sclaudio #define MOD_MERGE_EXCLUDE_FILE		0x0400
7557987d16Sclaudio #define MOD_MERGE_NO_INHERIT		0x0800
7657987d16Sclaudio #define MOD_MERGE_WORDSPLIT		0x1000
7757987d16Sclaudio 
7857987d16Sclaudio /* maybe support absolute and negate */
7957987d16Sclaudio const struct modifier {
8057987d16Sclaudio 	unsigned int		modifier;
8157987d16Sclaudio 	char			sopt;
8257987d16Sclaudio } modifiers[] = {
8357987d16Sclaudio 	{ MOD_ABSOLUTE,			'/' },
8457987d16Sclaudio 	{ MOD_NEGATE,			'!' },
8557987d16Sclaudio 	{ MOD_CVSEXCLUDE,		'C' },
8657987d16Sclaudio 	{ MOD_SENDING,			's' },
8757987d16Sclaudio 	{ MOD_RECEIVING,		'r' },
8857987d16Sclaudio 	{ MOD_PERISHABLE,		'p' },
8957987d16Sclaudio 	{ MOD_XATTR,			'x' },
9057987d16Sclaudio 	/* for '.' and ':' types */
9157987d16Sclaudio 	{ MOD_MERGE_EXCLUDE,		'-' },
9257987d16Sclaudio 	{ MOD_MERGE_INCLUDE,		'+' },
9357987d16Sclaudio 	{ MOD_MERGE_CVSCOMPAT,		'C' },
9457987d16Sclaudio 	{ MOD_MERGE_EXCLUDE_FILE,	'e' },
9557987d16Sclaudio 	{ MOD_MERGE_NO_INHERIT,		'n' },
9657987d16Sclaudio 	{ MOD_MERGE_WORDSPLIT,		'w' },
9757987d16Sclaudio 	{ 0 }
9857987d16Sclaudio }
9957987d16Sclaudio #endif
10057987d16Sclaudio 
10157987d16Sclaudio static struct rule *
10257987d16Sclaudio get_next_rule(void)
10357987d16Sclaudio {
10457987d16Sclaudio 	struct rule *new;
10557987d16Sclaudio 	size_t newsz;
10657987d16Sclaudio 
10757987d16Sclaudio 	if (++numrules > rulesz) {
10857987d16Sclaudio 		if (rulesz == 0)
10957987d16Sclaudio 			newsz = 16;
11057987d16Sclaudio 		else
11157987d16Sclaudio 			newsz = rulesz * 2;
11257987d16Sclaudio 
11357987d16Sclaudio 		new = recallocarray(rules, rulesz, newsz, sizeof(*rules));
11457987d16Sclaudio 		if (new == NULL)
11557987d16Sclaudio 			err(ERR_NOMEM, NULL);
11657987d16Sclaudio 
11757987d16Sclaudio 		rules = new;
11857987d16Sclaudio 		rulesz = newsz;
11957987d16Sclaudio 	}
12057987d16Sclaudio 
12157987d16Sclaudio 	return rules + numrules - 1;
12257987d16Sclaudio }
12357987d16Sclaudio 
12457987d16Sclaudio static enum rule_type
12557987d16Sclaudio parse_command(const char *command, size_t len)
12657987d16Sclaudio {
12757987d16Sclaudio 	const char *mod;
12857987d16Sclaudio 	size_t	i;
12957987d16Sclaudio 
13057987d16Sclaudio 	mod = memchr(command, ',', len);
13157987d16Sclaudio 	if (mod != NULL) {
13257987d16Sclaudio 		/* XXX modifiers not yet implemented */
13357987d16Sclaudio 		return RULE_NONE;
13457987d16Sclaudio 	}
13557987d16Sclaudio 
13657987d16Sclaudio 	for (i = 0; commands[i].type != RULE_NONE; i++) {
13757987d16Sclaudio 		if (strncmp(commands[i].lopt, command, len) == 0)
13857987d16Sclaudio 			return commands[i].type;
13957987d16Sclaudio 		if (len == 1 && commands[i].sopt == *command)
14057987d16Sclaudio 			return commands[i].type;
14157987d16Sclaudio 	}
14257987d16Sclaudio 
14357987d16Sclaudio 	return RULE_NONE;
14457987d16Sclaudio }
14557987d16Sclaudio 
14657987d16Sclaudio static void
14757987d16Sclaudio parse_pattern(struct rule *r, char *pattern)
14857987d16Sclaudio {
14957987d16Sclaudio 	size_t plen;
15057987d16Sclaudio 	char *p;
15157987d16Sclaudio 	short nseg = 1;
15257987d16Sclaudio 
15357987d16Sclaudio 	/*
15457987d16Sclaudio 	 * check for / at start and end of pattern both are special and
15557987d16Sclaudio 	 * can bypass full path matching.
15657987d16Sclaudio 	 */
15757987d16Sclaudio 	if (*pattern == '/') {
15857987d16Sclaudio 		pattern++;
15957987d16Sclaudio 		r->anchored = 1;
16057987d16Sclaudio 	}
16157987d16Sclaudio 	plen = strlen(pattern);
16257987d16Sclaudio 	/*
16357987d16Sclaudio 	 * check for patterns ending in '/' and '/'+'***' and handle them
16457987d16Sclaudio 	 * specially. Because of this and the check above pattern will never
16557987d16Sclaudio 	 * start or end with a '/'.
16657987d16Sclaudio 	 */
16757987d16Sclaudio 	if (plen > 1 && pattern[plen - 1] == '/') {
16857987d16Sclaudio 		r->onlydir = 1;
16957987d16Sclaudio 		pattern[plen - 1] = '\0';
17057987d16Sclaudio 	}
17157987d16Sclaudio 	if (plen > 4 && strcmp(pattern + plen - 4, "/***") == 0) {
17257987d16Sclaudio 		r->leadingdir = 1;
17357987d16Sclaudio 		pattern[plen - 4] = '\0';
17457987d16Sclaudio 	}
17557987d16Sclaudio 
17657987d16Sclaudio 	/* count how many segments the pattern has. */
17757987d16Sclaudio 	for (p = pattern; *p != '\0'; p++)
17857987d16Sclaudio 		if (*p == '/')
17957987d16Sclaudio 			nseg++;
18057987d16Sclaudio 	r->numseg = nseg;
18157987d16Sclaudio 
18257987d16Sclaudio 	/* check if this pattern only matches against the basename */
18357987d16Sclaudio 	if (nseg == 1 && !r->anchored)
18457987d16Sclaudio 		r->fileonly = 1;
18557987d16Sclaudio 
18657987d16Sclaudio 	if (strpbrk(pattern, "*?[") == NULL) {
18757987d16Sclaudio 		/* no wildchar matching */
18857987d16Sclaudio 		r->nowild = 1;
18957987d16Sclaudio 	} else {
19057987d16Sclaudio 		/* requires wildchar matching */
19157987d16Sclaudio 		if (strstr(pattern, "**") != NULL)
19257987d16Sclaudio 			r->numseg = -1;
19357987d16Sclaudio 	}
19457987d16Sclaudio 
19557987d16Sclaudio 	r->pattern = strdup(pattern);
19657987d16Sclaudio 	if (r->pattern == NULL)
19757987d16Sclaudio 		err(ERR_NOMEM, NULL);
19857987d16Sclaudio }
19957987d16Sclaudio 
20057987d16Sclaudio int
20157987d16Sclaudio parse_rule(char *line, enum rule_type def)
20257987d16Sclaudio {
20357987d16Sclaudio 	enum rule_type type;
20457987d16Sclaudio 	struct rule *r;
20557987d16Sclaudio 	char *pattern;
20657987d16Sclaudio 	size_t len;
20757987d16Sclaudio 
20857987d16Sclaudio 	switch (*line) {
20957987d16Sclaudio 	case '#':
21057987d16Sclaudio 	case ';':
21157987d16Sclaudio 		/* comment */
21257987d16Sclaudio 		return 0;
21357987d16Sclaudio 	case '\0':
214*d9a51c35Sjmc 		/* ignore empty lines */
21557987d16Sclaudio 		return 0;
21657987d16Sclaudio 	default:
21757987d16Sclaudio 		len = strcspn(line, " _");
21857987d16Sclaudio 		type = parse_command(line, len);
21957987d16Sclaudio 		if (type == RULE_NONE) {
22057987d16Sclaudio 			if (def == RULE_NONE)
22157987d16Sclaudio 				return -1;
22257987d16Sclaudio 			type = def;
22357987d16Sclaudio 			pattern = line;
22457987d16Sclaudio 		} else
22557987d16Sclaudio 			pattern = line + len + 1;
22657987d16Sclaudio 
22757987d16Sclaudio 		if (*pattern == '\0' && type != RULE_CLEAR)
22857987d16Sclaudio 			return -1;
22957987d16Sclaudio 		if (*pattern != '\0' && type == RULE_CLEAR)
23057987d16Sclaudio 			return -1;
23157987d16Sclaudio 		break;
23257987d16Sclaudio 	}
23357987d16Sclaudio 
23457987d16Sclaudio 	r = get_next_rule();
23557987d16Sclaudio 	r->type = type;
23657987d16Sclaudio 	parse_pattern(r, pattern);
23757987d16Sclaudio 
23857987d16Sclaudio 	return 0;
23957987d16Sclaudio }
24057987d16Sclaudio 
24157987d16Sclaudio void
2420345af14Sclaudio parse_file(const char *file, enum rule_type def)
24357987d16Sclaudio {
24457987d16Sclaudio 	FILE *fp;
24557987d16Sclaudio 	char *line = NULL;
24657987d16Sclaudio 	size_t linesize = 0, linenum = 0;
24757987d16Sclaudio 	ssize_t linelen;
24857987d16Sclaudio 
24957987d16Sclaudio 	if ((fp = fopen(file, "r")) == NULL)
25057987d16Sclaudio 		err(ERR_SYNTAX, "open: %s", file);
25157987d16Sclaudio 
2520345af14Sclaudio 	while ((linelen = getline(&line, &linesize, fp)) != -1) {
25357987d16Sclaudio 		linenum++;
25457987d16Sclaudio 		line[linelen - 1] = '\0';
25557987d16Sclaudio 		if (parse_rule(line, def) == -1)
25657987d16Sclaudio 			errx(ERR_SYNTAX, "syntax error in %s at entry %zu",
25757987d16Sclaudio 			    file, linenum);
25857987d16Sclaudio 	}
25957987d16Sclaudio 
26057987d16Sclaudio 	free(line);
26157987d16Sclaudio 	if (ferror(fp))
26257987d16Sclaudio 		err(ERR_SYNTAX, "failed to parse file %s", file);
26357987d16Sclaudio 	fclose(fp);
26457987d16Sclaudio }
26557987d16Sclaudio 
26657987d16Sclaudio static const char *
26757987d16Sclaudio send_command(struct rule *r)
26857987d16Sclaudio {
26957987d16Sclaudio 	static char buf[16];
27057987d16Sclaudio 	char *b = buf;
27157987d16Sclaudio 	char *ep = buf + sizeof(buf);
27257987d16Sclaudio 
27357987d16Sclaudio 	switch (r->type) {
27457987d16Sclaudio 	case RULE_EXCLUDE:
27557987d16Sclaudio 		*b++ = '-';
27657987d16Sclaudio 		break;
27757987d16Sclaudio 	case RULE_INCLUDE:
27857987d16Sclaudio 		*b++ = '+';
27957987d16Sclaudio 		break;
28057987d16Sclaudio 	case RULE_CLEAR:
28157987d16Sclaudio 		*b++ = '!';
28257987d16Sclaudio 		break;
28357987d16Sclaudio #ifdef NOTYET
28457987d16Sclaudio 	case RULE_MERGE:
28557987d16Sclaudio 		*b++ = '.';
28657987d16Sclaudio 		break;
28757987d16Sclaudio 	case RULE_DIR_MERGE:
28857987d16Sclaudio 		*b++ = ':';
28957987d16Sclaudio 		break;
29057987d16Sclaudio 	case RULE_SHOW:
29157987d16Sclaudio 		*b++ = 'S';
29257987d16Sclaudio 		break;
29357987d16Sclaudio 	case RULE_HIDE:
29457987d16Sclaudio 		*b++ = 'H';
29557987d16Sclaudio 		break;
29657987d16Sclaudio 	case RULE_PROTECT:
29757987d16Sclaudio 		*b++ = 'P';
29857987d16Sclaudio 		break;
29957987d16Sclaudio 	case RULE_RISK:
30057987d16Sclaudio 		*b++ = 'R';
30157987d16Sclaudio 		break;
30257987d16Sclaudio #endif
30357987d16Sclaudio 	default:
30457987d16Sclaudio 		err(ERR_SYNTAX, "unknown rule type %d", r->type);
30557987d16Sclaudio 	}
30657987d16Sclaudio 
30757987d16Sclaudio #ifdef NOTYET
30857987d16Sclaudio 	for (i = 0; modifiers[i].modifier != 0; i++) {
30957987d16Sclaudio 		if (rule->modifiers & modifiers[i].modifier)
31057987d16Sclaudio 			*b++ = modifiers[i].sopt;
31157987d16Sclaudio 		if (b >= ep - 3)
31257987d16Sclaudio 			err(ERR_SYNTAX, "rule modifiers overflow");
31357987d16Sclaudio 	}
31457987d16Sclaudio #endif
31557987d16Sclaudio 	if (b >= ep - 3)
31657987d16Sclaudio 		err(ERR_SYNTAX, "rule prefix overflow");
31757987d16Sclaudio 	*b++ = ' ';
31857987d16Sclaudio 
31957987d16Sclaudio 	/* include the stripped root '/' for anchored patterns */
32057987d16Sclaudio 	if (r->anchored)
32157987d16Sclaudio 		*b++ = '/';
32257987d16Sclaudio 	*b++ = '\0';
32357987d16Sclaudio 	return buf;
32457987d16Sclaudio }
32557987d16Sclaudio 
32657987d16Sclaudio static const char *
32757987d16Sclaudio postfix_command(struct rule *r)
32857987d16Sclaudio {
32957987d16Sclaudio 	static char buf[8];
33057987d16Sclaudio 
33157987d16Sclaudio 	buf[0] = '\0';
33257987d16Sclaudio 	if (r->onlydir)
33357987d16Sclaudio 		strlcpy(buf, "/", sizeof(buf));
33457987d16Sclaudio 	if (r->leadingdir)
33557987d16Sclaudio 		strlcpy(buf, "/***", sizeof(buf));
33657987d16Sclaudio 
33757987d16Sclaudio 	return buf;
33857987d16Sclaudio }
33957987d16Sclaudio 
34057987d16Sclaudio void
34157987d16Sclaudio send_rules(struct sess *sess, int fd)
34257987d16Sclaudio {
34357987d16Sclaudio 	const char *cmd;
34457987d16Sclaudio 	const char *postfix;
34557987d16Sclaudio 	struct rule *r;
34657987d16Sclaudio 	size_t cmdlen, len, postlen, i;
34757987d16Sclaudio 
34857987d16Sclaudio 	for (i = 0; i < numrules; i++) {
34957987d16Sclaudio 		r = &rules[i];
35057987d16Sclaudio 		cmd = send_command(r);
35157987d16Sclaudio 		if (cmd == NULL)
35257987d16Sclaudio 			err(ERR_PROTOCOL,
35357987d16Sclaudio 			    "rules are incompatible with remote rsync");
35457987d16Sclaudio 		postfix = postfix_command(r);
35557987d16Sclaudio 		cmdlen = strlen(cmd);
35657987d16Sclaudio 		len = strlen(r->pattern);
35757987d16Sclaudio 		postlen = strlen(postfix);
35857987d16Sclaudio 
35957987d16Sclaudio 		if (!io_write_int(sess, fd, cmdlen + len + postlen))
36057987d16Sclaudio 			err(ERR_SOCK_IO, "send rules");
36157987d16Sclaudio 		if (!io_write_buf(sess, fd, cmd, cmdlen))
36257987d16Sclaudio 			err(ERR_SOCK_IO, "send rules");
36357987d16Sclaudio 		if (!io_write_buf(sess, fd, r->pattern, len))
36457987d16Sclaudio 			err(ERR_SOCK_IO, "send rules");
36557987d16Sclaudio 		/* include the '/' stripped by onlydir */
36657987d16Sclaudio 		if (postlen > 0)
36757987d16Sclaudio 			if (!io_write_buf(sess, fd, postfix, postlen))
36857987d16Sclaudio 				err(ERR_SOCK_IO, "send rules");
36957987d16Sclaudio 	}
37057987d16Sclaudio 
37157987d16Sclaudio 	if (!io_write_int(sess, fd, 0))
37257987d16Sclaudio 		err(ERR_SOCK_IO, "send rules");
37357987d16Sclaudio }
37457987d16Sclaudio 
37557987d16Sclaudio void
37657987d16Sclaudio recv_rules(struct sess *sess, int fd)
37757987d16Sclaudio {
37857987d16Sclaudio 	char line[8192];
37957987d16Sclaudio 	size_t len;
38057987d16Sclaudio 
38157987d16Sclaudio 	do {
38257987d16Sclaudio 		if (!io_read_size(sess, fd, &len))
38357987d16Sclaudio 			err(ERR_SOCK_IO, "receive rules");
38457987d16Sclaudio 
38557987d16Sclaudio 		if (len == 0)
38657987d16Sclaudio 			return;
38757987d16Sclaudio 		if (len >= sizeof(line) - 1)
38857987d16Sclaudio 			errx(ERR_SOCK_IO, "received rule too long");
38957987d16Sclaudio 		if (!io_read_buf(sess, fd, line, len))
39057987d16Sclaudio 			err(ERR_SOCK_IO, "receive rules");
39157987d16Sclaudio 		line[len] = '\0';
39257987d16Sclaudio 		if (parse_rule(line, RULE_NONE) == -1)
39357987d16Sclaudio 			errx(ERR_PROTOCOL, "syntax error in received rules");
39457987d16Sclaudio 	} while (1);
39557987d16Sclaudio }
39657987d16Sclaudio 
39757987d16Sclaudio static inline int
39857987d16Sclaudio rule_matched(struct rule *r)
39957987d16Sclaudio {
40057987d16Sclaudio 	/* TODO apply negation once modifiers are added */
40157987d16Sclaudio 
40257987d16Sclaudio 	if (r->type == RULE_EXCLUDE)
40357987d16Sclaudio 		return -1;
40457987d16Sclaudio 	else
40557987d16Sclaudio 		return 1;
40657987d16Sclaudio }
40757987d16Sclaudio 
40857987d16Sclaudio int
40957987d16Sclaudio rules_match(const char *path, int isdir)
41057987d16Sclaudio {
41157987d16Sclaudio 	const char *basename, *p = NULL;
41257987d16Sclaudio 	struct rule *r;
41357987d16Sclaudio 	size_t i;
41457987d16Sclaudio 
41557987d16Sclaudio 	basename = strrchr(path, '/');
41657987d16Sclaudio 	if (basename != NULL)
41757987d16Sclaudio 		basename += 1;
41857987d16Sclaudio 	else
41957987d16Sclaudio 		basename = path;
42057987d16Sclaudio 
42157987d16Sclaudio 	for (i = 0; i < numrules; i++) {
42257987d16Sclaudio 		r = &rules[i];
42357987d16Sclaudio 
42457987d16Sclaudio 		if (r->onlydir && !isdir)
42557987d16Sclaudio 			continue;
42657987d16Sclaudio 
42757987d16Sclaudio 		if (r->nowild) {
42857987d16Sclaudio 			/* fileonly and anchored are mutually exclusive */
42957987d16Sclaudio 			if (r->fileonly) {
43057987d16Sclaudio 				if (strcmp(basename, r->pattern) == 0)
43157987d16Sclaudio 					return rule_matched(r);
43257987d16Sclaudio 			} else if (r->anchored) {
43357987d16Sclaudio 				/*
43457987d16Sclaudio 				 * assumes that neither path nor pattern
43557987d16Sclaudio 				 * start with a '/'.
43657987d16Sclaudio 				 */
43757987d16Sclaudio 				if (strcmp(path, r->pattern) == 0)
43857987d16Sclaudio 					return rule_matched(r);
43957987d16Sclaudio 			} else if (r->leadingdir) {
44057987d16Sclaudio 				size_t plen = strlen(r->pattern);
44157987d16Sclaudio 
44257987d16Sclaudio 				p = strstr(path, r->pattern);
44357987d16Sclaudio 				/*
44457987d16Sclaudio 				 * match from start or dir boundary also
44557987d16Sclaudio 				 * match to end or to dir boundary
44657987d16Sclaudio 				 */
44757987d16Sclaudio 				if (p != NULL && (p == path || p[-1] == '/') &&
44857987d16Sclaudio 				    (p[plen] == '\0' || p[plen] == '/'))
44957987d16Sclaudio 					return rule_matched(r);
45057987d16Sclaudio 			} else {
45157987d16Sclaudio 				size_t len = strlen(path);
45257987d16Sclaudio 				size_t plen = strlen(r->pattern);
45357987d16Sclaudio 
45457987d16Sclaudio 				if (len >= plen && strcmp(path + len - plen,
45557987d16Sclaudio 				    r->pattern) == 0) {
45657987d16Sclaudio 					/* match all or start on dir boundary */
45757987d16Sclaudio 					if (len == plen ||
45857987d16Sclaudio 					    path[len - plen - 1] == '/')
45957987d16Sclaudio 						return rule_matched(r);
46057987d16Sclaudio 				}
46157987d16Sclaudio 			}
46257987d16Sclaudio 		} else {
46357987d16Sclaudio 			if (r->fileonly) {
46457987d16Sclaudio 				p = basename;
46557987d16Sclaudio 			} else if (r->anchored || r->numseg == -1) {
46657987d16Sclaudio 				/* full path matching */
46757987d16Sclaudio 				p = path;
46857987d16Sclaudio 			} else {
46957987d16Sclaudio 				short nseg = 1;
47057987d16Sclaudio 
47157987d16Sclaudio 				/* match against the last numseg elements */
47257987d16Sclaudio 				for (p = path; *p != '\0'; p++)
47357987d16Sclaudio 					if (*p == '/')
47457987d16Sclaudio 						nseg++;
47557987d16Sclaudio 				if (nseg < r->numseg) {
47657987d16Sclaudio 					p = NULL;
47757987d16Sclaudio 				} else {
47857987d16Sclaudio 					nseg -= r->numseg;
47957987d16Sclaudio 					for (p = path; *p != '\0' && nseg > 0;
48057987d16Sclaudio 					    p++) {
48157987d16Sclaudio 						if (*p == '/')
48257987d16Sclaudio 							nseg--;
48357987d16Sclaudio 					}
48457987d16Sclaudio 				}
48557987d16Sclaudio 			}
48657987d16Sclaudio 
48757987d16Sclaudio 			if (p != NULL) {
48857987d16Sclaudio 				if (rmatch(r->pattern, p, r->leadingdir) == 0)
48957987d16Sclaudio 					return rule_matched(r);
49057987d16Sclaudio 			}
49157987d16Sclaudio 		}
49257987d16Sclaudio 	}
49357987d16Sclaudio 
49457987d16Sclaudio 	return 0;
49557987d16Sclaudio }
496