1 /* direvent - directory content watcher daemon
2    Copyright (C) 2012-2016 Sergey Poznyakoff
3 
4    Direvent is free software; you can redistribute it and/or modify it
5    under the terms of the GNU General Public License as published by the
6    Free Software Foundation; either version 3 of the License, or (at your
7    option) any later version.
8 
9    Direvent is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with direvent. If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include "direvent.h"
18 #include <fnmatch.h>
19 #include <grecs.h>
20 
21 static void
filename_pattern_free(void * p)22 filename_pattern_free(void *p)
23 {
24 	struct filename_pattern *pat = p;
25 	switch (pat->type) {
26 	case PAT_EXACT:
27 	case PAT_GLOB:
28 		free(pat->v.glob);
29 		break;
30 	case PAT_REGEX:
31 		regfree(&pat->v.re);
32 	}
33 	free(pat);
34 }
35 
36 struct filpatlist {
37 	grecs_list_ptr_t list;
38 };
39 
40 static int
is_glob(char const * str)41 is_glob(char const *str)
42 {
43 	return strcspn(str, "[]*?") < strlen(str);
44 }
45 
46 void
filpatlist_add_pattern(filpatlist_t * fptr,struct filename_pattern * pat)47 filpatlist_add_pattern(filpatlist_t *fptr, struct filename_pattern *pat)
48 {
49 	grecs_list_ptr_t list;
50 	if (!*fptr) {
51 		*fptr = emalloc(sizeof(*fptr));
52 		(*fptr)->list = grecs_list_create();
53 		(*fptr)->list->free_entry = filename_pattern_free;
54 	}
55 	list = (*fptr)->list;
56 	grecs_list_append(list, pat);
57 }
58 
59 void
filpatlist_add_exact(filpatlist_t * fptr,char const * arg)60 filpatlist_add_exact(filpatlist_t *fptr, char const *arg)
61 {
62 	struct filename_pattern *pat = emalloc(sizeof(*pat));
63 
64 	pat->neg = 0;
65 	pat->type = PAT_EXACT;
66 	pat->v.glob = estrdup(arg);
67 	filpatlist_add_pattern(fptr, pat);
68 }
69 
70 int
filpatlist_add(filpatlist_t * fptr,char const * arg,grecs_locus_t * loc)71 filpatlist_add(filpatlist_t *fptr, char const *arg, grecs_locus_t *loc)
72 {
73 	int flags = REG_EXTENDED|REG_NOSUB;
74 	struct filename_pattern *pat;
75 
76 	pat = emalloc(sizeof(*pat));
77 	if (*arg == '!') {
78 		pat->neg = 1;
79 		++arg;
80 	} else
81 		pat->neg = 0;
82 	if (arg[0] == '/') {
83 		int rc;
84 		char *q, *p;
85 
86 		pat->type = PAT_REGEX;
87 
88 		p = strchr(arg+1, '/');
89 		if (!p) {
90 			grecs_error(loc, 0, _("unterminated regexp"));
91 			free(pat);
92 			return 1;
93 		}
94 		for (q = p + 1; *q; q++) {
95 			switch (*q) {
96 			case 'b':
97 				flags &= ~REG_EXTENDED;
98 				break;
99 			case 'i':
100 				flags |= REG_ICASE;
101 				break;
102 			default:
103 				grecs_error(loc, 0,
104 					    _("unrecognized flag: %c"), *q);
105 				free(pat);
106 				return 1;
107 			}
108 		}
109 
110 		*p = 0;
111 		rc = regcomp(&pat->v.re, arg + 1, flags);
112 		*p = '/';
113 
114 		if (rc) {
115 			char errbuf[128];
116 			regerror(rc, &pat->v.re, errbuf, sizeof(errbuf));
117 			grecs_error(loc, 0, "%s", errbuf);
118 			filename_pattern_free(pat);
119 			return 1;
120 		}
121 	} else {
122 		pat->type = is_glob(arg) ? PAT_GLOB : PAT_EXACT;
123 		pat->v.glob = estrdup(arg);
124 	}
125 	filpatlist_add_pattern(fptr, pat);
126 	return 0;
127 }
128 
129 void
filpatlist_destroy(filpatlist_t * fptr)130 filpatlist_destroy(filpatlist_t *fptr)
131 {
132 	if (fptr && *fptr) {
133 		grecs_list_free((*fptr)->list);
134 		free(*fptr);
135 		*fptr = NULL;
136 	}
137 }
138 
139 int
filpatlist_is_empty(filpatlist_t fp)140 filpatlist_is_empty(filpatlist_t fp)
141 {
142 	if (!fp)
143 		return 1;
144 	return grecs_list_size(fp->list) == 0;
145 }
146 
147 int
filpatlist_match(filpatlist_t fp,const char * name)148 filpatlist_match(filpatlist_t fp, const char *name)
149 {
150 	struct grecs_list_entry *ep;
151 
152 	if (!fp || !fp->list)
153 		return 0;
154 	for (ep = fp->list->head; ep; ep = ep->next) {
155 		struct filename_pattern *pat = ep->data;
156 		int rc;
157 
158 		switch (pat->type) {
159 		case PAT_EXACT:
160 			rc = strcmp(pat->v.glob, name);
161 			break;
162 		case PAT_GLOB:
163 			rc = fnmatch(pat->v.glob, name, FNM_PATHNAME);
164 			break;
165 		case PAT_REGEX:
166 			rc = regexec(&pat->v.re, name, 0, NULL, 0);
167 			break;
168 		}
169 		if (pat->neg)
170 			rc = !rc;
171 		if (rc == 0)
172 			return 0;
173 	}
174 	return 1;
175 }
176