| /* Construct a regular expression from a literal string. | |
| Copyright (C) 1995, 2010-2026 Free Software Foundation, Inc. | |
| Written by Bruno Haible <haible@clisp.cons.org>, 2010. | |
| This program 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 of the License, or | |
| (at your option) any later version. | |
| This program 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 this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| #include <config.h> | |
| /* Specification. */ | |
| #include "regex-quote.h" | |
| #include <string.h> | |
| #if GNULIB_MCEL_PREFER | |
| # include "mcel.h" | |
| #else | |
| # include "mbuiter.h" | |
| #endif | |
| #include "xalloc.h" | |
| /* Characters that are special in a BRE. */ | |
| static const char bre_special[] = "$^.*[\\"; | |
| /* Characters that are special in an ERE. */ | |
| static const char ere_special[] = "$^.*[\\+?{()|"; | |
| struct regex_quote_spec | |
| regex_quote_spec_posix (int cflags, bool anchored) | |
| { | |
| struct regex_quote_spec result; | |
| strcpy (result.special, cflags != 0 ? ere_special : bre_special); | |
| result.multibyte = true; | |
| result.anchored = anchored; | |
| return result; | |
| } | |
| /* Syntax bit values, defined in GNU <regex.h>. We don't include it here, | |
| otherwise this module would need to depend on gnulib module 'regex'. */ | |
| #define RE_BK_PLUS_QM 0x00000002 | |
| #define RE_INTERVALS 0x00000200 | |
| #define RE_LIMITED_OPS 0x00000400 | |
| #define RE_NEWLINE_ALT 0x00000800 | |
| #define RE_NO_BK_BRACES 0x00001000 | |
| #define RE_NO_BK_PARENS 0x00002000 | |
| #define RE_NO_BK_VBAR 0x00008000 | |
| struct regex_quote_spec | |
| regex_quote_spec_gnu (unsigned long /*reg_syntax_t*/ syntax, bool anchored) | |
| { | |
| struct regex_quote_spec result; | |
| { | |
| char *p = result.special; | |
| memcpy (p, bre_special, sizeof (bre_special) - 1); | |
| p += sizeof (bre_special) - 1; | |
| if ((syntax & RE_LIMITED_OPS) == 0 && (syntax & RE_BK_PLUS_QM) == 0) | |
| { | |
| *p++ = '+'; | |
| *p++ = '?'; | |
| } | |
| if ((syntax & RE_INTERVALS) != 0 && (syntax & RE_NO_BK_BRACES) != 0) | |
| { | |
| *p++ = '{'; | |
| *p++ = '}'; | |
| } | |
| if ((syntax & RE_NO_BK_PARENS) != 0) | |
| { | |
| *p++ = '('; | |
| *p++ = ')'; | |
| } | |
| if ((syntax & RE_LIMITED_OPS) == 0 && (syntax & RE_NO_BK_VBAR) != 0) | |
| *p++ = '|'; | |
| if ((syntax & RE_NEWLINE_ALT) != 0) | |
| *p++ = '\n'; | |
| *p = '\0'; | |
| } | |
| result.multibyte = true; | |
| result.anchored = anchored; | |
| return result; | |
| } | |
| /* Characters that are special in a PCRE. */ | |
| static const char pcre_special[] = "$^.*[]\\+?{}()|"; | |
| /* Options bit values, defined in <pcre.h>. We don't include it here, because | |
| it is not a standard header. */ | |
| #define PCRE_ANCHORED 0x00000010 | |
| #define PCRE_EXTENDED 0x00000008 | |
| struct regex_quote_spec | |
| regex_quote_spec_pcre (int options, bool anchored) | |
| { | |
| struct regex_quote_spec result; | |
| { | |
| char *p = result.special; | |
| memcpy (p, pcre_special, sizeof (pcre_special) - 1); | |
| p += sizeof (pcre_special) - 1; | |
| if (options & PCRE_EXTENDED) | |
| { | |
| *p++ = ' '; | |
| *p++ = '\t'; | |
| *p++ = '\n'; | |
| *p++ = '\v'; | |
| *p++ = '\f'; | |
| *p++ = '\r'; | |
| *p++ = '#'; | |
| } | |
| *p = '\0'; | |
| } | |
| /* PCRE regular expressions consist of UTF-8 characters of options contains | |
| PCRE_UTF8 and of single bytes otherwise. */ | |
| result.multibyte = false; | |
| /* If options contains PCRE_ANCHORED, the anchoring is implicit. */ | |
| result.anchored = (options & PCRE_ANCHORED ? 0 : anchored); | |
| return result; | |
| } | |
| size_t | |
| regex_quote_length (const char *string, const struct regex_quote_spec *spec) | |
| { | |
| const char *special = spec->special; | |
| size_t length = 0; | |
| if (spec->anchored) | |
| length += 2; /* for '^' at the beginning and '$' at the end */ | |
| if (spec->multibyte) | |
| { | |
| #if GNULIB_MCEL_PREFER | |
| char const *iter = string; | |
| for (; *iter; ) | |
| { | |
| mcel_t g = mcel_scanz (iter); | |
| /* We know that special contains only ASCII characters. */ | |
| length += g.len == 1 && strchr (special, *iter); | |
| iter += g.len; | |
| } | |
| length += iter - string; | |
| #else | |
| mbui_iterator_t iter; | |
| for (mbui_init (iter, string); mbui_avail (iter); mbui_advance (iter)) | |
| { | |
| /* We know that special contains only ASCII characters. */ | |
| if (mb_len (mbui_cur (iter)) == 1 | |
| && strchr (special, * mbui_cur_ptr (iter))) | |
| length += 1; | |
| length += mb_len (mbui_cur (iter)); | |
| } | |
| #endif | |
| } | |
| else | |
| { | |
| for (const char *iter = string; *iter != '\0'; iter++) | |
| { | |
| if (strchr (special, *iter)) | |
| length += 1; | |
| length += 1; | |
| } | |
| } | |
| return length; | |
| } | |
| char * | |
| regex_quote_copy (char *p, const char *string, const struct regex_quote_spec *spec) | |
| { | |
| const char *special = spec->special; | |
| if (spec->anchored) | |
| *p++ = '^'; | |
| if (spec->multibyte) | |
| { | |
| #if GNULIB_MCEL_PREFER | |
| for (char const *iter = string; *iter; ) | |
| { | |
| mcel_t g = mcel_scanz (iter); | |
| *p = '\\'; | |
| /* We know that special contains only ASCII characters. */ | |
| p += g.len == 1 && strchr (special, *iter); | |
| p = mempcpy (p, iter, g.len); | |
| iter += g.len; | |
| } | |
| #else | |
| mbui_iterator_t iter; | |
| for (mbui_init (iter, string); mbui_avail (iter); mbui_advance (iter)) | |
| { | |
| /* We know that special contains only ASCII characters. */ | |
| if (mb_len (mbui_cur (iter)) == 1 | |
| && strchr (special, * mbui_cur_ptr (iter))) | |
| *p++ = '\\'; | |
| memcpy (p, mbui_cur_ptr (iter), mb_len (mbui_cur (iter))); | |
| p += mb_len (mbui_cur (iter)); | |
| } | |
| #endif | |
| } | |
| else | |
| { | |
| for (const char *iter = string; *iter != '\0'; iter++) | |
| { | |
| if (strchr (special, *iter)) | |
| *p++ = '\\'; | |
| *p++ = *iter++; | |
| } | |
| } | |
| if (spec->anchored) | |
| *p++ = '$'; | |
| return p; | |
| } | |
| char * | |
| regex_quote (const char *string, const struct regex_quote_spec *spec) | |
| { | |
| size_t length = regex_quote_length (string, spec); | |
| char *result = XNMALLOC (length + 1, char); | |
| { | |
| char *p = regex_quote_copy (result, string, spec); | |
| *p = '\0'; | |
| } | |
| return result; | |
| } |