diff --git a/src/commands.c b/src/commands.c index a3623a01..ac0d3d4d 100644 --- a/src/commands.c +++ b/src/commands.c @@ -788,7 +788,7 @@ static bool cmd_errorfmt(EditorState *e, const CommandArgs *a) const char *pattern = a->args[1]; regex_t re; - if (unlikely(!regexp_compile(&re, pattern, 0))) { + if (unlikely(!regexp_compile(e->err, &re, pattern, 0))) { return false; } @@ -854,31 +854,25 @@ static bool cmd_exec(EditorState *e, const CommandArgs *a) static bool cmd_ft(EditorState *e, const CommandArgs *a) { + ErrorBuffer *ebuf = e->err; char **args = a->args; const char *filetype = args[0]; if (unlikely(!is_valid_filetype_name(filetype))) { - return error_msg(e->err, "Invalid filetype name: '%s'", filetype); + return error_msg(ebuf, "Invalid filetype name: '%s'", filetype); } FileDetectionType dt = FT_EXTENSION; switch (last_flag(a)) { - case 'b': - dt = FT_BASENAME; - break; - case 'c': - dt = FT_CONTENT; - break; - case 'f': - dt = FT_FILENAME; - break; - case 'i': - dt = FT_INTERPRETER; - break; + case 'b': dt = FT_BASENAME; break; + case 'c': dt = FT_CONTENT; break; + case 'f': dt = FT_FILENAME; break; + case 'i': dt = FT_INTERPRETER; break; } + PointerArray *filetypes = &e->filetypes; size_t nfailed = 0; for (size_t i = 1, n = a->nr_args; i < n; i++) { - if (!add_filetype(&e->filetypes, filetype, args[i], dt)) { + if (!add_filetype(filetypes, filetype, args[i], dt, ebuf)) { nfailed++; } } @@ -1299,7 +1293,7 @@ static bool cmd_option(EditorState *e, const CommandArgs *a) PointerArray *opts = &e->file_options; if (has_flag(a, 'r')) { - FileTypeOrFileName u = {.filename = regexp_intern(arg0)}; + FileTypeOrFileName u = {.filename = regexp_intern(e->err, arg0)}; if (unlikely(!u.filename)) { return false; } diff --git a/src/filetype.c b/src/filetype.c index 1fd2ba9f..678a751e 100644 --- a/src/filetype.c +++ b/src/filetype.c @@ -75,12 +75,17 @@ static bool ft_uses_regex(FileDetectionType type) return type == FT_CONTENT || type == FT_FILENAME; } -bool add_filetype(PointerArray *filetypes, const char *name, const char *str, FileDetectionType type) -{ +bool add_filetype ( + PointerArray *filetypes, + const char *name, + const char *str, + FileDetectionType type, + ErrorBuffer *ebuf +) { BUG_ON(!is_valid_filetype_name(name)); const InternedRegexp *ir = NULL; if (ft_uses_regex(type)) { - ir = regexp_intern(str); + ir = regexp_intern(ebuf, str); if (unlikely(!ir)) { return false; } diff --git a/src/filetype.h b/src/filetype.h index 5ef6fdb7..2e05b078 100644 --- a/src/filetype.h +++ b/src/filetype.h @@ -2,6 +2,7 @@ #define FILETYPE_H #include +#include "error.h" #include "util/macros.h" #include "util/ptr-array.h" #include "util/string-view.h" @@ -29,11 +30,19 @@ static inline bool is_valid_filetype_name(const char *name) return is_valid_filetype_name_sv(&sv); } -bool add_filetype(PointerArray *filetypes, const char *name, const char *str, FileDetectionType type) NONNULL_ARGS WARN_UNUSED_RESULT; bool is_ft(const PointerArray *filetypes, const char *name); const char *find_ft(const PointerArray *filetypes, const char *filename, StringView line); void collect_ft(const PointerArray *filetypes, PointerArray *a, const char *prefix); String dump_filetypes(const PointerArray *filetypes); void free_filetypes(PointerArray *filetypes); +WARN_UNUSED_RESULT NONNULL_ARG(1, 2, 3) +bool add_filetype ( + PointerArray *filetypes, + const char *name, + const char *str, + FileDetectionType type, + ErrorBuffer *ebuf +); + #endif diff --git a/src/options.c b/src/options.c index be61a43a..38c12efb 100644 --- a/src/options.c +++ b/src/options.c @@ -244,18 +244,20 @@ static OptionValue re_get(const OptionDesc* UNUSED_ARG(desc), void *ptr) static void re_set(const OptionDesc* UNUSED_ARG(d), void *ptr, OptionValue v) { + // Note that this function is only ever called if re_parse() has already + // validated the pattern const InternedRegexp **irp = ptr; - *irp = v.str_val ? regexp_intern(v.str_val) : NULL; + *irp = v.str_val ? regexp_intern(NULL, v.str_val) : NULL; } -static bool re_parse(const OptionDesc* UNUSED_ARG(d), ErrorBuffer* UNUSED_ARG(ebuf), const char *str, OptionValue *v) +static bool re_parse(const OptionDesc* UNUSED_ARG(d), ErrorBuffer *ebuf, const char *str, OptionValue *v) { if (str[0] == '\0') { v->str_val = NULL; return true; } - bool valid = regexp_is_interned(str) || regexp_is_valid(str, REG_NEWLINE); + bool valid = regexp_is_interned(str) || regexp_is_valid(ebuf, str, REG_NEWLINE); v->str_val = valid ? str : NULL; return valid; } diff --git a/src/regexp.c b/src/regexp.c index 3a6f46ee..5d9c90ea 100644 --- a/src/regexp.c +++ b/src/regexp.c @@ -1,7 +1,6 @@ #include #include #include "regexp.h" -#include "error.h" #include "util/ascii.h" #include "util/debug.h" #include "util/hashmap.h" @@ -12,17 +11,14 @@ // NOLINTNEXTLINE(*-avoid-non-const-global-variables) static HashMap interned_regexps; -bool regexp_error_msg(const regex_t *re, const char *pattern, int err) +bool regexp_error_msg(ErrorBuffer *ebuf, const regex_t *re, const char *pattern, int err) { + if (!ebuf) { + return false; + } char msg[1024]; regerror(err, re, msg, sizeof(msg)); - return error_msg_("%s: %s", msg, pattern); -} - -bool regexp_compile_internal(regex_t *re, const char *pattern, int flags) -{ - int err = regcomp(re, pattern, flags); - return !err || regexp_error_msg(re, pattern, err); + return error_msg(ebuf, "%s: %s", msg, pattern); } void regexp_compile_or_fatal_error(regex_t *re, const char *pattern, int flags) @@ -46,14 +42,12 @@ bool regexp_exec ( regmatch_t *pmatch, int flags ) { - // "If REG_STARTEND is specified, pmatch must point to at least one - // regmatch_t (even if nmatch is 0 or REG_NOSUB was specified), to - // hold the input offsets for REG_STARTEND." - // -- https://man.openbsd.org/regex.3 - BUG_ON(!pmatch); - // ASan's __interceptor_regexec() doesn't support REG_STARTEND #if defined(REG_STARTEND) && ASAN_ENABLED == 0 && MSAN_ENABLED == 0 + // "If REG_STARTEND is specified, pmatch must point to at least + // one regmatch_t (even if nmatch is 0 or REG_NOSUB was specified), + // to hold the input offsets for REG_STARTEND." + // -- https://man.openbsd.org/regex.3 pmatch[0].rm_so = 0; pmatch[0].rm_eo = size; return !regexec(re, buf, nmatch, pmatch, flags | REG_STARTEND); @@ -125,7 +119,7 @@ char *regexp_escape(const char *pattern, size_t len) return buf; } -const InternedRegexp *regexp_intern(const char *pattern) +const InternedRegexp *regexp_intern(ErrorBuffer *ebuf, const char *pattern) { if (pattern[0] == '\0') { return NULL; @@ -139,7 +133,7 @@ const InternedRegexp *regexp_intern(const char *pattern) ir = xnew(InternedRegexp, 1); int err = regcomp(&ir->re, pattern, DEFAULT_REGEX_FLAGS | REG_NEWLINE | REG_NOSUB); if (unlikely(err)) { - regexp_error_msg(&ir->re, pattern, err); + regexp_error_msg(ebuf, &ir->re, pattern, err); free(ir); return NULL; } diff --git a/src/regexp.h b/src/regexp.h index b7c25d6e..5f21f0ce 100644 --- a/src/regexp.h +++ b/src/regexp.h @@ -4,6 +4,7 @@ #include #include #include +#include "error.h" #include "util/macros.h" enum { @@ -34,42 +35,42 @@ typedef struct { char end[8]; } RegexpWordBoundaryTokens; -bool regexp_compile_internal(regex_t *re, const char *pattern, int flags) WARN_UNUSED_RESULT; +void regexp_compile_or_fatal_error(regex_t *re, const char *pattern, int flags) NONNULL_ARGS; +bool regexp_init_word_boundary_tokens(RegexpWordBoundaryTokens *rwbt) NONNULL_ARGS; +bool regexp_error_msg(ErrorBuffer *ebuf, const regex_t *re, const char *pattern, int err) NONNULL_ARG(2, 3); +char *regexp_escape(const char *pattern, size_t len) NONNULL_ARGS WARN_UNUSED_RESULT; +size_t regexp_escapeb(char *buf, size_t buflen, const char *pat, size_t plen) NONNULL_ARG(1); -WARN_UNUSED_RESULT -static inline bool regexp_compile(regex_t *re, const char *pattern, int flags) +const InternedRegexp *regexp_intern(ErrorBuffer *ebuf, const char *pattern) NONNULL_ARG(2) WARN_UNUSED_RESULT; +bool regexp_is_interned(const char *pattern) NONNULL_ARGS; +void free_interned_regexps(void); + +WARN_UNUSED_RESULT NONNULL_ARGS +bool regexp_exec ( + const regex_t *re, + const char *buf, + size_t size, + size_t nmatch, + regmatch_t *pmatch, + int flags +); + +WARN_UNUSED_RESULT NONNULL_ARG(2, 3) +static inline bool regexp_compile(ErrorBuffer *ebuf, regex_t *re, const char *pattern, int flags) { - return regexp_compile_internal(re, pattern, flags | DEFAULT_REGEX_FLAGS); + int err = regcomp(re, pattern, flags | DEFAULT_REGEX_FLAGS); + return !err || regexp_error_msg(ebuf, re, pattern, err); } -WARN_UNUSED_RESULT -static inline bool regexp_is_valid(const char *pattern, int flags) +WARN_UNUSED_RESULT NONNULL_ARG(2) +static inline bool regexp_is_valid(ErrorBuffer *ebuf, const char *pattern, int flags) { regex_t re; - if (!regexp_compile(&re, pattern, flags | REG_NOSUB)) { + if (!regexp_compile(ebuf, &re, pattern, flags | REG_NOSUB)) { return false; } regfree(&re); return true; } -void regexp_compile_or_fatal_error(regex_t *re, const char *pattern, int flags); -bool regexp_init_word_boundary_tokens(RegexpWordBoundaryTokens *rwbt); -bool regexp_error_msg(const regex_t *re, const char *pattern, int err); -char *regexp_escape(const char *pattern, size_t len); -size_t regexp_escapeb(char *buf, size_t buflen, const char *pat, size_t plen); - -const InternedRegexp *regexp_intern(const char *pattern); -bool regexp_is_interned(const char *pattern); -void free_interned_regexps(void); - -bool regexp_exec ( - const regex_t *re, - const char *buf, - size_t size, - size_t nmatch, - regmatch_t *pmatch, - int flags -) WARN_UNUSED_RESULT; - #endif diff --git a/src/replace.c b/src/replace.c index cd804ce1..395b97c3 100644 --- a/src/replace.c +++ b/src/replace.c @@ -168,8 +168,9 @@ bool reg_replace(View *view, const char *pattern, const char *format, ReplaceFla re_flags |= (flags & REPLACE_BASIC) ? 0 : DEFAULT_REGEX_FLAGS; regex_t re; - if (unlikely(!regexp_compile_internal(&re, pattern, re_flags))) { - return false; + int err = regcomp(&re, pattern, re_flags); + if (unlikely(err)) { + return regexp_error_msg(ebuf, &re, pattern, err); } BlockIter bi; diff --git a/src/search.c b/src/search.c index f991c0dd..d4de14f2 100644 --- a/src/search.c +++ b/src/search.c @@ -112,8 +112,9 @@ bool search_tag(View *view, const char *pattern) // DEFAULT_REGEX_FLAGS is not used here because pattern has been // escaped by parse_ex_pattern() for use as a POSIX BRE regex_t regex; - if (!regexp_compile_internal(®ex, pattern, REG_NEWLINE)) { - return false; + int err = regcomp(®ex, pattern, REG_NEWLINE); + if (unlikely(err)) { + regexp_error_msg(view->window->editor->err, ®ex, pattern, err); } BlockIter bi = block_iter(view->buffer); @@ -140,7 +141,7 @@ static bool has_upper(const char *str) return false; } -static bool update_regex(SearchState *search, SearchCaseSensitivity cs) +static bool update_regex(SearchState *search, ErrorBuffer *ebuf, SearchCaseSensitivity cs) { const char *pattern = search->pattern; bool icase = (cs == CSS_FALSE) || (cs == CSS_AUTO && !has_upper(pattern)); @@ -154,7 +155,7 @@ static bool update_regex(SearchState *search, SearchCaseSensitivity cs) search->re_flags = 0; } - if (regexp_compile(&search->regex, pattern, flags)) { + if (regexp_compile(ebuf, &search->regex, pattern, flags)) { search->re_flags = flags; return true; } @@ -180,11 +181,11 @@ void search_set_regexp(SearchState *search, const char *pattern) bool do_search_next(View *view, SearchState *search, SearchCaseSensitivity cs, bool skip) { - ErrorBuffer *err = view->window->editor->err; + ErrorBuffer *ebuf = view->window->editor->err; if (!search->pattern) { - return error_msg(err, "No previous search pattern"); + return error_msg(ebuf, "No previous search pattern"); } - if (!update_regex(search, cs)) { + if (!update_regex(search, ebuf, cs)) { return false; } @@ -196,7 +197,7 @@ bool do_search_next(View *view, SearchState *search, SearchCaseSensitivity cs, b } block_iter_bof(&bi); if (do_search_fwd(view, regex, &bi, false)) { - return info_msg(err, "Continuing at top"); + return info_msg(ebuf, "Continuing at top"); } } else { size_t cursor_x = block_iter_bol(&bi); @@ -205,9 +206,9 @@ bool do_search_next(View *view, SearchState *search, SearchCaseSensitivity cs, b } block_iter_eof(&bi); if (do_search_bwd(view, regex, &bi, -1, false)) { - return info_msg(err, "Continuing at bottom"); + return info_msg(ebuf, "Continuing at bottom"); } } - return error_msg(err, "Pattern '%s' not found", search->pattern); + return error_msg(ebuf, "Pattern '%s' not found", search->pattern); } diff --git a/test/buffer.c b/test/buffer.c index bf60c59c..7204967b 100644 --- a/test/buffer.c +++ b/test/buffer.c @@ -94,7 +94,7 @@ static void test_make_indent(TestContext *ctx) static void test_get_indent_for_next_line(TestContext *ctx) { const char *pattern = "\\{$"; - const InternedRegexp *ir = regexp_intern(pattern); + const InternedRegexp *ir = regexp_intern(NULL, pattern); ASSERT_NONNULL(ir); LocalOptions options = { diff --git a/test/filetype.c b/test/filetype.c index 817c9e80..b3915c7a 100644 --- a/test/filetype.c +++ b/test/filetype.c @@ -326,28 +326,28 @@ static void test_find_ft_dynamic(TestContext *ctx) const char *ft = "test1"; StringView line = STRING_VIEW_INIT; EXPECT_FALSE(is_ft(&a, ft)); - EXPECT_TRUE(add_filetype(&a, ft, "ext-test", FT_EXTENSION)); + EXPECT_TRUE(add_filetype(&a, ft, "ext-test", FT_EXTENSION, NULL)); EXPECT_TRUE(is_ft(&a, ft)); EXPECT_STREQ(find_ft(&a, "/tmp/file.ext-test", line), ft); ft = "test2"; - EXPECT_TRUE(add_filetype(&a, ft, "/zdir/__[A-Z]+$", FT_FILENAME)); + EXPECT_TRUE(add_filetype(&a, ft, "/zdir/__[A-Z]+$", FT_FILENAME, NULL)); EXPECT_STREQ(find_ft(&a, "/tmp/zdir/__TESTFILE", line), ft); EXPECT_STREQ(find_ft(&a, "/tmp/zdir/__testfile", line), NULL); ft = "test3"; - EXPECT_TRUE(add_filetype(&a, ft, "._fiLeName", FT_BASENAME)); + EXPECT_TRUE(add_filetype(&a, ft, "._fiLeName", FT_BASENAME, NULL)); EXPECT_STREQ(find_ft(&a, "/tmp/._fiLeName", line), ft); EXPECT_STREQ(find_ft(&a, "/tmp/._filename", line), NULL); ft = "test4"; line = strview_from_cstring("!!42"); - EXPECT_TRUE(add_filetype(&a, ft, "^!+42$", FT_CONTENT)); + EXPECT_TRUE(add_filetype(&a, ft, "^!+42$", FT_CONTENT, NULL)); EXPECT_STREQ(find_ft(&a, NULL, line), ft); ft = "test5"; line = strview_from_cstring("#!/usr/bin/xyzlang4.2"); - EXPECT_TRUE(add_filetype(&a, ft, "xyzlang", FT_INTERPRETER)); + EXPECT_TRUE(add_filetype(&a, ft, "xyzlang", FT_INTERPRETER, NULL)); EXPECT_STREQ(find_ft(&a, NULL, line), ft); EXPECT_TRUE(is_ft(&a, "test1")); diff --git a/test/regexp.c b/test/regexp.c index 6a073a48..a175facd 100644 --- a/test/regexp.c +++ b/test/regexp.c @@ -5,14 +5,14 @@ static void test_regexp_escape(TestContext *ctx) { static const char pat[] = "^([a-z]+|0-9{3,6}|\\.|-?.*)$"; - ASSERT_TRUE(regexp_is_valid(pat, REG_NEWLINE)); + ASSERT_TRUE(regexp_is_valid(NULL, pat, REG_NEWLINE)); char *escaped = regexp_escape(pat, sizeof(pat) - 1); EXPECT_STREQ(escaped, "\\^\\(\\[a-z]\\+\\|0-9\\{3,6}\\|\\\\\\.\\|-\\?\\.\\*\\)\\$"); // Ensure the escaped pattern matches the original pattern string regex_t re; regmatch_t m; - ASSERT_TRUE(regexp_compile(&re, escaped, REG_NEWLINE | REG_NOSUB)); + ASSERT_TRUE(regexp_compile(NULL, &re, escaped, REG_NEWLINE | REG_NOSUB)); free(escaped); EXPECT_TRUE(regexp_exec(&re, pat, sizeof(pat) - 1, 0, &m, 0)); regfree(&re);