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