From: Bruno Haible Date: Sat, 21 Jun 2025 00:53:18 +0000 (+0200) Subject: xgettext: Shell: Recognize \c escape sequences in $'...' strings. X-Git-Tag: v0.26~96 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6ced019bdd43c1aaa7bebe0c61a48f627ec36a54;p=thirdparty%2Fgettext.git xgettext: Shell: Recognize \c escape sequences in $'...' strings. * gettext-tools/src/x-sh.c (read_word): Recognize \c escape sequences. * gettext-tools/tests/xgettext-sh-1: Add test cases with ASCII $'...' strings. --- diff --git a/gettext-tools/src/x-sh.c b/gettext-tools/src/x-sh.c index 154b185d7..c826dcf9b 100644 --- a/gettext-tools/src/x-sh.c +++ b/gettext-tools/src/x-sh.c @@ -1,5 +1,5 @@ /* xgettext sh backend. - Copyright (C) 2003-2024 Free Software Foundation, Inc. + Copyright (C) 2003-2025 Free Software Foundation, Inc. Written by Bruno Haible , 2003. This program is free software: you can redistribute it and/or modify @@ -64,6 +64,9 @@ - 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. + + - 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. @@ -985,7 +988,8 @@ read_word (struct word *wp, int looking_for, flag_region_ty *region) 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, @@ -1040,6 +1044,41 @@ read_word (struct word *wp, int looking_for, flag_region_ty *region) 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') @@ -1120,7 +1159,7 @@ read_word (struct word *wp, int looking_for, flag_region_ty *region) } else if (c2 == '"' && !open_doublequote) { - /* Bash builtin for internationalized string. */ + /* $"...": Bash builtin for internationalized string. */ lex_pos_ty pos; struct token string; diff --git a/gettext-tools/tests/xgettext-sh-1 b/gettext-tools/tests/xgettext-sh-1 index 8178d7baa..a98e54ae0 100755 --- a/gettext-tools/tests/xgettext-sh-1 +++ b/gettext-tools/tests/xgettext-sh-1 @@ -4,6 +4,8 @@ # 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. @@ -479,6 +481,20 @@ echo "`echo "\`ngettext 'depth_2_11_squote_6_semi \\\\\\;'\`"`" 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" @@ -1870,6 +1886,39 @@ msgstr "" msgid "depth_2_11_squote_8_semi \\\\;" msgstr "" +msgid "" +"depth_0_dollar_posix_0_\"ab\"cd'ef\\ghij\fkl\n" +"mn\rop\tqr\vst" +msgstr "" + +msgid "depth_0_dollar_posix_1_abcdefghijkl" +msgstr "" + +msgid "depth_0_dollar_bash_0_ab" +msgstr "" + +msgid "" +"depth_1_dollar_posix_0_\"ab\"cd'ef\\ghij\fkl\n" +"mn\rop\tqr\vst" +msgstr "" + +msgid "depth_1_dollar_posix_1_abcdefghijkl" +msgstr "" + +msgid "depth_1_dollar_bash_0_ab" +msgstr "" + +msgid "" +"depth_2_dollar_posix_0_\"ab\"cd'ef\\ghij\fkl\n" +"mn\rop\tqr\vst" +msgstr "" + +msgid "depth_2_dollar_posix_1_abcdefghijkl" +msgstr "" + +msgid "depth_2_dollar_bash_0_ab" +msgstr "" + msgid "concat_0_part2" msgstr ""