This should help with some static checking. Derived from a suggestion by Martin Sebor in: https://sourceware.org/pipermail/libc-alpha/2021-August/130336.html This also ports recent and relevant Glibc changes to Gnulib and prepares to copy back. * lib/cdefs.h (__ARG_NELTS): New macro. * lib/regex.c: Ignore -Wvla for the whole file. * lib/regex.h (_ARG_NELTS_, _Attr_access_): New macros. Ignore -Wvla when declaring regexec. * lib/regex.h (re_compile_pattern, re_search, re_search_2) (re_match, re_match_2, regcomp, regerror): Use _Attr_access_ where that could help static checking. * lib/regexec.c (regexec, __compat_regexec, re_copy_regs) (re_search_internal, proceed_next_node, push_fail_stack) (pop_fail_stack, set_regs, update_regs): Use __ARG_NELTS for each array parameter whose size is another arg, but which might be null. --- ChangeLog | 15 ++++++++++ lib/cdefs.h | 9 ++++++ lib/regex.c | 1 + lib/regex.h | 52 +++++++++++++++++++++++++++++----- lib/regexec.c | 78 +++++++++++++++++++++++++++++---------------------- 5 files changed, 114 insertions(+), 41 deletions(-)
diff --git a/ChangeLog b/ChangeLog index 785fbc44b3..80c2b83846 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2021-08-26 Paul Eggert <egg...@cs.ucla.edu> + + regex: use C99-style array arg syntax + This should help with some static checking. + Derived from a suggestion by Martin Sebor in: + https://sourceware.org/pipermail/libc-alpha/2021-August/130336.html + * lib/cdefs.h (__ARG_NELTS): New macro. + * lib/regex.c: Ignore -Wvla for the whole file. + * lib/regex.h (_ARG_NELTS_): New macro. + Ignore -Wvla when declaring regexec. + * lib/regexec.c (regexec, __compat_regexec, re_copy_regs) + (re_search_internal, proceed_next_node, push_fail_stack) + (pop_fail_stack, set_regs, update_regs): + Use __ARG_NELTS for each array parameter whose size is another arg. + 2021-08-25 Bruno Haible <br...@clisp.org> execute tests: Fix test failure when libtool is in use. diff --git a/lib/cdefs.h b/lib/cdefs.h index 4dac9d264d..13c5542bfd 100644 --- a/lib/cdefs.h +++ b/lib/cdefs.h @@ -634,4 +634,13 @@ _Static_assert (0, "IEEE 128-bits long double requires redirection on this platf # define __attribute_returns_twice__ /* Ignore. */ #endif +/* Specify the number of elements of a function's array parameter, + as in 'int f (int n, int a[__ARG_NELTS (n)]);'. */ +#if (defined __STDC_VERSION__ && 199901L <= __STDC_VERSION__ \ + && !defined __STDC_NO_VLA__) +# define __ARG_NELTS(n) n +#else +# define __ARG_NELTS(n) +#endif + #endif /* sys/cdefs.h */ diff --git a/lib/regex.c b/lib/regex.c index 7296be0f08..d32863972c 100644 --- a/lib/regex.c +++ b/lib/regex.c @@ -24,6 +24,7 @@ # if __GNUC_PREREQ (4, 6) # pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" +# pragma GCC diagnostic ignored "-Wvla" # endif # if __GNUC_PREREQ (4, 3) # pragma GCC diagnostic ignored "-Wold-style-definition" diff --git a/lib/regex.h b/lib/regex.h index 8e4ef45578..540c323163 100644 --- a/lib/regex.h +++ b/lib/regex.h @@ -522,6 +522,32 @@ typedef struct /* Declarations for routines. */ +#ifndef _ARG_NELTS_ +# ifdef __ARG_NELTS +# define _ARG_NELTS_(arg) __ARG_NELTS (arg) +# elif (defined __STDC_VERSION__ && 199901L <= __STDC_VERSION__ \ + && !defined __STDC_NO_VLA__) +# define _ARG_NELTS_(n) n +# else +# define _ARG_NELTS_(n) +# endif +#endif + +#if defined __GNUC__ && 4 < __GNUC__ + (6 <= __GNUC_MINOR__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wvla" +#endif + +#ifndef _Attr_access_ +# ifdef __attr_access +# define _Attr_access_(arg) __attr_access (arg) +# elif defined __GNUC__ && 10 <= __GNUC__ +# define _Attr_access_(x) __attribute__ ((__access__ x)) +# else +# define _Attr_access_(x) +# endif +#endif + #ifdef __USE_GNU /* Sets the current default syntax to SYNTAX, and return the old syntax. You can also simply assign to the 're_syntax_options' variable. */ @@ -536,7 +562,8 @@ extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax); 'regcomp', with a malloc'ed value, or set to NULL before calling 'regfree'. */ extern const char *re_compile_pattern (const char *__pattern, size_t __length, - struct re_pattern_buffer *__buffer); + struct re_pattern_buffer *__buffer) + _Attr_access_ ((__read_only__, 1, 2)); /* Compile a fastmap for the compiled pattern in BUFFER; used to @@ -553,7 +580,8 @@ extern int re_compile_fastmap (struct re_pattern_buffer *__buffer); extern regoff_t re_search (struct re_pattern_buffer *__buffer, const char *__String, regoff_t __length, regoff_t __start, regoff_t __range, - struct re_registers *__regs); + struct re_registers *__regs) + _Attr_access_ ((__read_only__, 2, 3)); /* Like 're_search', but search in the concatenation of STRING1 and @@ -563,14 +591,17 @@ extern regoff_t re_search_2 (struct re_pattern_buffer *__buffer, const char *__string2, regoff_t __length2, regoff_t __start, regoff_t __range, struct re_registers *__regs, - regoff_t __stop); + regoff_t __stop) + _Attr_access_ ((__read_only__, 2, 3)) + _Attr_access_ ((__read_only__, 4, 5)); /* Like 're_search', but return how many characters in STRING the regexp in BUFFER matched, starting at position START. */ extern regoff_t re_match (struct re_pattern_buffer *__buffer, const char *__String, regoff_t __length, - regoff_t __start, struct re_registers *__regs); + regoff_t __start, struct re_registers *__regs) + _Attr_access_ ((__read_only__, 2, 3)); /* Relates to 're_match' as 're_search_2' relates to 're_search'. */ @@ -578,7 +609,9 @@ extern regoff_t re_match_2 (struct re_pattern_buffer *__buffer, const char *__string1, regoff_t __length1, const char *__string2, regoff_t __length2, regoff_t __start, struct re_registers *__regs, - regoff_t __stop); + regoff_t __stop) + _Attr_access_ ((__read_only__, 2, 3)) + _Attr_access_ ((__read_only__, 4, 5)); /* Set REGS to hold NUM_REGS registers, storing them in STARTS and @@ -648,13 +681,18 @@ extern int regcomp (regex_t *_Restrict_ __preg, extern int regexec (const regex_t *_Restrict_ __preg, const char *_Restrict_ __String, size_t __nmatch, regmatch_t __pmatch[_Restrict_arr_], - int __eflags); + int __eflags) + _Attr_access_ ((__write_only__, 4, 3)); extern size_t regerror (int __errcode, const regex_t *_Restrict_ __preg, - char *_Restrict_ __errbuf, size_t __errbuf_size); + char *_Restrict_ __errbuf, size_t __errbuf_size) + _Attr_access_ ((__write_only__, 3, 4)); extern void regfree (regex_t *__preg); +#if defined __GNUC__ && 4 < __GNUC__ + (6 <= __GNUC_MINOR__) +# pragma GCC diagnostic pop +#endif #ifdef __cplusplus } diff --git a/lib/regexec.c b/lib/regexec.c index 5e4eb497a6..da1fb7fafa 100644 --- a/lib/regexec.c +++ b/lib/regexec.c @@ -31,11 +31,11 @@ static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop, static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, re_dfastate_t **limited_sts, Idx last_node, Idx last_str_idx); -static reg_errcode_t re_search_internal (const regex_t *preg, - const char *string, Idx length, - Idx start, Idx last_start, Idx stop, - size_t nmatch, regmatch_t pmatch[], - int eflags); +static reg_errcode_t +re_search_internal (const regex_t *preg, const char *string, Idx length, + Idx start, Idx last_start, Idx stop, size_t nmatch, + regmatch_t pmatch[__ARG_NELTS (static nmatch)], + int eflags); static regoff_t re_search_2_stub (struct re_pattern_buffer *bufp, const char *string1, Idx length1, const char *string2, Idx length2, @@ -47,23 +47,29 @@ static regoff_t re_search_stub (struct re_pattern_buffer *bufp, regoff_t range, Idx stop, struct re_registers *regs, bool ret_len); -static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, - Idx nregs, int regs_allocated); +static unsigned re_copy_regs (struct re_registers *regs, Idx nregs, + regmatch_t pmatch[__ARG_NELTS (static nregs)], + int regs_allocated); static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx); static Idx check_matching (re_match_context_t *mctx, bool fl_longest_match, Idx *p_match_first); static Idx check_halt_state_context (const re_match_context_t *mctx, const re_dfastate_t *state, Idx idx); -static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, - regmatch_t *prev_idx_match, Idx cur_node, - Idx cur_idx, Idx nmatch); -static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs, - Idx str_idx, Idx dest_node, Idx nregs, - regmatch_t *regs, regmatch_t *prevregs, - re_node_set *eps_via_nodes); +static void +update_regs (const re_dfa_t *dfa, Idx nmatch, + regmatch_t pmatch[__ARG_NELTS (static nmatch)], + regmatch_t prev_idx_match[__ARG_NELTS (static nmatch)], + Idx cur_node, Idx cur_idx); +static reg_errcode_t +push_fail_stack (struct re_fail_stack_t *fs, + Idx str_idx, Idx dest_node, Idx nregs, + regmatch_t regs[__ARG_NELTS (static nregs)], + regmatch_t prevregs[__ARG_NELTS (static nregs)], + re_node_set *eps_via_nodes); static reg_errcode_t set_regs (const regex_t *preg, const re_match_context_t *mctx, - size_t nmatch, regmatch_t *pmatch, + size_t nmatch, + regmatch_t pmatch[__ARG_NELTS (static nmatch)], bool fl_backtrack); static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs); @@ -191,7 +197,7 @@ static reg_errcode_t extend_buffers (re_match_context_t *mctx, int min_len); int regexec (const regex_t *__restrict preg, const char *__restrict string, - size_t nmatch, regmatch_t pmatch[], int eflags) + size_t nmatch, regmatch_t pmatch[__ARG_NELTS (nmatch)], int eflags) { reg_errcode_t err; Idx start, length; @@ -212,12 +218,8 @@ regexec (const regex_t *__restrict preg, const char *__restrict string, } lock_lock (dfa->lock); - if (preg->no_sub) - err = re_search_internal (preg, string, length, start, length, - length, 0, NULL, eflags); - else - err = re_search_internal (preg, string, length, start, length, - length, nmatch, pmatch, eflags); + err = re_search_internal (preg, string, length, start, length, + length, preg->no_sub ? 0 : nmatch, pmatch, eflags); lock_unlock (dfa->lock); return err != REG_NOERROR; } @@ -235,7 +237,7 @@ int attribute_compat_text_section __compat_regexec (const regex_t *__restrict preg, const char *__restrict string, size_t nmatch, - regmatch_t pmatch[], int eflags) + regmatch_t pmatch[__ARG_NELTS (nmatch)], int eflags) { return regexec (preg, string, nmatch, pmatch, eflags & (REG_NOTBOL | REG_NOTEOL)); @@ -434,7 +436,7 @@ re_search_stub (struct re_pattern_buffer *bufp, const char *string, Idx length, else if (regs != NULL) { /* If caller wants register contents data back, copy them. */ - bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs, + bufp->regs_allocated = re_copy_regs (regs, nregs, pmatch, bufp->regs_allocated); if (__glibc_unlikely (bufp->regs_allocated == REGS_UNALLOCATED)) rval = -2; @@ -457,7 +459,8 @@ re_search_stub (struct re_pattern_buffer *bufp, const char *string, Idx length, } static unsigned -re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, Idx nregs, +re_copy_regs (struct re_registers *regs, Idx nregs, + regmatch_t pmatch[__ARG_NELTS (static nregs)], int regs_allocated) { int rval = REGS_REALLOCATE; @@ -585,7 +588,8 @@ static reg_errcode_t __attribute_warn_unused_result__ re_search_internal (const regex_t *preg, const char *string, Idx length, Idx start, Idx last_start, Idx stop, size_t nmatch, - regmatch_t pmatch[], int eflags) + regmatch_t pmatch[__ARG_NELTS (static nmatch)], + int eflags) { reg_errcode_t err; const re_dfa_t *dfa = preg->buffer; @@ -1210,8 +1214,9 @@ check_halt_state_context (const re_match_context_t *mctx, return -1 on match failure, -2 on error. */ static Idx -proceed_next_node (const re_match_context_t *mctx, Idx nregs, regmatch_t *regs, - regmatch_t *prevregs, +proceed_next_node (const re_match_context_t *mctx, Idx nregs, + regmatch_t regs[__ARG_NELTS (static nregs)], + regmatch_t prevregs[__ARG_NELTS (static nregs)], Idx *pidx, Idx node, re_node_set *eps_via_nodes, struct re_fail_stack_t *fs) { @@ -1321,7 +1326,9 @@ proceed_next_node (const re_match_context_t *mctx, Idx nregs, regmatch_t *regs, static reg_errcode_t __attribute_warn_unused_result__ push_fail_stack (struct re_fail_stack_t *fs, Idx str_idx, Idx dest_node, - Idx nregs, regmatch_t *regs, regmatch_t *prevregs, + Idx nregs, + regmatch_t regs[__ARG_NELTS (static nregs)], + regmatch_t prevregs[__ARG_NELTS (static nregs)], re_node_set *eps_via_nodes) { reg_errcode_t err; @@ -1349,7 +1356,8 @@ push_fail_stack (struct re_fail_stack_t *fs, Idx str_idx, Idx dest_node, static Idx pop_fail_stack (struct re_fail_stack_t *fs, Idx *pidx, Idx nregs, - regmatch_t *regs, regmatch_t *prevregs, + regmatch_t regs[__ARG_NELTS (static nregs)], + regmatch_t prevregs[__ARG_NELTS (static nregs)], re_node_set *eps_via_nodes) { if (fs == NULL || fs->num == 0) @@ -1379,7 +1387,7 @@ pop_fail_stack (struct re_fail_stack_t *fs, Idx *pidx, Idx nregs, static reg_errcode_t __attribute_warn_unused_result__ set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch, - regmatch_t *pmatch, bool fl_backtrack) + regmatch_t pmatch[__ARG_NELTS (static nmatch)], bool fl_backtrack) { const re_dfa_t *dfa = preg->buffer; Idx idx, cur_node; @@ -1415,7 +1423,7 @@ set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch, for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;) { - update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch); + update_regs (dfa, nmatch, pmatch, prev_idx_match, cur_node, idx); if ((idx == pmatch[0].rm_eo && cur_node == mctx->last_node) || (fs && re_node_set_contains (&eps_via_nodes, cur_node))) @@ -1487,8 +1495,10 @@ free_fail_stack_return (struct re_fail_stack_t *fs) } static void -update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, - regmatch_t *prev_idx_match, Idx cur_node, Idx cur_idx, Idx nmatch) +update_regs (const re_dfa_t *dfa, Idx nmatch, + regmatch_t pmatch[__ARG_NELTS (static nmatch)], + regmatch_t prev_idx_match[__ARG_NELTS (static nmatch)], + Idx cur_node, Idx cur_idx) { int type = dfa->nodes[cur_node].type; if (type == OP_OPEN_SUBEXP) -- 2.31.1