diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2016-12-06 15:32:20 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2016-12-06 15:47:19 +0200 |
commit | df608ed0cfb5f0db57d1ff97eb208ceb46f06e17 (patch) | |
tree | 87536d81395c41d8720dd846d18665108559773d /libmailutils/base/glob.c | |
parent | f8a0fd0fdb20fdb607343d932f19e650dfd8f1d2 (diff) | |
download | mailutils-df608ed0cfb5f0db57d1ff97eb208ceb46f06e17.tar.gz mailutils-df608ed0cfb5f0db57d1ff97eb208ceb46f06e17.tar.bz2 |
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.
Diffstat (limited to 'libmailutils/base/glob.c')
-rw-r--r-- | libmailutils/base/glob.c | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/libmailutils/base/glob.c b/libmailutils/base/glob.c new file mode 100644 index 000000000..2d889902b --- /dev/null +++ b/libmailutils/base/glob.c @@ -0,0 +1,264 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2007, 2009-2012, 2014-2016 Free Software Foundation, + Inc. + + GNU Mailutils 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 3, or (at your option) + any later version. + + GNU Mailutils 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif +#include <mailutils/opool.h> +#include <mailutils/error.h> +#include <mailutils/errno.h> +#include <mailutils/glob.h> +#include <regex.h> +#include <string.h> +#include <stdlib.h> + +static void +parse_character_class (unsigned char const *str, mu_opool_t pool, + unsigned char const **endp) +{ + unsigned char const *cur; + + cur = str + 1; + if (*cur == '!') + cur++; + if (*cur == ']') + cur++; + + while (*cur && *cur != ']') + { + int c = *cur++; + if (c == '\\') + cur++; + else if (c >= 0xc2) + { + size_t len; + + if (c < 0xe0) + len = 1; + else if (c < 0xf0) + len = 2; + else if (c < 0xf8) + len = 3; + else + /* Invalid UTF-8 sequence; skip. */ + continue; + + while (len-- && *cur) + cur++; + } + } + + if (*cur == ']') + { + /* Valid character class */ + mu_opool_append_char (pool, *str); + str++; + if (*str == '!') + { + mu_opool_append_char (pool, '^'); + str++; + } + while (str < cur) + { + if (*str == '[') + mu_opool_append_char (pool, '\\'); + + mu_opool_append_char (pool, *str); + str++; + } + mu_opool_append_char (pool, ']'); + *endp = cur + 1; + } + else + { + mu_opool_append_char (pool, '\\'); + mu_opool_append_char (pool, *str); + str++; + *endp = str; + } +} + +int +mu_glob_to_regex_opool (char const *pattern, mu_opool_t pool, int flags) +{ + unsigned char const *str = (unsigned char const *) pattern; + mu_nonlocal_jmp_t jmp; + + if (!(flags & MU_GLOBF_SUB)) + flags |= MU_GLOBF_COLLAPSE; + + mu_opool_setup_nonlocal_jump (pool, jmp); + + while (*str) + { + int c = *str++; + + if (c < 0x80) + { + switch (c) + { + case '\\': + mu_opool_append_char (pool, '\\'); + if (*str && strchr ("?*[", *str)) + { + mu_opool_append_char (pool, *str); + str++; + } + else + mu_opool_append_char (pool, '\\'); + break; + + case '?': + if (flags & MU_GLOBF_SUB) + mu_opool_append_char (pool, '('); + mu_opool_append_char (pool, '.'); + if (flags & MU_GLOBF_SUB) + mu_opool_append_char (pool, ')'); + break; + + case '*': + if (flags & MU_GLOBF_COLLAPSE) + { + while (*str == '*') + str++; + } + + if (flags & MU_GLOBF_SUB) + { + while (*str == '*') + { + mu_opool_append (pool, "()", 2); + str++; + } + mu_opool_append_char (pool, '('); + mu_opool_append (pool, ".*", 2); + mu_opool_append_char (pool, ')'); + } + else + mu_opool_append (pool, ".*", 2); + break; + + case '[': + parse_character_class (str - 1, pool, &str); + break; + + case '(': + case ')': + case '{': + case '}': + case '^': + case '$': + case ']': + case '|': + case '.': + mu_opool_append_char (pool, '\\'); + mu_opool_append_char (pool, c); + break; + + default: + mu_opool_append_char (pool, c); + } + } + else + { + mu_opool_append_char (pool, c); + if (c >= 0xc2) + { + size_t len; + + if (c < 0xe0) + len = 1; + else if (c < 0xf0) + len = 2; + else if (c < 0xf8) + len = 3; + else + /* Invalid UTF-8 sequence; skip. */ + continue; + + for (; len-- && *str; str++) + mu_opool_append_char (pool, *str); + } + } + } + mu_opool_clrjmp (pool); + return 0; +} + +int +mu_glob_to_regex (char **rxstr, char const *pattern, int flags) +{ + mu_opool_t pool; + int rc; + mu_nonlocal_jmp_t jmp; + + rc = mu_opool_create (&pool, MU_OPOOL_DEFAULT); + if (rc) + return rc; + mu_opool_setup_nonlocal_jump (pool, jmp); + mu_opool_append_char (pool, '^'); + rc = mu_glob_to_regex_opool (pattern, pool, flags); + if (rc == 0) + { + mu_opool_append_char (pool, '$'); + mu_opool_append_char (pool, 0); + *rxstr = mu_opool_detach (pool, NULL); + } + mu_opool_clrjmp (pool); + mu_opool_destroy (&pool); + return rc; +} + +int +mu_glob_compile (regex_t *rx, char const *pattern, int flags) +{ + char *str; + int rc; + int rxflags; + + rc = mu_glob_to_regex (&str, pattern, flags); + if (rc) + return rc; + + rxflags = REG_EXTENDED; + if (flags & MU_GLOBF_ICASE) + rxflags |= REG_ICASE; + if (!(flags & MU_GLOBF_SUB)) + rxflags |= REG_NOSUB; + + rc = regcomp (rx, str, rxflags); + if (rc) + { + size_t size = regerror (rc, rx, NULL, 0); + char *errbuf = malloc (size + 1); + if (errbuf) + { + regerror (rc, rx, errbuf, size); + mu_error ("INTERNAL ERROR: can't compile regular expression \"%s\": %s", + str, mu_strerror (rc)); + } + else + mu_error ("INTERNAL ERROR: can't compile regular expression \"%s\"", + str); + mu_error ("INTERNAL ERROR: expression compiled from globbing pattern: %s", + pattern); + free (errbuf); + } + free (str); + + return rc; +} |