]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
xgettext: Recognize gettext -e invocations in shell parser.
authorBruno Haible <bruno@clisp.org>
Mon, 19 Aug 2019 08:52:36 +0000 (10:52 +0200)
committerBruno Haible <bruno@clisp.org>
Mon, 13 Apr 2020 11:08:56 +0000 (13:08 +0200)
Reported by Eugene V. Lyubimkin <jackyf.devel@gmail.com>
in <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=507091>.

* gettext-runtime/src/escapes.h: New file, extracted from
gettext-runtime/src/gettext.c.
* gettext-runtime/src/gettext.c: Include escapes.h.
(expand_escape): Remove function.
(inhibit_added_newline): New variable.
(add_newline): Remove variable.
(main): Initialize inhibit_added_newline. Invoke expand_escapes instead of
expand_escape.
* gettext-runtime/src/ngettext.c: Include escapes.h.
(expand_escape): Remove function.
(main): Invoke expand_escapes instead of expand_escape.
* gettext-runtime/src/Makefile.am (gettext_SOURCES, ngettext_SOURCES): Add
escapes.h.
* gettext-tools/tests/tstgettext.c: Include escapes.h.
(expand_escape): Remove function.
(inhibit_added_newline): New variable.
(add_newline): Remove variable.
(main): Initialize inhibit_added_newline. Invoke expand_escapes instead of
expand_escape.
* gettext-tools/tests/Makefile.am (tstgettext_SOURCES): Add escapes.h.
* gettext-tools/src/x-sh.c: Include escapes.h.
(read_command): Recognize a '-e' option in the argument list of gettext and
ngettext.
* gettext-tools/src/Makefile.am (xgettext_SOURCES): Add escapes.h.
* gettext-tools/tests/xgettext-sh-1: Test the recognition of gettext -e
arguments.
* NEWS: Mention the change.

NEWS
gettext-runtime/src/Makefile.am
gettext-runtime/src/escapes.h [new file with mode: 0644]
gettext-runtime/src/gettext.c
gettext-runtime/src/ngettext.c
gettext-tools/src/Makefile.am
gettext-tools/src/x-sh.c
gettext-tools/tests/Makefile.am
gettext-tools/tests/tstgettext.c
gettext-tools/tests/xgettext-sh-1

diff --git a/NEWS b/NEWS
index a80d9c8eeca5a729115443a31e675e717a42ed13..32050e96f11792040ff1eac90fcfb4cf62a9ee21 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,9 +2,12 @@ Version 0.20.2 - April 2020
 
 * Programming languages support:
   - Shell:
-    The programs 'gettext', 'ngettext', when invoked with option -e, now
-    expand '\\' and octal escape sequences, instead of swallowing them.
-    (Bug present since the beginning.)
+    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.)
+    o xgettext now recognizes 'gettext' program invocations with the '-e'
+      option, such as
+        gettext -e 'some\nstring\n'
   - Desktop Entry:
     The value of the 'Icon' property is no longer extracted into the POT file
     by xgettext.  The documentation explains how to localize icons.
index e3c161ee374cff114a55b06591b8e70d392ab989..e86003468a8c8c6d923b70e1684281fc7ab3d3ae 100644 (file)
@@ -1,5 +1,5 @@
 ## Makefile for the gettext-runtime/src subdirectory of GNU gettext
-## Copyright (C) 1995-1998, 2000-2007, 2009 Free Software Foundation, Inc.
+## Copyright (C) 1995-1998, 2000-2007, 2009, 2019 Free Software Foundation, Inc.
 ##
 ## This program is free software: you can redistribute it and/or modify
 ## it under the terms of the GNU General Public License as published by
@@ -34,8 +34,8 @@ AM_CPPFLAGS = \
 DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
 
 # Source dependencies.
-gettext_SOURCES = gettext.c
-ngettext_SOURCES = ngettext.c
+gettext_SOURCES = gettext.c escapes.h
+ngettext_SOURCES = ngettext.c escapes.h
 envsubst_SOURCES = envsubst.c
 
 # Link dependencies.
diff --git a/gettext-runtime/src/escapes.h b/gettext-runtime/src/escapes.h
new file mode 100644 (file)
index 0000000..acda054
--- /dev/null
@@ -0,0 +1,133 @@
+/* Expand escape sequences in a string.
+   Copyright (C) 1995-1997, 2000-2007, 2012, 2018-2019 Free Software
+   Foundation, Inc.
+   Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, May 1995.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Expand some escape sequences found in the argument string.
+   If backslash_c_seen is != NULL, '\c' sequences are recognized and
+   have the effect of setting *backslash_c_seen to true.
+   Returns either the argument string or a freshly allocated string.  */
+static const char *
+expand_escapes (const char *str, bool *backslash_c_seen)
+{
+  const char *cp = str;
+
+  /* Find the location of the first escape sequence.
+     If the string contains no escape sequences, return it right away.  */
+  for (;;)
+    {
+      while (cp[0] != '\0' && cp[0] != '\\')
+        ++cp;
+      if (cp[0] == '\0')
+        /* The argument string contains no escape sequence.  */
+        return str;
+      /* Found a backslash.  */
+      if (cp[1] == '\0')
+        return str;
+      if (strchr ("abcfnrtv\\01234567", cp[1]) != NULL)
+        break;
+      ++cp;
+    }
+
+  {
+    char *retval = XNMALLOC (strlen (str), char);
+
+    memcpy (retval, str, cp - str);
+    {
+      char *rp = retval + (cp - str);
+
+      do
+        {
+          /* Here cp[0] == '\\'.  */
+          switch (*++cp)
+            {
+            case 'a':               /* alert */
+              *rp++ = '\a';
+              ++cp;
+              break;
+            case 'b':               /* backspace */
+              *rp++ = '\b';
+              ++cp;
+              break;
+            case 'f':               /* form feed */
+              *rp++ = '\f';
+              ++cp;
+              break;
+            case 'n':               /* new line */
+              *rp++ = '\n';
+              ++cp;
+              break;
+            case 'r':               /* carriage return */
+              *rp++ = '\r';
+              ++cp;
+              break;
+            case 't':               /* horizontal tab */
+              *rp++ = '\t';
+              ++cp;
+              break;
+            case 'v':               /* vertical tab */
+              *rp++ = '\v';
+              ++cp;
+              break;
+            case '\\':
+              *rp++ = '\\';
+              ++cp;
+              break;
+            case '0': case '1': case '2': case '3':
+            case '4': case '5': case '6': case '7':
+              {
+                int ch = *cp++ - '0';
+
+                if (*cp >= '0' && *cp <= '7')
+                  {
+                    ch *= 8;
+                    ch += *cp++ - '0';
+
+                    if (*cp >= '0' && *cp <= '7')
+                      {
+                        ch *= 8;
+                        ch += *cp++ - '0';
+                      }
+                  }
+                *rp++ = ch;
+              }
+              break;
+            case 'c':
+              if (backslash_c_seen != NULL)
+                {
+                  *backslash_c_seen = true;
+                  ++cp;
+                  break;
+                }
+              /* FALLTHROUGH */
+            default:
+              *rp++ = '\\';
+              break;
+            }
+
+          /* Find the next escape sequence.  */
+          while (cp[0] != '\0' && cp[0] != '\\')
+            *rp++ = *cp++;
+        }
+      while (cp[0] != '\0');
+
+      /* Terminate the resulting string.  */
+      *rp = '\0';
+    }
+
+    return retval;
+  }
+}
index ac50ad4c777f885b73c23c7f4b29d21cfa0b0912..95f4b735ecabcabbd1dd54bb812049f1dd39a95f 100644 (file)
 #include "basename.h"
 #include "xalloc.h"
 #include "propername.h"
+#include "escapes.h"
 #include "gettext.h"
 
 #define _(str) gettext (str)
 
-/* If true, add newline after last string.  This makes only sense in
+/* If false, add newline after last string.  This makes only sense in
    the 'echo' emulation mode.  */
-static bool add_newline;
+static bool inhibit_added_newline;
 
 /* If true, expand escape sequences in strings before looking in the
    message catalog.  */
@@ -63,7 +64,6 @@ static void usage (int status)
      __attribute__ ((noreturn))
 #endif
 ;
-static const char *expand_escape (const char *str);
 
 int
 main (int argc, char *argv[])
@@ -78,7 +78,7 @@ main (int argc, char *argv[])
   const char *domain = getenv ("TEXTDOMAIN");
   const char *domaindir = getenv ("TEXTDOMAINDIR");
   const char *context = NULL;
-  add_newline = true;
+  inhibit_added_newline = false;
   do_expand = false;
 
   /* Set program name for message texts.  */
@@ -117,7 +117,7 @@ main (int argc, char *argv[])
       do_help = true;
       break;
     case 'n':
-      add_newline = false;
+      inhibit_added_newline = true;
       break;
     case 's':
       do_shell = true;
@@ -175,7 +175,7 @@ There is NO WARRANTY, to the extent permitted by law.\n\
 
       /* Expand escape sequences if enabled.  */
       if (do_expand)
-        msgid = expand_escape (msgid);
+        msgid = expand_escapes (msgid, &inhibit_added_newline);
 
       /* If no domain name is given we don't translate.  */
       if (domain == NULL || domain[0] == '\0')
@@ -215,7 +215,7 @@ There is NO WARRANTY, to the extent permitted by law.\n\
 
               /* Expand escape sequences if enabled.  */
               if (do_expand)
-                msgid = expand_escape (msgid);
+                msgid = expand_escapes (msgid, &inhibit_added_newline);
 
               /* Write out the result.  */
               fputs ((domain == NULL ? msgid :
@@ -232,7 +232,7 @@ There is NO WARRANTY, to the extent permitted by law.\n\
         }
 
       /* If not otherwise told: add trailing newline.  */
-      if (add_newline)
+      if (!inhibit_added_newline)
         fputc ('\n', stdout);
     }
 
@@ -307,109 +307,3 @@ or by email to <%s>.\n"),
 
   exit (status);
 }
-
-
-/* Expand some escape sequences found in the argument string.  */
-static const char *
-expand_escape (const char *str)
-{
-  char *retval, *rp;
-  const char *cp = str;
-
-  /* Find the location of the first escape sequence.
-     If the string contains no escape sequences, return it right away.  */
-  for (;;)
-    {
-      while (cp[0] != '\0' && cp[0] != '\\')
-        ++cp;
-      if (cp[0] == '\0')
-        return str;
-      /* Found a backslash.  */
-      if (cp[1] == '\0')
-        return str;
-      if (strchr ("abcfnrtv\\01234567", cp[1]) != NULL)
-        break;
-      ++cp;
-    }
-
-  retval = XNMALLOC (strlen (str), char);
-
-  rp = retval + (cp - str);
-  memcpy (retval, str, cp - str);
-
-  do
-    {
-      /* Here cp[0] == '\\'.  */
-      switch (*++cp)
-        {
-        case 'a':               /* alert */
-          *rp++ = '\a';
-          ++cp;
-          break;
-        case 'b':               /* backspace */
-          *rp++ = '\b';
-          ++cp;
-          break;
-        case 'c':               /* suppress trailing newline */
-          add_newline = false;
-          ++cp;
-          break;
-        case 'f':               /* form feed */
-          *rp++ = '\f';
-          ++cp;
-          break;
-        case 'n':               /* new line */
-          *rp++ = '\n';
-          ++cp;
-          break;
-        case 'r':               /* carriage return */
-          *rp++ = '\r';
-          ++cp;
-          break;
-        case 't':               /* horizontal tab */
-          *rp++ = '\t';
-          ++cp;
-          break;
-        case 'v':               /* vertical tab */
-          *rp++ = '\v';
-          ++cp;
-          break;
-        case '\\':
-          *rp++ = '\\';
-          ++cp;
-          break;
-        case '0': case '1': case '2': case '3':
-        case '4': case '5': case '6': case '7':
-          {
-            int ch = *cp++ - '0';
-
-            if (*cp >= '0' && *cp <= '7')
-              {
-                ch *= 8;
-                ch += *cp++ - '0';
-
-                if (*cp >= '0' && *cp <= '7')
-                  {
-                    ch *= 8;
-                    ch += *cp++ - '0';
-                  }
-              }
-            *rp++ = ch;
-          }
-          break;
-        default:
-          *rp++ = '\\';
-          break;
-        }
-
-      /* Find the next escape sequence.  */
-      while (cp[0] != '\0' && cp[0] != '\\')
-        *rp++ = *cp++;
-    }
-  while (cp[0] != '\0');
-
-  /* Terminate the resulting string.  */
-  *rp = '\0';
-
-  return retval;
-}
index 685bf7fbbdefd260c15b4868b8a7c7594a572056..88a4c5c6449565548a3084c24aaffca2eea6abab 100644 (file)
@@ -33,6 +33,7 @@
 #include "basename.h"
 #include "xalloc.h"
 #include "propername.h"
+#include "escapes.h"
 #include "gettext.h"
 
 #define _(str) gettext (str)
@@ -57,7 +58,6 @@ static void usage (int status)
      __attribute__ ((noreturn))
 #endif
 ;
-static const char *expand_escape (const char *str);
 
 int
 main (int argc, char *argv[])
@@ -180,8 +180,8 @@ There is NO WARRANTY, to the extent permitted by law.\n\
   /* Expand escape sequences if enabled.  */
   if (do_expand)
     {
-      msgid = expand_escape (msgid);
-      msgid_plural = expand_escape (msgid_plural);
+      msgid = expand_escapes (msgid, NULL);
+      msgid_plural = expand_escapes (msgid_plural, NULL);
     }
 
   /* If no domain name is given we don't translate, and we use English
@@ -270,105 +270,3 @@ or by email to <%s>.\n"),
 
   exit (status);
 }
-
-
-/* Expand some escape sequences found in the argument string.  */
-static const char *
-expand_escape (const char *str)
-{
-  char *retval, *rp;
-  const char *cp = str;
-
-  /* Find the location of the first escape sequence.
-     If the string contains no escape sequences, return it right away.  */
-  for (;;)
-    {
-      while (cp[0] != '\0' && cp[0] != '\\')
-        ++cp;
-      if (cp[0] == '\0')
-        return str;
-      /* Found a backslash.  */
-      if (cp[1] == '\0')
-        return str;
-      if (strchr ("abcfnrtv\\01234567", cp[1]) != NULL)
-        break;
-      ++cp;
-    }
-
-  retval = XNMALLOC (strlen (str), char);
-
-  rp = retval + (cp - str);
-  memcpy (retval, str, cp - str);
-
-  do
-    {
-      /* Here cp[0] == '\\'.  */
-      switch (*++cp)
-        {
-        case 'a':               /* alert */
-          *rp++ = '\a';
-          ++cp;
-          break;
-        case 'b':               /* backspace */
-          *rp++ = '\b';
-          ++cp;
-          break;
-        case 'f':               /* form feed */
-          *rp++ = '\f';
-          ++cp;
-          break;
-        case 'n':               /* new line */
-          *rp++ = '\n';
-          ++cp;
-          break;
-        case 'r':               /* carriage return */
-          *rp++ = '\r';
-          ++cp;
-          break;
-        case 't':               /* horizontal tab */
-          *rp++ = '\t';
-          ++cp;
-          break;
-        case 'v':               /* vertical tab */
-          *rp++ = '\v';
-          ++cp;
-          break;
-        case '\\':
-          *rp++ = '\\';
-          ++cp;
-          break;
-        case '0': case '1': case '2': case '3':
-        case '4': case '5': case '6': case '7':
-          {
-            int ch = *cp++ - '0';
-
-            if (*cp >= '0' && *cp <= '7')
-              {
-                ch *= 8;
-                ch += *cp++ - '0';
-
-                if (*cp >= '0' && *cp <= '7')
-                  {
-                    ch *= 8;
-                    ch += *cp++ - '0';
-                  }
-              }
-            *rp++ = ch;
-          }
-          break;
-        default:
-          *rp++ = '\\';
-          break;
-        }
-
-      /* Find the next escape sequence.  */
-      while (cp[0] != '\0' && cp[0] != '\\')
-        *rp++ = *cp++;
-    }
-  while (cp[0] != '\0');
-
-  /* Terminate the resulting string.  */
-  *rp = '\0';
-
-  return retval;
-}
index 1ec1587a50b449099999e4c445280ccb2b8de78d..5b6c361af1cbbfb1e2f0d6774cc5e445492bf845 100644 (file)
@@ -188,9 +188,26 @@ xgettext_SOURCES += \
   xg-mixed-string.c \
   xg-arglist-context.c xg-arglist-callshape.c xg-arglist-parser.c \
   xg-message.c \
-  x-c.c x-po.c x-sh.c x-python.c x-lisp.c x-elisp.c x-librep.c x-scheme.c \
-  x-smalltalk.c x-java.c x-csharp.c x-awk.c x-ycp.c x-tcl.c x-perl.c x-php.c \
-  x-rst.c x-lua.c x-javascript.c x-vala.c \
+  x-c.c \
+  x-po.c \
+  x-sh.c ../../gettext-runtime/src/escapes.h \
+  x-python.c \
+  x-lisp.c \
+  x-elisp.c \
+  x-librep.c \
+  x-scheme.c \
+  x-smalltalk.c \
+  x-java.c \
+  x-csharp.c \
+  x-awk.c \
+  x-ycp.c \
+  x-tcl.c \
+  x-perl.c \
+  x-php.c \
+  x-rst.c \
+  x-lua.c \
+  x-javascript.c \
+  x-vala.c \
   x-desktop.c
 if !WOE32DLL
 msgattrib_SOURCES = msgattrib.c
index dcfd7895a1dd7e97737bef01e58f5eca35aa9f36..2470bf19042802ea8d8197c13743eed3be70bdab 100644 (file)
@@ -41,6 +41,7 @@
 #include "error-progname.h"
 #include "xalloc.h"
 #include "hash.h"
+#include "../../gettext-runtime/src/escapes.h"
 #include "gettext.h"
 
 #define _(s) gettext(s)
@@ -1241,6 +1242,8 @@ read_command (int looking_for, flag_context_ty outer_context)
      command.  */
   int arg = 0;                  /* Current argument number.  */
   bool arg_of_redirect = false; /* True right after a redirection operator.  */
+  bool must_expand_arg_strings = false; /* True if need to expand escape
+                                           sequences in arguments.  */
   flag_context_list_iterator_ty context_iter;
   const struct callshapes *shapes = NULL;
   struct arglist_parser *argparser = NULL;
@@ -1336,6 +1339,11 @@ read_command (int looking_for, flag_context_ty outer_context)
                       && memcmp (argparser->keyword, "gettext", 7) == 0)
                      || (argparser->keyword_len == 8
                          && memcmp (argparser->keyword, "ngettext", 8) == 0));
+                  bool accepts_expand =
+                    ((argparser->keyword_len == 7
+                      && memcmp (argparser->keyword, "gettext", 7) == 0)
+                     || (argparser->keyword_len == 8
+                         && memcmp (argparser->keyword, "ngettext", 8) == 0));
                   if (accepts_context && argparser->next_is_msgctxt)
                     {
                       char *s = string_of_word (&inner);
@@ -1377,13 +1385,38 @@ read_command (int looking_for, flag_context_ty outer_context)
                                                        inner.line_number_at_start);
                       matters_for_argparser = false;
                     }
+                  else if (accepts_expand
+                           && inner.token->charcount == 2
+                           && memcmp (inner.token->chars, "-e", 2) == 0)
+                    {
+                      must_expand_arg_strings = true;
+                      matters_for_argparser = false;
+                    }
                   else
                     {
                       char *s = string_of_word (&inner);
-                      mixed_string_ty *ms =
-                        mixed_string_alloc_simple (s, lc_string,
-                                                   logical_file_name,
-                                                   inner.line_number_at_start);
+                      mixed_string_ty *ms;
+
+                      /* When '-e' was specified, expand escape sequences in s.  */
+                      if (accepts_expand && must_expand_arg_strings)
+                        {
+                          bool expands_backslash_c =
+                            (argparser->keyword_len == 7
+                             && memcmp (argparser->keyword, "gettext", 7) == 0);
+                          bool backslash_c = false;
+                          char *expanded =
+                            (char *)
+                            expand_escapes (s, expands_backslash_c ? &backslash_c : NULL);
+                          /* We can ignore the value of expands_backslash_c, because
+                             here we don't support the gettext '-s' option.  */
+                          if (expanded != s)
+                            free (s);
+                          s = expanded;
+                        }
+
+                      ms = mixed_string_alloc_simple (s, lc_string,
+                                                      logical_file_name,
+                                                      inner.line_number_at_start);
                       free (s);
                       arglist_parser_remember (argparser, arg, ms,
                                                inner_context,
index b200aa342d4f32fc34a3251bda33b6eec8e5985e..77e710007d2347e312210d8c5a37dee461f0c590 100644 (file)
@@ -234,7 +234,9 @@ LDADD = $(LDADD_@USE_INCLUDED_LIBINTL@) @INTL_MACOSX_LIBS@
 LDADD_yes = ../intl/libintl.la @LTLIBTHREAD@
 LDADD_no = ../intl/libgnuintl.la @LTLIBTHREAD@ @LTLIBINTL@
 check_PROGRAMS = tstgettext tstngettext testlocale intl-1-prg intl-3-prg intl-4-prg intl-5-prg intl-setlocale-1-prg intl-setlocale-2-prg intl-thread-1-prg intl-thread-2-prg intl-thread-3-prg intl-version-prg cake fc3 fc4 fc5 gettextpo-1-prg sentence-1-prg
-tstgettext_SOURCES = tstgettext.c setlocale.c
+tstgettext_SOURCES = \
+  tstgettext.c ../../gettext-runtime/src/escapes.h \
+  setlocale.c
 tstgettext_CFLAGS = -DINSTALLDIR=\".\"
 tstgettext_LDADD = ../gnulib-lib/libgettextlib.la $(LDADD)
 tstngettext_SOURCES = tstngettext.c setlocale.c
index 48fb92a5d5154292fd17b522876a9010d025b0c2..52ba1cea9c50fc733274976b8524005a487854de 100644 (file)
@@ -35,6 +35,7 @@
 #include "xalloc.h"
 #include "propername.h"
 #include "xsetenv.h"
+#include "../../gettext-runtime/src/escapes.h"
 
 /* Make sure we use the included libintl, not the system's one. */
 #undef _LIBINTL_H
@@ -42,9 +43,9 @@
 
 #define _(str) gettext (str)
 
-/* If true, add newline after last string.  This makes only sense in
+/* If false, add newline after last string.  This makes only sense in
    the 'echo' emulation mode.  */
-static bool add_newline;
+static bool inhibit_added_newline;
 
 /* If true, expand escape sequences in strings before looking in the
    message catalog.  */
@@ -67,7 +68,6 @@ static void usage (int status)
      __attribute__ ((noreturn))
 #endif
 ;
-static const char *expand_escape (const char *str);
 
 int
 main (int argc, char *argv[])
@@ -82,7 +82,7 @@ main (int argc, char *argv[])
   bool environ_changed = false;
   const char *domain = getenv ("TEXTDOMAIN");
   const char *domaindir = getenv ("TEXTDOMAINDIR");
-  add_newline = true;
+  inhibit_added_newline = false;
   do_expand = false;
 
   /* Set program name for message texts.  */
@@ -118,7 +118,7 @@ main (int argc, char *argv[])
       do_help = true;
       break;
     case 'n':
-      add_newline = false;
+      inhibit_added_newline = true;
       break;
     case 's':
       do_shell = true;
@@ -193,7 +193,7 @@ There is NO WARRANTY, to the extent permitted by law.\n\
 
       /* Expand escape sequences if enabled.  */
       if (do_expand)
-        msgid = expand_escape (msgid);
+        msgid = expand_escapes (msgid, &inhibit_added_newline);
 
       /* If no domain name is given we don't translate.  */
       if (domain == NULL || domain[0] == '\0')
@@ -230,7 +230,7 @@ There is NO WARRANTY, to the extent permitted by law.\n\
 
               /* Expand escape sequences if enabled.  */
               if (do_expand)
-                msgid = expand_escape (msgid);
+                msgid = expand_escapes (msgid, &inhibit_added_newline);
 
               /* Write out the result.  */
               fputs (domain == NULL ? msgid : dgettext (domain, msgid),
@@ -244,7 +244,7 @@ There is NO WARRANTY, to the extent permitted by law.\n\
         }
 
       /* If not otherwise told: add trailing newline.  */
-      if (add_newline)
+      if (!inhibit_added_newline)
         fputc ('\n', stdout);
     }
 
@@ -308,109 +308,3 @@ or by email to <%s>.\n"),
 
   exit (status);
 }
-
-
-/* Expand some escape sequences found in the argument string.  */
-static const char *
-expand_escape (const char *str)
-{
-  char *retval, *rp;
-  const char *cp = str;
-
-  /* Find the location of the first escape sequence.
-     If the string contains no escape sequences, return it right away.  */
-  for (;;)
-    {
-      while (cp[0] != '\0' && cp[0] != '\\')
-        ++cp;
-      if (cp[0] == '\0')
-        return str;
-      /* Found a backslash.  */
-      if (cp[1] == '\0')
-        return str;
-      if (strchr ("abcfnrtv\\01234567", cp[1]) != NULL)
-        break;
-      ++cp;
-    }
-
-  retval = XNMALLOC (strlen (str), char);
-
-  rp = retval + (cp - str);
-  memcpy (retval, str, cp - str);
-
-  do
-    {
-      /* Here cp[0] == '\\'.  */
-      switch (*++cp)
-        {
-        case 'a':               /* alert */
-          *rp++ = '\a';
-          ++cp;
-          break;
-        case 'b':               /* backspace */
-          *rp++ = '\b';
-          ++cp;
-          break;
-        case 'c':               /* suppress trailing newline */
-          add_newline = false;
-          ++cp;
-          break;
-        case 'f':               /* form feed */
-          *rp++ = '\f';
-          ++cp;
-          break;
-        case 'n':               /* new line */
-          *rp++ = '\n';
-          ++cp;
-          break;
-        case 'r':               /* carriage return */
-          *rp++ = '\r';
-          ++cp;
-          break;
-        case 't':               /* horizontal tab */
-          *rp++ = '\t';
-          ++cp;
-          break;
-        case 'v':               /* vertical tab */
-          *rp++ = '\v';
-          ++cp;
-          break;
-        case '\\':
-          *rp++ = '\\';
-          ++cp;
-          break;
-        case '0': case '1': case '2': case '3':
-        case '4': case '5': case '6': case '7':
-          {
-            int ch = *cp++ - '0';
-
-            if (*cp >= '0' && *cp <= '7')
-              {
-                ch *= 8;
-                ch += *cp++ - '0';
-
-                if (*cp >= '0' && *cp <= '7')
-                  {
-                    ch *= 8;
-                    ch += *cp++ - '0';
-                  }
-              }
-            *rp++ = ch;
-          }
-          break;
-        default:
-          *rp++ = '\\';
-          break;
-        }
-
-      /* Find the next escape sequence.  */
-      while (cp[0] != '\0' && cp[0] != '\\')
-        *rp++ = *cp++;
-    }
-  while (cp[0] != '\0');
-
-  /* Terminate the resulting string.  */
-  *rp = '\0';
-
-  return retval;
-}
index 4ace9a3602411f4e938e79550c03d6917a7fc0a4..b7de75449d55c747e55181979e44659d60d95f02 100755 (executable)
@@ -4,6 +4,20 @@
 # Test of Shell support.
 
 cat <<\EOF > xg-sh-1.sh
+# Test escape sequences expansion.
+
+gettext -e 'escape_0_\n'
+gettext -e 'escape_1_\a'
+gettext -e 'escape_2_\b'
+gettext -e 'escape_3_\f'
+gettext -e 'escape_4_\r'
+gettext -e 'escape_5_\t'
+gettext -e 'escape_6_\v'
+gettext -e 'escape_7_\\\\z'
+gettext -e 'escape_10_\40'
+gettext -e 'escape_11_\044'
+gettext -e 'escape_12_\140'
+
 # Test backslash before normal alphabetic character.
 
 gettext  depth_0_none_0_x
@@ -472,6 +486,39 @@ ${XGETTEXT} --omit-header --no-location --keyword=ngettext:1 \
 LC_ALL=C tr -d '\r' < xg-sh-1.tmp.po > xg-sh-1.po || Exit 1
 
 cat <<\EOF > xg-sh-1.ok
+msgid "escape_0_\n"
+msgstr ""
+
+msgid "escape_1_\a"
+msgstr ""
+
+msgid "escape_2_\b"
+msgstr ""
+
+msgid "escape_3_\f"
+msgstr ""
+
+msgid "escape_4_\r"
+msgstr ""
+
+msgid "escape_5_\t"
+msgstr ""
+
+msgid "escape_6_\v"
+msgstr ""
+
+msgid "escape_7_\\\\z"
+msgstr ""
+
+msgid "escape_10_ "
+msgstr ""
+
+msgid "escape_11_$"
+msgstr ""
+
+msgid "escape_12_`"
+msgstr ""
+
 msgid "depth_0_none_0_x"
 msgstr ""