/* xgettext sh backend.
- Copyright (C) 2003-2024 Free Software Foundation, Inc.
+ Copyright (C) 2003-2025 Free Software Foundation, Inc.
Written by Bruno Haible <bruno@clisp.org>, 2003.
This program is free software: you can redistribute it and/or modify
- Strings are enclosed in "..."; command substitution, variable
substitution and arithmetic substitution are performed here as well.
- '...' is a string without substitutions.
+ - $'...' is a string with escapes but without substitutions.
+ <https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_02_04>
+ <https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html>
- The list of resulting words is split into commands by semicolon and
newline.
- '#' at the beginning of a word introduces a comment until end of line.
if (c2 == '\'' && !open_singlequote)
{
- /* Bash builtin for string with ANSI-C escape sequences. */
+ /* $'...': POSIX dollar-single-quoted string. Also known as
+ bash builtin for string with ANSI-C escape sequences. */
for (;;)
{
/* We have to use phase1 throughout this loop,
c = '\v';
break;
+ case 'c':
+ c = phase1_getc ();
+ if (c >= 'A' && c <= 'Z')
+ c = c - 'A' + 0x01;
+ else if (c >= 'a' && c <= 'z')
+ c = c - 'a' + 0x01;
+ else if (c == '[')
+ c = 0x1b; /* ESC */
+ else if (c == '\\')
+ {
+ c = phase1_getc ();
+ if (c == '\\')
+ c = 0x1c; /* FS */
+ else
+ {
+ phase1_ungetc (c);
+ phase1_ungetc ('\\');
+ c = 'c';
+ }
+ }
+ else if (c == ']')
+ c = 0x1d; /* GS */
+ else if (c == '^')
+ c = 0x1e; /* RS */
+ else if (c == '_')
+ c = 0x1f; /* US */
+ else if (c == '?')
+ c = 0x7f; /* DEL */
+ else
+ {
+ phase1_ungetc (c);
+ c = 'c';
+ }
+ break;
+
case 'x':
c = phase1_getc ();
if ((c >= '0' && c <= '9')
}
else if (c2 == '"' && !open_doublequote)
{
- /* Bash builtin for internationalized string. */
+ /* $"...": Bash builtin for internationalized string. */
lex_pos_ty pos;
struct token string;
# Test of Shell support: escape sequences, string concatenation,
# strings with embedded expressions.
+# Note! This file contains unescaped ASCII control characters. Edit carefully!
+
cat <<\EOF > xg-sh-1.sh
# Test escape sequences expansion.
echo "`echo "\`ngettext 'depth_2_11_squote_7_semi \\\\\\\;'\`"`"
echo "`echo "\`ngettext 'depth_2_11_squote_8_semi \\\\\\\\;'\`"`"
+# Test dollar-single-quote strings.
+
+gettext $'depth_0_dollar_posix_0_"ab\"cd\'ef\\gh\eij\fkl\nmn\rop\tqr\vst'
+gettext $'depth_0_dollar_posix_1_\cvab\cVcd\c[ef\c\\gh\c]ij\c?kl'
+gettext $'depth_0_dollar_bash_0_\Eab'
+
+echo `gettext $'depth_1_dollar_posix_0_"ab\"cd\'ef\\gh\eij\fkl\nmn\rop\tqr\vst'`
+echo `gettext $'depth_1_dollar_posix_1_\cvab\cVcd\c[ef\c\\gh\c]ij\c?kl'`
+echo `gettext $'depth_1_dollar_bash_0_\Eab'`
+
+echo `echo \`gettext $'depth_2_dollar_posix_0_"ab\"cd\'ef\\gh\eij\fkl\nmn\rop\tqr\vst'\``
+echo `echo \`gettext $'depth_2_dollar_posix_1_\cvab\cVcd\c[ef\c\\gh\c]ij\c?kl'\``
+echo `echo \`gettext $'depth_2_dollar_bash_0_\Eab'\``
+
# Test string concatenation.
gettext "concat_0_""part2"
msgid "depth_2_11_squote_8_semi \\\\;"
msgstr ""
+msgid ""
+"depth_0_dollar_posix_0_\"ab\"cd'ef\\gh\eij\fkl\n"
+"mn\rop\tqr\vst"
+msgstr ""
+
+msgid "depth_0_dollar_posix_1_\16ab\16cd\eef\1cgh\1dij\7fkl"
+msgstr ""
+
+msgid "depth_0_dollar_bash_0_\eab"
+msgstr ""
+
+msgid ""
+"depth_1_dollar_posix_0_\"ab\"cd'ef\\gh\eij\fkl\n"
+"mn\rop\tqr\vst"
+msgstr ""
+
+msgid "depth_1_dollar_posix_1_\16ab\16cd\eef\1cgh\1dij\7fkl"
+msgstr ""
+
+msgid "depth_1_dollar_bash_0_\eab"
+msgstr ""
+
+msgid ""
+"depth_2_dollar_posix_0_\"ab\"cd'ef\\gh\eij\fkl\n"
+"mn\rop\tqr\vst"
+msgstr ""
+
+msgid "depth_2_dollar_posix_1_\16ab\16cd\eef\1cgh\1dij\7fkl"
+msgstr ""
+
+msgid "depth_2_dollar_bash_0_\eab"
+msgstr ""
+
msgid "concat_0_part2"
msgstr ""