From: Bruno Haible Date: Mon, 16 Sep 2019 00:47:44 +0000 (+0200) Subject: xgettext: Recognize 'env' and assignments in Shell scripts. X-Git-Tag: v0.21~164 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=63587292db018bf10d9ab9ca9d63dc9ee8cabfc8;p=thirdparty%2Fgettext.git xgettext: Recognize 'env' and assignments in Shell scripts. Reported by Jason Vas Dias in . * gettext-tools/src/x-sh.c (enum word_type): Add t_assignment. (read_word): Recognize assignments. (read_command): In a function position, ignore assignments and 'env' tokens. * gettext-tools/tests/xgettext-sh-7: New file. * gettext-tools/tests/Makefile.am (TESTS): Add it. * NEWS: Mention the change. --- diff --git a/NEWS b/NEWS index 3730e8ee2..69dba3d26 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ Version 0.21 - September 2019 * Programming languages support: - Shell: + o xgettext now recognizes and ignores 'env' invocations and environment + variable assignments in front of commands. o The programs 'gettext', 'ngettext', when invoked with option -e, now expand '\\' and octal escape sequences, instead of swallowing them. (Bug present since the beginning.) diff --git a/gettext-tools/src/x-sh.c b/gettext-tools/src/x-sh.c index 2470bf190..2d863aae5 100644 --- a/gettext-tools/src/x-sh.c +++ b/gettext-tools/src/x-sh.c @@ -434,6 +434,7 @@ saw_closing_singlequote () enum word_type { t_string, /* constant string */ + t_assignment, /* variable assignment */ t_other, /* other string */ t_separator, /* command separator: semicolon or newline */ t_redirect, /* redirection: one of < > >| << <<- >> <> <& >& */ @@ -746,6 +747,7 @@ read_word (struct word *wp, int looking_for, flag_context_ty context) { int c; bool all_unquoted_digits; + bool all_unquoted_name_characters; do { @@ -852,7 +854,14 @@ read_word (struct word *wp, int looking_for, flag_context_ty context) wp->token = XMALLOC (struct token); init_token (wp->token); wp->line_number_at_start = line_number; + /* True while all characters in the token seen so far are digits. */ all_unquoted_digits = true; + /* True while all characters in the token seen so far form a "name": + all characters are unquoted underscores, digits, or alphabetics from the + portable character set, and the first character is not a digit. Cf. + + */ + all_unquoted_name_characters = true; for (;; c = phase2_getc ()) { @@ -887,6 +896,17 @@ read_word (struct word *wp, int looking_for, flag_context_ty context) all_unquoted_digits = all_unquoted_digits && (c >= '0' && c <= '9'); + if (all_unquoted_name_characters && wp->token->charcount > 0 && c == '=') + { + wp->type = t_assignment; + continue; + } + + all_unquoted_name_characters = + all_unquoted_name_characters + && ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' + || (wp->token->charcount > 0 && c >= '0' && c <= '9')); + if (c == '$') { int c2; @@ -1305,24 +1325,39 @@ read_command (int looking_for, flag_context_ty outer_context) { /* This is the function position. */ arg = 0; - if (inner.type == t_string) + if (inner.type == t_assignment) + { + /* An assignment just set an environment variable. + Ignore it. */ + } + else if (inner.type == t_string) { char *function_name = string_of_word (&inner); - void *keyword_value; - if (hash_find_entry (&keywords, - function_name, strlen (function_name), - &keyword_value) - == 0) - shapes = (const struct callshapes *) keyword_value; - - argparser = arglist_parser_alloc (mlp, shapes); - - context_iter = - flag_context_list_iterator ( - flag_context_list_table_lookup ( - flag_context_list_table, - function_name, strlen (function_name))); + if (strcmp (function_name, "env") == 0) + { + /* The 'env' command just introduces more assignments. + Ignore it. */ + } + else + { + void *keyword_value; + + if (hash_find_entry (&keywords, + function_name, + strlen (function_name), + &keyword_value) + == 0) + shapes = (const struct callshapes *) keyword_value; + + argparser = arglist_parser_alloc (mlp, shapes); + + context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + function_name, strlen (function_name))); + } free (function_name); } diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index b32849483..0b68a964e 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -111,7 +111,7 @@ TESTS = gettext-1 gettext-2 \ xgettext-scheme-1 xgettext-scheme-2 xgettext-scheme-3 \ xgettext-scheme-4 \ xgettext-sh-1 xgettext-sh-2 xgettext-sh-3 xgettext-sh-4 xgettext-sh-5 \ - xgettext-sh-6 \ + xgettext-sh-6 xgettext-sh-7 \ xgettext-smalltalk-1 xgettext-smalltalk-2 \ xgettext-stringtable-1 \ xgettext-tcl-1 xgettext-tcl-2 xgettext-tcl-3 xgettext-tcl-4 \ diff --git a/gettext-tools/tests/xgettext-sh-7 b/gettext-tools/tests/xgettext-sh-7 new file mode 100755 index 000000000..738803e9a --- /dev/null +++ b/gettext-tools/tests/xgettext-sh-7 @@ -0,0 +1,55 @@ +#!/bin/sh +. "${srcdir=.}/init.sh"; path_prepend_ . ../src + +# Test of Shell support: assignment syntax. + +cat <<\EOF > xg-sh-7.sh +gettext 'immediate invocation' +foo=bar gettext 'invocation with 1 environment variable' +foo=bar FOO=baz gettext 'invocation with 2 environment variables' +env gettext 'invocation with env' +env foo=bar gettext 'invocation with env and 1 environment variable' +env foo=bar FOO=baz gettext 'invocation with env and 2 environment variables' +'foo'=bar gettext 'invocation after a non-assignment 1' +"foo"=bar gettext 'invocation after a non-assignment 2' +fo\o=bar gettext 'invocation after a non-assignment 3' +foo'='bar gettext 'invocation after a non-assignment 4' +foo"="bar gettext 'invocation after a non-assignment 5' +foo\=bar gettext 'invocation after a non-assignment 6' +7=bar gettext 'invocation after a non-assignment 7' +océ=bar gettext 'invocation after a non-assignment 8' +f0oO_=bar gettext 'invocation with a mixed environment variable' +EOF + +: ${XGETTEXT=xgettext} +${XGETTEXT} --omit-header --no-location -d xg-sh-7.tmp xg-sh-7.sh || Exit 1 +LC_ALL=C tr -d '\r' < xg-sh-7.tmp.po > xg-sh-7.po || Exit 1 + +cat <<\EOF > xg-sh-7.ok +msgid "immediate invocation" +msgstr "" + +msgid "invocation with 1 environment variable" +msgstr "" + +msgid "invocation with 2 environment variables" +msgstr "" + +msgid "invocation with env" +msgstr "" + +msgid "invocation with env and 1 environment variable" +msgstr "" + +msgid "invocation with env and 2 environment variables" +msgstr "" + +msgid "invocation with a mixed environment variable" +msgstr "" +EOF + +: ${DIFF=diff} +${DIFF} xg-sh-7.ok xg-sh-7.po +result=$? + +exit $result