* 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.
## 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
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.
--- /dev/null
+/* 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;
+ }
+}
#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. */
__attribute__ ((noreturn))
#endif
;
-static const char *expand_escape (const char *str);
int
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. */
do_help = true;
break;
case 'n':
- add_newline = false;
+ inhibit_added_newline = true;
break;
case 's':
do_shell = true;
/* 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')
/* 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 :
}
/* If not otherwise told: add trailing newline. */
- if (add_newline)
+ if (!inhibit_added_newline)
fputc ('\n', stdout);
}
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;
-}
#include "basename.h"
#include "xalloc.h"
#include "propername.h"
+#include "escapes.h"
#include "gettext.h"
#define _(str) gettext (str)
__attribute__ ((noreturn))
#endif
;
-static const char *expand_escape (const char *str);
int
main (int argc, char *argv[])
/* 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
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;
-}
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
#include "error-progname.h"
#include "xalloc.h"
#include "hash.h"
+#include "../../gettext-runtime/src/escapes.h"
#include "gettext.h"
#define _(s) gettext(s)
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;
&& 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);
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,
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
#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
#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. */
__attribute__ ((noreturn))
#endif
;
-static const char *expand_escape (const char *str);
int
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. */
do_help = true;
break;
case 'n':
- add_newline = false;
+ inhibit_added_newline = true;
break;
case 's':
do_shell = true;
/* 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')
/* 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),
}
/* If not otherwise told: add trailing newline. */
- if (add_newline)
+ if (!inhibit_added_newline)
fputc ('\n', stdout);
}
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;
-}
# 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
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 ""