From: Bruno Haible Date: Fri, 8 May 2026 20:44:03 +0000 (+0200) Subject: xgettext: Refactor --keyword value checking. X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=f947aff99c5719e25f4301a2cf686499baa8b72f;p=thirdparty%2Fgettext.git xgettext: Refactor --keyword value checking. * autogen.sh (GNULIB_MODULES_TOOLS_FOR_SRC): Add memrchr. * gettext-tools/src/xg-arglist-callshape.h (split_keywordspec_ok, split_keywordspec_ok2, split_keywordspec_ok_lisp): New declarations. * gettext-tools/src/xg-arglist-callshape.c (split_keywordspec_ok, split_keywordspec_ok2, split_keywordspec_ok_lisp): New functions. * gettext-tools/src/x-awk.c (x_awk_keyword): Call split_keywordspec_ok. * gettext-tools/src/x-c.c (add_keyword): Likewise. * gettext-tools/src/x-csharp.c (x_csharp_keyword): Likewise. * gettext-tools/src/x-d.c (x_d_keyword): Likewise. * gettext-tools/src/x-elisp.c (x_elisp_keyword): Likewise. * gettext-tools/src/x-go.c (x_go_keyword): Likewise. * gettext-tools/src/x-java.c (x_java_keyword): Likewise. * gettext-tools/src/x-javascript.c (x_javascript_keyword): Likewise. * gettext-tools/src/x-librep.c (x_librep_keyword): Likewise. * gettext-tools/src/x-lua.c (x_lua_keyword): Likewise. * gettext-tools/src/x-modula2.c (x_modula2_keyword): Likewise. * gettext-tools/src/x-ocaml.c (x_ocaml_keyword): Likewise. * gettext-tools/src/x-perl.c (x_perl_keyword): Likewise. * gettext-tools/src/x-php.c (x_php_keyword): Likewise. * gettext-tools/src/x-python.c (x_python_keyword): Likewise. * gettext-tools/src/x-rust.c (x_rust_keyword): Likewise. * gettext-tools/src/x-sh.c (x_sh_keyword): Likewise. * gettext-tools/src/x-typescript-impl.h (NOTE_OPTION_KEYWORD): Likewise. * gettext-tools/src/x-vala.c (add_keyword): Likewise. * gettext-tools/src/x-tcl.c (x_tcl_keyword): Call split_keywordspec_ok2. * gettext-tools/src/x-lisp.c (x_lisp_keyword): Call split_keywordspec_ok_lisp. * gettext-tools/src/x-scheme.c (x_scheme_keyword): Likewise. --- diff --git a/autogen.sh b/autogen.sh index 92b8055e4..54524eb41 100755 --- a/autogen.sh +++ b/autogen.sh @@ -239,6 +239,7 @@ if ! $skip_gnulib; then memchr memeq memmove + memrchr memset minmax mkdir diff --git a/gettext-tools/src/x-awk.c b/gettext-tools/src/x-awk.c index c9848154f..a63682b6e 100644 --- a/gettext-tools/src/x-awk.c +++ b/gettext-tools/src/x-awk.c @@ -81,11 +81,9 @@ x_awk_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid C identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid + C identifier. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-c.c b/gettext-tools/src/x-c.c index 38986c32a..b0c32e61b 100644 --- a/gettext-tools/src/x-c.c +++ b/gettext-tools/src/x-c.c @@ -129,11 +129,9 @@ add_keyword (const char *name, hash_table *keywords) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid C identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid + C identifier. */ insert_keyword_callshape (keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-csharp.c b/gettext-tools/src/x-csharp.c index f55924476..22b259f64 100644 --- a/gettext-tools/src/x-csharp.c +++ b/gettext-tools/src/x-csharp.c @@ -91,12 +91,9 @@ x_csharp_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid C# - identifier sequence with dots. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid C# + identifier sequence with dots. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-d.c b/gettext-tools/src/x-d.c index e5be658af..34e080e10 100644 --- a/gettext-tools/src/x-d.c +++ b/gettext-tools/src/x-d.c @@ -112,13 +112,10 @@ x_d_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid identifier, - possibly with a trailing '!'. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) { + /* The characters between name and end should form a valid identifier, + possibly with a trailing '!'. */ if (end > name && end[-1] == '!') insert_keyword_callshape (&template_keywords, name, end - 1 - name, &shape); diff --git a/gettext-tools/src/x-elisp.c b/gettext-tools/src/x-elisp.c index 409f69002..aa809e744 100644 --- a/gettext-tools/src/x-elisp.c +++ b/gettext-tools/src/x-elisp.c @@ -94,11 +94,9 @@ x_elisp_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid Lisp - symbol. */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid Lisp + symbol. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-go.c b/gettext-tools/src/x-go.c index 5145db57c..dd5650b7c 100644 --- a/gettext-tools/src/x-go.c +++ b/gettext-tools/src/x-go.c @@ -683,10 +683,7 @@ x_go_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) { /* The characters between name and end should form - either a valid Go identifier, diff --git a/gettext-tools/src/x-java.c b/gettext-tools/src/x-java.c index 8bb265f4b..4b7e4293e 100644 --- a/gettext-tools/src/x-java.c +++ b/gettext-tools/src/x-java.c @@ -94,12 +94,9 @@ x_java_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid Java - identifier sequence with dots. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid Java + identifier sequence with dots. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-javascript.c b/gettext-tools/src/x-javascript.c index 08338a827..7c4726290 100644 --- a/gettext-tools/src/x-javascript.c +++ b/gettext-tools/src/x-javascript.c @@ -101,11 +101,9 @@ x_javascript_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid C identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid + C identifier. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-librep.c b/gettext-tools/src/x-librep.c index 4d74ec2c9..98a3c9f91 100644 --- a/gettext-tools/src/x-librep.c +++ b/gettext-tools/src/x-librep.c @@ -95,11 +95,9 @@ x_librep_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid Lisp - symbol. */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid Lisp + symbol. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-lisp.c b/gettext-tools/src/x-lisp.c index 184c9d9ec..797f81c7d 100644 --- a/gettext-tools/src/x-lisp.c +++ b/gettext-tools/src/x-lisp.c @@ -136,28 +136,24 @@ x_lisp_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid Lisp symbol. - Extract the symbol name part. */ - const char *colon = strchr (name, ':'); - if (colon != NULL && colon < end) + if (split_keywordspec_ok_lisp (name, end - name)) { - name = colon + 1; - if (name < end && *name == ':') - name++; - colon = strchr (name, ':'); - if (colon != NULL && colon < end) - return; + /* The characters between name and end should form a valid Lisp + symbol. */ + /* Extract the symbol name part. */ + const char *colon = memrchr (name, ':', end - name); + if (colon != NULL) + name = colon + 1; + + /* Uppercase it. */ + size_t len = end - name; + char *symname = XNMALLOC (len, char); + for (size_t i = 0; i < len; i++) + symname[i] = + (name[i] >= 'a' && name[i] <= 'z' ? name[i] - 'a' + 'A' : name[i]); + + insert_keyword_callshape (&keywords, symname, len, &shape); } - - /* Uppercase it. */ - size_t len = end - name; - char *symname = XNMALLOC (len, char); - for (size_t i = 0; i < len; i++) - symname[i] = - (name[i] >= 'a' && name[i] <= 'z' ? name[i] - 'a' + 'A' : name[i]); - - insert_keyword_callshape (&keywords, symname, len, &shape); } } diff --git a/gettext-tools/src/x-lua.c b/gettext-tools/src/x-lua.c index 7cea7688b..61b54f681 100644 --- a/gettext-tools/src/x-lua.c +++ b/gettext-tools/src/x-lua.c @@ -82,11 +82,9 @@ x_lua_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid C identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid + C identifier. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-modula2.c b/gettext-tools/src/x-modula2.c index 521fb83d0..02b3e12d8 100644 --- a/gettext-tools/src/x-modula2.c +++ b/gettext-tools/src/x-modula2.c @@ -83,12 +83,9 @@ x_modula2_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid Modula-2 - identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid Modula-2 + identifier. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-ocaml.c b/gettext-tools/src/x-ocaml.c index a425e99c0..d8d8e81bb 100644 --- a/gettext-tools/src/x-ocaml.c +++ b/gettext-tools/src/x-ocaml.c @@ -97,11 +97,9 @@ x_ocaml_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid + identifier. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-perl.c b/gettext-tools/src/x-perl.c index 937021cef..337434251 100644 --- a/gettext-tools/src/x-perl.c +++ b/gettext-tools/src/x-perl.c @@ -100,11 +100,9 @@ x_perl_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid C identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid + C identifier. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-php.c b/gettext-tools/src/x-php.c index 071e188b1..497544afd 100644 --- a/gettext-tools/src/x-php.c +++ b/gettext-tools/src/x-php.c @@ -88,11 +88,9 @@ x_php_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid C identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid + C identifier. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-python.c b/gettext-tools/src/x-python.c index 66e54d110..92615209d 100644 --- a/gettext-tools/src/x-python.c +++ b/gettext-tools/src/x-python.c @@ -101,11 +101,9 @@ x_python_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid C identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid + C identifier. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-rust.c b/gettext-tools/src/x-rust.c index 687d3657f..c3a8e99ff 100644 --- a/gettext-tools/src/x-rust.c +++ b/gettext-tools/src/x-rust.c @@ -95,13 +95,10 @@ x_rust_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid Rust - identifier, possibly with a trailing '!'. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) { + /* The characters between name and end should form a valid Rust + identifier, possibly with a trailing '!'. */ if (end > name && end[-1] == '!') insert_keyword_callshape (¯o_keywords, name, end - 1 - name, &shape); diff --git a/gettext-tools/src/x-scheme.c b/gettext-tools/src/x-scheme.c index d2cca1864..6b7a50b9f 100644 --- a/gettext-tools/src/x-scheme.c +++ b/gettext-tools/src/x-scheme.c @@ -127,21 +127,17 @@ x_scheme_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid Lisp symbol. - Extract the symbol name part. */ - const char *colon = strchr (name, ':'); - if (colon != NULL && colon < end) + if (split_keywordspec_ok_lisp (name, end - name)) { - name = colon + 1; - if (name < end && *name == ':') - name++; - colon = strchr (name, ':'); - if (colon != NULL && colon < end) - return; + /* The characters between name and end should form a valid Lisp + symbol. */ + /* Extract the symbol name part. */ + const char *colon = memrchr (name, ':', end - name); + if (colon != NULL) + name = colon + 1; + + insert_keyword_callshape (&keywords, name, end - name, &shape); } - - insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-sh.c b/gettext-tools/src/x-sh.c index 48fb15e30..80c22a130 100644 --- a/gettext-tools/src/x-sh.c +++ b/gettext-tools/src/x-sh.c @@ -101,11 +101,9 @@ x_sh_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid C identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid + C identifier. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-tcl.c b/gettext-tools/src/x-tcl.c index 944efccf4..4e3be4c9e 100644 --- a/gettext-tools/src/x-tcl.c +++ b/gettext-tools/src/x-tcl.c @@ -100,13 +100,15 @@ x_tcl_keyword (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); + if (split_keywordspec_ok2 (name, end - name)) + { + /* The characters between name and end should form a valid Tcl + function name. A leading "::" is redundant. */ + if (end - name >= 2 && name[0] == ':' && name[1] == ':') + name += 2; - /* The characters between name and end should form a valid Tcl - function name. A leading "::" is redundant. */ - if (end - name >= 2 && name[0] == ':' && name[1] == ':') - name += 2; - - insert_keyword_callshape (&keywords, name, end - name, &shape); + insert_keyword_callshape (&keywords, name, end - name, &shape); + } } } diff --git a/gettext-tools/src/x-typescript-impl.h b/gettext-tools/src/x-typescript-impl.h index c3d97a8a5..c617e285e 100644 --- a/gettext-tools/src/x-typescript-impl.h +++ b/gettext-tools/src/x-typescript-impl.h @@ -87,11 +87,9 @@ NOTE_OPTION_KEYWORD (const char *name) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid + identifier. */ insert_keyword_callshape (&keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/x-vala.c b/gettext-tools/src/x-vala.c index 14046c2ea..b3905e396 100644 --- a/gettext-tools/src/x-vala.c +++ b/gettext-tools/src/x-vala.c @@ -92,11 +92,9 @@ add_keyword (const char *name, hash_table *keywords) const char *end; struct callshape shape; split_keywordspec (name, &end, &shape); - - /* The characters between name and end should form a valid C identifier. - A colon means an invalid parse in split_keywordspec(). */ - const char *colon = strchr (name, ':'); - if (colon == NULL || colon >= end) + if (split_keywordspec_ok (name, end - name)) + /* The characters between name and end should form a valid + C identifier. */ insert_keyword_callshape (keywords, name, end - name, &shape); } } diff --git a/gettext-tools/src/xg-arglist-callshape.c b/gettext-tools/src/xg-arglist-callshape.c index cd92f6f4b..a6197c09f 100644 --- a/gettext-tools/src/xg-arglist-callshape.c +++ b/gettext-tools/src/xg-arglist-callshape.c @@ -163,6 +163,48 @@ split_keywordspec (const char *spec, string_list_destroy (&xcomments); } +bool +split_keywordspec_ok (const char *keyword, size_t keyword_len) +{ + /* If the keyword contains a colon, it means an invalid parse + in split_keywordspec(). */ + return memchr (keyword, ':', keyword_len) == NULL; +} + +bool +split_keywordspec_ok2 (const char *keyword, size_t keyword_len) +{ + /* If the keyword contains a colon that is not part of a colon pair, + it means an invalid parse in split_keywordspec(). */ + const char *keyword_end = keyword + keyword_len; + for (const char *p = keyword; p < keyword_end; ) + { + const char *colon = memchr (p, ':', keyword_end - p); + if (colon == NULL) + break; + if (!(colon + 1 < keyword_end && colon[1] == ':')) + /* Found a colon that is not part of a colon pair. */ + return false; + /* Found a colon pair. */ + p = colon + 2; + } + return true; +} + +bool +split_keywordspec_ok_lisp (const char *keyword, size_t keyword_len) +{ + /* If the keyword contains more than a single colon or a colon pair, + it means an invalid parse in split_keywordspec(). */ + const char *keyword_end = keyword + keyword_len; + const char *colon = memchr (keyword, ':', keyword_end - keyword); + if (colon == NULL) + return true; + if (colon + 1 < keyword_end && colon[1] == ':') + colon++; + return memchr (colon, ':', keyword_end - colon) == NULL; +} + void insert_keyword_callshape (hash_table *table, diff --git a/gettext-tools/src/xg-arglist-callshape.h b/gettext-tools/src/xg-arglist-callshape.h index b58301a0b..d5cfc9407 100644 --- a/gettext-tools/src/xg-arglist-callshape.h +++ b/gettext-tools/src/xg-arglist-callshape.h @@ -49,6 +49,21 @@ struct callshape extern void split_keywordspec (const char *spec, const char **endp, struct callshape *shapep); +/* Test whether the preceding split_keywordspec call was successful, + assuming a programming language in which a keyword cannot contain + colons. */ +extern bool split_keywordspec_ok (const char *keyword, size_t keyword_len); + +/* Test whether the preceding split_keywordspec call was successful, + assuming a programming language in which a keyword cannot contain colons, + except in pairs (such as e.g. in C++). */ +extern bool split_keywordspec_ok2 (const char *keyword, size_t keyword_len); + +/* Test whether the preceding split_keywordspec call was successful, + assuming a programming language in which a keyword cannot contain colons, + except a single colon or a pair of colons (such as e.g. in Common Lisp). */ +extern bool split_keywordspec_ok_lisp (const char *keyword, size_t keyword_len); + /* Set of alternative calling conventions for a given keyword. */ struct callshapes {