]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
xgettext: Recognize 'env' and assignments in Shell scripts.
authorBruno Haible <bruno@clisp.org>
Mon, 16 Sep 2019 00:47:44 +0000 (02:47 +0200)
committerBruno Haible <bruno@clisp.org>
Mon, 16 Sep 2019 00:47:44 +0000 (02:47 +0200)
Reported by Jason Vas Dias <jason.vas.dias@gmail.com>
in <https://lists.gnu.org/archive/html/bug-gettext/2019-08/msg00032.html>.

* 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.

NEWS
gettext-tools/src/x-sh.c
gettext-tools/tests/Makefile.am
gettext-tools/tests/xgettext-sh-7 [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index 3730e8ee27ac5126f922cc2f6503800827f7ce11..69dba3d265d4c59f5d92f09332dc794b08076101 100644 (file)
--- 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.)
index 2470bf19042802ea8d8197c13743eed3be70bdab..2d863aae5721bbb85ac35951f3cb58fbbcd116f5 100644 (file)
@@ -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.
+     <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_235>
+   */
+  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);
                 }
index b32849483903c71ae25171219bfad442fedded04..0b68a964ec1b887f78847adc8ecd4c94c6665f58 100644 (file)
@@ -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 (executable)
index 0000000..738803e
--- /dev/null
@@ -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