Commit df608ed0 df608ed0cfb5f0db57d1ff97eb208ceb46f06e17 by Sergey Poznyakoff

New API for converting globbing patterns to extended POSIX regex

* include/mailutils/opool.h (mu_nonlocal_jmp_t): New type.
(mu_opool_setjmp,mu_opool_clrjmp): New functions.
(mu_opool_setup_nonlocal_jump): New macro.
* libmailutils/base/opool.c (_mu_opool)<jmp>: New field.
(alloc_bucket): Do a non-local jump on out of memory condition,
if jmp is not NULL.
(mu_opool_setjmp,mu_opool_clrjmp): New functions.

* libmailutils/base/glob.c: New file.
* libmailutils/base/Makefile.am: Add glob.c

* include/mailutils/glob.h: New file.
* include/mailutils/mailutils.h: Include glob.h

* libmailutils/tests/globtest.c: New file.
* libmailutils/tests/globtest.at: New test.
* libmailutils/tests/Makefile.am: Add new files.
* libmailutils/tests/testsuite.at: Include new test.
1 parent f8a0fd0f
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
32 #include <mailutils/error.h> 32 #include <mailutils/error.h>
33 #include <mailutils/filter.h> 33 #include <mailutils/filter.h>
34 #include <mailutils/folder.h> 34 #include <mailutils/folder.h>
35 #include <mailutils/glob.h>
35 #include <mailutils/header.h> 36 #include <mailutils/header.h>
36 #include <mailutils/iterator.h> 37 #include <mailutils/iterator.h>
37 #include <mailutils/kwd.h> 38 #include <mailutils/kwd.h>
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
19 #define _MAILUTILS_OPOOL_H 19 #define _MAILUTILS_OPOOL_H
20 20
21 #include <mailutils/types.h> 21 #include <mailutils/types.h>
22 #include <setjmp.h>
22 23
23 #ifndef MU_OPOOL_BUCKET_SIZE 24 #ifndef MU_OPOOL_BUCKET_SIZE
24 # define MU_OPOOL_BUCKET_SIZE 1024 25 # define MU_OPOOL_BUCKET_SIZE 1024
...@@ -33,6 +34,27 @@ int mu_opool_create (mu_opool_t *pret, int flags); ...@@ -33,6 +34,27 @@ int mu_opool_create (mu_opool_t *pret, int flags);
33 int mu_opool_set_bucket_size (mu_opool_t opool, size_t size); 34 int mu_opool_set_bucket_size (mu_opool_t opool, size_t size);
34 int mu_opool_get_bucket_size (mu_opool_t opool, size_t *psize); 35 int mu_opool_get_bucket_size (mu_opool_t opool, size_t *psize);
35 36
37 struct mu_nonlocal_jmp
38 {
39 jmp_buf buf;
40 struct mu_nonlocal_jmp *next;
41 };
42
43 typedef struct mu_nonlocal_jmp mu_nonlocal_jmp_t;
44
45 void mu_opool_setjmp (mu_opool_t opool, mu_nonlocal_jmp_t *err);
46 void mu_opool_clrjmp (mu_opool_t opool);
47
48 #define mu_opool_setup_nonlocal_jump(p,jb) \
49 do \
50 { \
51 int __rc = setjmp (jb.buf); \
52 if (__rc) \
53 return __rc; \
54 mu_opool_setjmp (p, &jb); \
55 } \
56 while (0)
57
36 /* Merge all data from *SRC into *DST. If the latter is NULL, create 58 /* Merge all data from *SRC into *DST. If the latter is NULL, create
37 it. On success, free *SRC and initialize it with NULL. */ 59 it. On success, free *SRC and initialize it with NULL. */
38 int mu_opool_union (mu_opool_t *dst, mu_opool_t *src); 60 int mu_opool_union (mu_opool_t *dst, mu_opool_t *src);
......
...@@ -34,6 +34,7 @@ libbase_la_SOURCES = \ ...@@ -34,6 +34,7 @@ libbase_la_SOURCES = \
34 getcwd.c\ 34 getcwd.c\
35 getmaxfd.c\ 35 getmaxfd.c\
36 getpass.c\ 36 getpass.c\
37 glob.c\
37 hostname.c\ 38 hostname.c\
38 iterator.c\ 39 iterator.c\
39 kwd.c\ 40 kwd.c\
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2007, 2009-2012, 2014-2016 Free Software Foundation,
3 Inc.
4
5 GNU Mailutils is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3, or (at your option)
8 any later version.
9
10 GNU Mailutils is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
17
18 #if HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <mailutils/opool.h>
22 #include <mailutils/error.h>
23 #include <mailutils/errno.h>
24 #include <mailutils/glob.h>
25 #include <regex.h>
26 #include <string.h>
27 #include <stdlib.h>
28
29 static void
30 parse_character_class (unsigned char const *str, mu_opool_t pool,
31 unsigned char const **endp)
32 {
33 unsigned char const *cur;
34
35 cur = str + 1;
36 if (*cur == '!')
37 cur++;
38 if (*cur == ']')
39 cur++;
40
41 while (*cur && *cur != ']')
42 {
43 int c = *cur++;
44 if (c == '\\')
45 cur++;
46 else if (c >= 0xc2)
47 {
48 size_t len;
49
50 if (c < 0xe0)
51 len = 1;
52 else if (c < 0xf0)
53 len = 2;
54 else if (c < 0xf8)
55 len = 3;
56 else
57 /* Invalid UTF-8 sequence; skip. */
58 continue;
59
60 while (len-- && *cur)
61 cur++;
62 }
63 }
64
65 if (*cur == ']')
66 {
67 /* Valid character class */
68 mu_opool_append_char (pool, *str);
69 str++;
70 if (*str == '!')
71 {
72 mu_opool_append_char (pool, '^');
73 str++;
74 }
75 while (str < cur)
76 {
77 if (*str == '[')
78 mu_opool_append_char (pool, '\\');
79
80 mu_opool_append_char (pool, *str);
81 str++;
82 }
83 mu_opool_append_char (pool, ']');
84 *endp = cur + 1;
85 }
86 else
87 {
88 mu_opool_append_char (pool, '\\');
89 mu_opool_append_char (pool, *str);
90 str++;
91 *endp = str;
92 }
93 }
94
95 int
96 mu_glob_to_regex_opool (char const *pattern, mu_opool_t pool, int flags)
97 {
98 unsigned char const *str = (unsigned char const *) pattern;
99 mu_nonlocal_jmp_t jmp;
100
101 if (!(flags & MU_GLOBF_SUB))
102 flags |= MU_GLOBF_COLLAPSE;
103
104 mu_opool_setup_nonlocal_jump (pool, jmp);
105
106 while (*str)
107 {
108 int c = *str++;
109
110 if (c < 0x80)
111 {
112 switch (c)
113 {
114 case '\\':
115 mu_opool_append_char (pool, '\\');
116 if (*str && strchr ("?*[", *str))
117 {
118 mu_opool_append_char (pool, *str);
119 str++;
120 }
121 else
122 mu_opool_append_char (pool, '\\');
123 break;
124
125 case '?':
126 if (flags & MU_GLOBF_SUB)
127 mu_opool_append_char (pool, '(');
128 mu_opool_append_char (pool, '.');
129 if (flags & MU_GLOBF_SUB)
130 mu_opool_append_char (pool, ')');
131 break;
132
133 case '*':
134 if (flags & MU_GLOBF_COLLAPSE)
135 {
136 while (*str == '*')
137 str++;
138 }
139
140 if (flags & MU_GLOBF_SUB)
141 {
142 while (*str == '*')
143 {
144 mu_opool_append (pool, "()", 2);
145 str++;
146 }
147 mu_opool_append_char (pool, '(');
148 mu_opool_append (pool, ".*", 2);
149 mu_opool_append_char (pool, ')');
150 }
151 else
152 mu_opool_append (pool, ".*", 2);
153 break;
154
155 case '[':
156 parse_character_class (str - 1, pool, &str);
157 break;
158
159 case '(':
160 case ')':
161 case '{':
162 case '}':
163 case '^':
164 case '$':
165 case ']':
166 case '|':
167 case '.':
168 mu_opool_append_char (pool, '\\');
169 mu_opool_append_char (pool, c);
170 break;
171
172 default:
173 mu_opool_append_char (pool, c);
174 }
175 }
176 else
177 {
178 mu_opool_append_char (pool, c);
179 if (c >= 0xc2)
180 {
181 size_t len;
182
183 if (c < 0xe0)
184 len = 1;
185 else if (c < 0xf0)
186 len = 2;
187 else if (c < 0xf8)
188 len = 3;
189 else
190 /* Invalid UTF-8 sequence; skip. */
191 continue;
192
193 for (; len-- && *str; str++)
194 mu_opool_append_char (pool, *str);
195 }
196 }
197 }
198 mu_opool_clrjmp (pool);
199 return 0;
200 }
201
202 int
203 mu_glob_to_regex (char **rxstr, char const *pattern, int flags)
204 {
205 mu_opool_t pool;
206 int rc;
207 mu_nonlocal_jmp_t jmp;
208
209 rc = mu_opool_create (&pool, MU_OPOOL_DEFAULT);
210 if (rc)
211 return rc;
212 mu_opool_setup_nonlocal_jump (pool, jmp);
213 mu_opool_append_char (pool, '^');
214 rc = mu_glob_to_regex_opool (pattern, pool, flags);
215 if (rc == 0)
216 {
217 mu_opool_append_char (pool, '$');
218 mu_opool_append_char (pool, 0);
219 *rxstr = mu_opool_detach (pool, NULL);
220 }
221 mu_opool_clrjmp (pool);
222 mu_opool_destroy (&pool);
223 return rc;
224 }
225
226 int
227 mu_glob_compile (regex_t *rx, char const *pattern, int flags)
228 {
229 char *str;
230 int rc;
231 int rxflags;
232
233 rc = mu_glob_to_regex (&str, pattern, flags);
234 if (rc)
235 return rc;
236
237 rxflags = REG_EXTENDED;
238 if (flags & MU_GLOBF_ICASE)
239 rxflags |= REG_ICASE;
240 if (!(flags & MU_GLOBF_SUB))
241 rxflags |= REG_NOSUB;
242
243 rc = regcomp (rx, str, rxflags);
244 if (rc)
245 {
246 size_t size = regerror (rc, rx, NULL, 0);
247 char *errbuf = malloc (size + 1);
248 if (errbuf)
249 {
250 regerror (rc, rx, errbuf, size);
251 mu_error ("INTERNAL ERROR: can't compile regular expression \"%s\": %s",
252 str, mu_strerror (rc));
253 }
254 else
255 mu_error ("INTERNAL ERROR: can't compile regular expression \"%s\"",
256 str);
257 mu_error ("INTERNAL ERROR: expression compiled from globbing pattern: %s",
258 pattern);
259 free (errbuf);
260 }
261 free (str);
262
263 return rc;
264 }
...@@ -53,6 +53,7 @@ struct _mu_opool ...@@ -53,6 +53,7 @@ struct _mu_opool
53 int flags; /* Flag bits */ 53 int flags; /* Flag bits */
54 size_t bucket_size; /* Default bucket size */ 54 size_t bucket_size; /* Default bucket size */
55 size_t itr_count; /* Number of iterators created for this pool */ 55 size_t itr_count; /* Number of iterators created for this pool */
56 mu_nonlocal_jmp_t *jmp; /* Buffer for non-local exit */
56 union mu_opool_bucket *bkt_head, *bkt_tail; 57 union mu_opool_bucket *bkt_head, *bkt_tail;
57 union mu_opool_bucket *bkt_fini; /* List of finished objects */ 58 union mu_opool_bucket *bkt_fini; /* List of finished objects */
58 }; 59 };
...@@ -65,6 +66,8 @@ alloc_bucket (struct _mu_opool *opool, size_t size) ...@@ -65,6 +66,8 @@ alloc_bucket (struct _mu_opool *opool, size_t size)
65 { 66 {
66 if (opool->flags & MU_OPOOL_ENOMEMABRT) 67 if (opool->flags & MU_OPOOL_ENOMEMABRT)
67 mu_alloc_die (); 68 mu_alloc_die ();
69 if (opool->jmp)
70 longjmp (opool->jmp->buf, ENOMEM);
68 } 71 }
69 else 72 else
70 { 73 {
...@@ -122,10 +125,30 @@ mu_opool_create (mu_opool_t *pret, int flags) ...@@ -122,10 +125,30 @@ mu_opool_create (mu_opool_t *pret, int flags)
122 x->bucket_size = MU_OPOOL_BUCKET_SIZE; 125 x->bucket_size = MU_OPOOL_BUCKET_SIZE;
123 x->itr_count = 0; 126 x->itr_count = 0;
124 x->bkt_head = x->bkt_tail = x->bkt_fini = NULL; 127 x->bkt_head = x->bkt_tail = x->bkt_fini = NULL;
128 x->jmp = NULL;
125 *pret = x; 129 *pret = x;
126 return 0; 130 return 0;
127 } 131 }
128 132
133 void
134 mu_opool_setjmp (mu_opool_t opool, mu_nonlocal_jmp_t *jmp)
135 {
136 if (jmp)
137 {
138 jmp->next = opool->jmp;
139 opool->jmp = jmp;
140 }
141 else
142 mu_opool_clrjmp (opool);
143 }
144
145 void
146 mu_opool_clrjmp (mu_opool_t opool)
147 {
148 if (opool->jmp)
149 opool->jmp = opool->jmp->next;
150 }
151
129 int 152 int
130 mu_opool_set_bucket_size (mu_opool_t opool, size_t size) 153 mu_opool_set_bucket_size (mu_opool_t opool, size_t size)
131 { 154 {
......
...@@ -49,6 +49,7 @@ noinst_PROGRAMS = \ ...@@ -49,6 +49,7 @@ noinst_PROGRAMS = \
49 fsaf\ 49 fsaf\
50 fsaftomod\ 50 fsaftomod\
51 fsfolder\ 51 fsfolder\
52 globtest\
52 imapio\ 53 imapio\
53 listop\ 54 listop\
54 mailcap\ 55 mailcap\
...@@ -94,6 +95,7 @@ TESTSUITE_AT = \ ...@@ -94,6 +95,7 @@ TESTSUITE_AT = \
94 fsfolder02.at\ 95 fsfolder02.at\
95 hdrflt.at\ 96 hdrflt.at\
96 htmlent.at\ 97 htmlent.at\
98 globtest.at\
97 imapio.at\ 99 imapio.at\
98 inline-comment.at\ 100 inline-comment.at\
99 linecon.at\ 101 linecon.at\
......
1 # This file is part of GNU Mailutils. -*- Autotest -*-
2 # Copyright (C) 2016 Free Software Foundation, Inc.
3 #
4 # GNU Mailutils is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3, or (at
7 # your option) any later version.
8 #
9 # GNU Mailutils is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
16
17 AT_BANNER([Globbing patterns])
18
19 dnl ------------------------------------------------------------------
20 dnl GLOBCOMP (pattern, regex, [FLAGS])
21 m4_pushdef([GLOBCOMP],
22 [AT_SETUP([[$1][]m4_foreach([flag],[m4_shift(m4_shift($@))],[ flag])])
23 AT_KEYWORDS([glob])
24 AT_CHECK([globtest m4_foreach([flag],[m4_shift(m4_shift($@))],[dnl
25 m4_if(flag,[sub],[ -s],[icase],[ -i],[collapse],[ -c])]) '[$1]'],[0],[[^$2$]
26 ])
27 AT_CLEANUP
28 ])
29 dnl ------------------------------------------------------------------
30
31 GLOBCOMP(abab, abab)
32 GLOBCOMP(a*c, a.*c)
33 GLOBCOMP(a*c?d, a(.*)c(.)d, sub)
34 GLOBCOMP(a***c, a.*c)
35 GLOBCOMP(a***c, a()()(.*)c, sub)
36 GLOBCOMP(a***c, a(.*)c, sub, collapse)
37 GLOBCOMP([{$|a$$], [\{\$\|a\$\$])
38 GLOBCOMP([a[0-9A-Z]c], [a[0-9A-Z]c])
39 GLOBCOMP([a[!a-z]c], [a[^a-z]c])
40 GLOBCOMP([a[!]z@:>@], [a[^]z@:>@])
41 GLOBCOMP([a@<:@cde], [a\@<:@cde])
42 GLOBCOMP([a[@<:@ba]], [a[\@<:@ba]])
43 GLOBCOMP([*.c], [.*\.c])
44 GLOBCOMP([a\],[a\\])
45
46 m4_popdef([GLOBCOMP])
...\ No newline at end of file ...\ No newline at end of file
1 #include <config.h>
2 #include <mailutils/mailutils.h>
3
4 /* globtest PATTERN [WORD...]
5 */
6 int
7 main (int argc, char **argv)
8 {
9 char *pattern = NULL;
10 int flags = 0;
11 int rc;
12 int i;
13
14 mu_set_program_name (argv[0]);
15 for (i = 1; i < argc; i++)
16 {
17 char *a = argv[i];
18 if (strcmp (a, "-i") == 0)
19 flags |= MU_GLOBF_ICASE;
20 else if (strcmp (a, "-s") == 0)
21 flags |= MU_GLOBF_SUB;
22 else if (strcmp (a, "-c") == 0)
23 flags |= MU_GLOBF_COLLAPSE;
24 else if (strcmp (a, "--") == 0)
25 {
26 i++;
27 break;
28 }
29 else if (*a != '-')
30 break;
31 else
32 {
33 mu_error ("unknown option %s", a);
34 return 1;
35 }
36 }
37
38 if (i == argc)
39 {
40 mu_printf ("usage: %s [-ics] PATTERN [WORD...]\n", mu_program_name);
41 return 1;
42 }
43
44 pattern = argv[i++];
45
46 if (i == argc)
47 {
48 char *regstr;
49
50 rc = mu_glob_to_regex (&regstr, pattern, flags);
51 if (rc)
52 {
53 mu_error ("convert: %s", mu_strerror (rc));
54 return 1;
55 }
56 mu_printf ("%s\n", regstr);
57 free (regstr);
58 }
59 else
60 {
61 regex_t regex;
62 size_t nmatch = 0;
63 regmatch_t *matches = NULL;
64
65 rc = mu_glob_compile (&regex, pattern, flags);
66 if (rc)
67 {
68 mu_error ("compile: %s", mu_strerror (rc));
69 return 1;
70 }
71
72 if (flags & MU_GLOBF_SUB)
73 {
74 nmatch = regex.re_nsub + 1;
75 matches = mu_calloc (nmatch, sizeof matches[0]);
76 }
77
78 for (; i < argc; i++)
79 {
80 char *a = argv[i];
81 rc = regexec (&regex, a, nmatch, matches, 0);
82 mu_printf ("%s: %s\n", a, rc == 0 ? "OK" : "NO");
83 if (flags & MU_GLOBF_SUB)
84 {
85 size_t j;
86
87 for (j = 0; j < nmatch; j++)
88 printf ("%02d: %.*s\n", j,
89 matches[j].rm_eo - matches[j].rm_so,
90 a + matches[j].rm_so);
91 }
92 }
93 }
94 return 0;
95 }
...@@ -180,3 +180,5 @@ m4_include([modtofsaf.at]) ...@@ -180,3 +180,5 @@ m4_include([modtofsaf.at])
180 m4_include([mimehdr.at]) 180 m4_include([mimehdr.at])
181 181
182 m4_include([msgset.at]) 182 m4_include([msgset.at])
183
184 m4_include([globtest.at])
......