]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
more changes for here-docs and $'...'; command optimization updates
authorChet Ramey <chet.ramey@case.edu>
Mon, 31 Jan 2022 14:53:03 +0000 (09:53 -0500)
committerChet Ramey <chet.ramey@case.edu>
Mon, 31 Jan 2022 14:53:03 +0000 (09:53 -0500)
20 files changed:
CWRU/CWRU.chlog
MANIFEST
Makefile.in
builtins/common.h
builtins/evalstring.c
execute_cmd.c
externs.h
include/chartypes.h
lib/readline/doc/rltech.texi
lib/readline/history.h
lib/readline/readline.h
lib/sh/Makefile.in
lib/sh/strdup.c
lib/sh/strvis.c [new file with mode: 0644]
parse.y
subst.c
tests/exec.right
tests/exec14.sub
tests/posixexp.right
tests/posixexp7.sub

index 5982d30cab752979a634de7370af9e64afe76889..446c3df4dfa22a007e2756d039fd642de461bc8c 100644 (file)
@@ -3009,10 +3009,48 @@ subst.c
          read this new ${...} string
        - extract_heredoc_dolbrace_string: new function, variant of
          extract_dollar_brace_string, to process the WORD in ${PARAM OP WORD}
-         while processing here-document data. It's complicated by the
+         while expanding lines of here-document data. It's complicated by the
          requirement to add to the result string as we go along, since we
          need to change the contents of the input string with ansi expansion
          or locale translation.
        - string_extract_single_quoted: take a new third argument: ALLOWESC.
          This allows backslash to escape an embedded single quote, needed by
          extract_heredoc_dolbrace_string to process $'...'; changed callers
+
+                                  1/25
+                                  ----
+parse.y
+       - parse_matched_pair: ansi-expand $'...' in WORD for ${PARAM OP WORD}
+         and single-quote the result if dolbrace_state == DOLBRACE_QUOTE
+         (posix pattern removal operators) even if extended_quote == 0
+
+subst.c
+       - extract_heredoc_dolbrace_string: add logic to align with parse.y:
+         parse_matched_pair and its $'...' expansion, including handling
+         extended_quote
+
+                                  1/27
+                                  ----
+builtins/evalstring.c
+       - should_optimize_fork: broke conditions for optimizing away the fork
+         for a simple command out of optimize_fork into new function, call
+         from should_suppress_fork and optimize_subshell_command. Call from
+         optimize_fork if (subshell_environment & SUBSHELL_PAREN), relying
+         on fact that CMD_TRY_OPTIMIZING is only set in a couple of specific
+         conditions
+       - optimize_fork: call should_suppress_fork only if startup_state == 2;
+         it does the extra checks for that specific case
+       - optimize_fork: call should_optimize_fork if we're in a (list)
+         subshell (subshell_environment & SUBSHELL_PAREN)
+       - optimize_subshell_command: set CMD_TRY_OPTIMIZING on the right side
+         of a `&&', `||', or `;' list as long as it's a simple command so
+         we can check with optimize_fork() when it's time to execute it
+
+execute_cmd.c
+       - execute_in_subshell: call optimize_subshell_command for (list)
+         subshells to either set CMD_NO_FORK for simple commands or set
+         CMD_TRY_OPTIMIZING for likely candidates for later optimization
+
+builtins/common.h,builtins/evalstring.c
+       - optimize_fork: renamed to optimize_connection_fork; changed callers
+
index 75ad5982275eb5d88d2102bd530f0a1cbb9c25a7..a4cd9ead6541b751ac65ee01d196377eb82647df 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -454,6 +454,7 @@ lib/sh/strtoul.c    f
 lib/sh/strtoull.c      f
 lib/sh/strtoumax.c     f
 lib/sh/strtrans.c      f
+lib/sh/strvis.c                f
 lib/sh/timers.c                f
 lib/sh/times.c         f
 lib/sh/timeval.c       f
index 818a51b7524b13124609f86b812755429d72b90b..ef26d7083ffa5d12177eea1629dda963d1247fa3 100644 (file)
@@ -239,7 +239,7 @@ SHLIB_SOURCE =      ${SH_LIBSRC}/clktck.c ${SH_LIBSRC}/getcwd.c \
                ${SH_LIBSRC}/wcswidth.c ${SH_LIBSRC}/wcsnwidth.c \
                ${SH_LIBSRC}/shmbchar.c ${SH_LIBSRC}/utf8.c \
                ${SH_LIBSRC}/random.c ${SH_LIBSRC}/gettimeofday.c \
-               ${SH_LIBSRC}/timers.c
+               ${SH_LIBSRC}/timers.c ${SH_LIBSRC}/strvis.c
 
 SHLIB_LIB = -lsh
 SHLIB_LIBNAME = libsh.a
index 38326b99e5489c304c6c01036cb4c52cbf4ab76e..1cec62fc2b5e59b7c2c265c5bc6c90ca98b8c440 100644 (file)
@@ -1,6 +1,6 @@
 /* common.h -- extern declarations for functions defined in common.c. */
 
-/* Copyright (C) 1993-2021 Free Software Foundation, Inc.
+/* Copyright (C) 1993-2022 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -217,7 +217,7 @@ extern int parse_string PARAMS((char *, const char *, int, COMMAND **, char **))
 extern int should_suppress_fork PARAMS((COMMAND *));
 extern int can_optimize_connection PARAMS((COMMAND *));
 extern int can_optimize_cat_file PARAMS((COMMAND *));
-extern void optimize_fork PARAMS((COMMAND *));
+extern void optimize_connection_fork PARAMS((COMMAND *));
 extern void optimize_subshell_command PARAMS((COMMAND *));
 extern void optimize_shell_function PARAMS((COMMAND *));
 
index 3801a7a07800897183a5d90d5a406954315413e1..5088151a698b4f6b5c852d8fd9791b40b719e660 100644 (file)
@@ -1,6 +1,6 @@
 /* evalstring.c - evaluate a string as one or more shell commands. */
 
-/* Copyright (C) 1996-2021 Free Software Foundation, Inc.
+/* Copyright (C) 1996-2022 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -84,6 +84,24 @@ restore_lastcom (x)
   the_printed_command_except_trap = x;
 }
 
+int
+should_optimize_fork (command, subshell)
+     COMMAND *command;
+     int subshell;
+{
+  return (running_trap == 0 &&
+      command->type == cm_simple &&
+      signal_is_trapped (EXIT_TRAP) == 0 &&
+      signal_is_trapped (ERROR_TRAP) == 0 &&
+      any_signals_trapped () < 0 &&
+      (subshell || (command->redirects == 0 && command->value.Simple->redirects == 0)) &&
+      ((command->flags & CMD_TIME_PIPELINE) == 0) &&
+      ((command->flags & CMD_INVERT_RETURN) == 0));
+}
+
+/* This has extra tests to account for STARTUP_STATE == 2, which is for
+   -c command but has been extended to command and process substitution
+   (basically any time you call parse_and_execute in a subshell). */
 int
 should_suppress_fork (command)
      COMMAND *command;
@@ -94,14 +112,7 @@ should_suppress_fork (command)
   return (startup_state == 2 && parse_and_execute_level == 1 &&
          *bash_input.location.string == '\0' &&
          parser_expanding_alias () == 0 &&
-         running_trap == 0 &&
-         command->type == cm_simple &&
-         signal_is_trapped (EXIT_TRAP) == 0 &&
-         signal_is_trapped (ERROR_TRAP) == 0 &&
-         any_signals_trapped () < 0 &&
-         (subshell || (command->redirects == 0 && command->value.Simple->redirects == 0)) &&
-         ((command->flags & CMD_TIME_PIPELINE) == 0) &&
-         ((command->flags & CMD_INVERT_RETURN) == 0));
+         should_optimize_fork (command, subshell));
 }
 
 int
@@ -115,13 +126,14 @@ can_optimize_connection (command)
 }
 
 void
-optimize_fork (command)
+optimize_connection_fork (command)
      COMMAND *command;
 {
   if (command->type == cm_connection &&
       (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR || command->value.Connection->connector == ';') &&
       (command->value.Connection->second->flags & CMD_TRY_OPTIMIZING) &&
-      should_suppress_fork (command->value.Connection->second))
+      ((startup_state == 2 && should_suppress_fork (command->value.Connection->second)) ||
+       ((subshell_environment & SUBSHELL_PAREN) && should_optimize_fork (command->value.Connection->second, 0))))
     {
       command->value.Connection->second->flags |= CMD_NO_FORK;
       command->value.Connection->second->value.Simple->flags |= CMD_NO_FORK;
@@ -132,21 +144,19 @@ void
 optimize_subshell_command (command)
      COMMAND *command;
 {
-  if (running_trap == 0 &&
-      command->type == cm_simple &&
-      signal_is_trapped (EXIT_TRAP) == 0 &&
-      signal_is_trapped (ERROR_TRAP) == 0 &&
-      any_signals_trapped () < 0 &&
-      command->redirects == 0 && command->value.Simple->redirects == 0 &&
-      ((command->flags & CMD_TIME_PIPELINE) == 0) &&
-      ((command->flags & CMD_INVERT_RETURN) == 0))
+  if (should_optimize_fork (command, 0))
     {
       command->flags |= CMD_NO_FORK;
       command->value.Simple->flags |= CMD_NO_FORK;
     }
   else if (command->type == cm_connection &&
-          (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR))
-    optimize_subshell_command (command->value.Connection->second);
+          (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR || command->value.Connection->connector == ';') &&
+          command->value.Connection->second->type == cm_simple &&
+          parser_expanding_alias () == 0)
+    {     
+      command->value.Connection->second->flags |= CMD_TRY_OPTIMIZING;
+      command->value.Connection->second->value.Simple->flags |= CMD_TRY_OPTIMIZING;
+    }
 }
 
 void
index f86f396ad6f8cc9eef83f0384d05f26785520c2d..48706435f4f750083df8a8920f200699ee3accc5 100644 (file)
@@ -1,6 +1,6 @@
 /* execute_cmd.c -- Execute a COMMAND structure. */
 
-/* Copyright (C) 1987-2021 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -1650,12 +1650,12 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
   default_buffered_input = -1;
 #endif
 
-#if 0
-  /* We can't optimize if one of the commands executed by the subshell sets
-     an exit trap. */
+  /* We can't optimize away forks if one of the commands executed by the
+     subshell sets an exit trap, so we set CMD_NO_FORK for simple commands
+     and set CMD_TRY_OPTIMIZING for simple commands on the right side of an
+     and-or or `;' list to test for optimizing forks when they are executed. */
   if (user_subshell && command->type == cm_subshell)
     optimize_subshell_command (command->value.Subshell->command);
-#endif
 
   /* Do redirections, then dispose of them before recursive call. */
   if (command->redirects)
@@ -2753,7 +2753,7 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 #endif
 
       QUIT;
-      optimize_fork (command);                 /* XXX */
+      optimize_connection_fork (command);                      /* XXX */
       exec_result = execute_command_internal (command->value.Connection->second,
                                      asynchronous, pipe_in, pipe_out,
                                      fds_to_close);
@@ -2826,7 +2826,7 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
          ((command->value.Connection->connector == OR_OR) &&
           (exec_result != EXECUTION_SUCCESS)))
        {
-         optimize_fork (command);
+         optimize_connection_fork (command);
 
          second = command->value.Connection->second;
          if (ignore_return && second)
index e8289064b27c17f9f14580a608b385a2c2b28cf6..325703bbb38d605571aa1e2204fe936d570b4fee 100644 (file)
--- a/externs.h
+++ b/externs.h
@@ -471,6 +471,10 @@ extern char *ansic_quote PARAMS((char *, int, int *));
 extern int ansic_shouldquote PARAMS((const char *));
 extern char *ansiexpand PARAMS((char *, int, int, int *));
 
+/* declarations for functions defined in lib/sh/strvis.c */
+extern int charvis PARAMS((const char *, size_t *, char *, size_t *));
+extern char *sh_strvis PARAMS((const char *));
+
 /* declarations for functions defined in lib/sh/timeval.c.  No prototypes
    so we don't have to count on having a definition of struct timeval in
    scope when this file is included. */
index 0fe5fc1a2af10fe8b1fdc3358c3e92b87f1042ba..d5be4a3ce73e7d5b30d5d21a8fd3e42831132e99 100644 (file)
 #endif
 #ifndef UNCTRL
    /* control char to letter -- ASCII */
-#  define UNCTRL(x)    (TOUPPER(x) ^ 0x40)
+#  define UNCTRL(x)    (TOUPPER(x ^ 0x40))
 #endif
 
 #endif /* _SH_CHARTYPES_H */
index 8c4814ecfaf2bee4ff06cc9bd35972fbf69e938b..a1eac5ebc71cf223b161107ae8e41aa959cf6d2c 100644 (file)
@@ -1184,10 +1184,13 @@ Returns 0 if the timeout is set successfully.
 
 @deftypefun int rl_timeout_remaining (unsigned int *secs, unsigned int *usecs)
 Return the number of seconds and microseconds remaining in the current
-timeout duration in @code{*secs} and @code{*usecs}, respectively.
-Returns -1 on error or when there is no timeout set, 0 when the timeout has
-expired (leaving @code{*secs} and @code{*usecs} unchanged), and 1 if the
-timeout has not expired. If @code{secs} and @code{usecs} are @code{NULL},
+timeout duration in @var{*secs} and @var{*usecs}, respectively.
+Both @var{*secs} and @var{*usecs} must be non-NULL to return any values.
+The return value is -1 on error or when there is no timeout set,
+0 when the timeout has expired (leaving @var{*secs} and @var{*usecs}
+unchanged),
+and 1 if the timeout has not expired.
+If either of @var{secs} and @var{usecs} is @code{NULL},
 the return value indicates whether the timeout has expired.
 @end deftypefun
 
index fc98eeeaa4d7cf7402617d2de6d14a0eca337354..ad2ce70a179e9e16a0e04f94273c871eb0e866c6 100644 (file)
@@ -44,7 +44,7 @@ typedef char *histdata_t;
 
 /* Let's not step on anyone else's define for now, since we don't use this yet. */
 #ifndef HS_HISTORY_VERSION
-#  define HS_HISTORY_VERSION 0x0801    /* History 8.1 */
+#  define HS_HISTORY_VERSION 0x0802    /* History 8.2 */
 #endif
 
 /* The structure used to store a history entry. */
index 5f17dfd7b95c6e53cf7abb410df75ecf3ec0a330..7cf1fcf58a42972f1a9fe42b4fac09c3be05cd2b 100644 (file)
@@ -39,9 +39,9 @@ extern "C" {
 #endif
 
 /* Hex-encoded Readline version number. */
-#define RL_READLINE_VERSION    0x0801          /* Readline 8.1 */
+#define RL_READLINE_VERSION    0x0802          /* Readline 8.2 */
 #define RL_VERSION_MAJOR       8
-#define RL_VERSION_MINOR       1
+#define RL_VERSION_MINOR       2
 
 /* Readline data structures. */
 
index cbaf94d23c342b4d763dbf3182a8b1604b968719..8c42c73d057a2c66b64c0d74723a9bbc76536bb4 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for the Bash library
 #
 #
-# Copyright (C) 1998-2020 Free Software Foundation, Inc.
+# Copyright (C) 1998-2022 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
@@ -94,7 +94,7 @@ CSOURCES = clktck.c clock.c getcwd.c getenv.c oslib.c setlinebuf.c \
           wcsdup.c fpurge.c zgetline.c mbscmp.c uconvert.c ufuncs.c \
           casemod.c dprintf.c input_avail.c mbscasecmp.c fnxform.c \
           strchrnul.c unicode.c wcswidth.c wcsnwidth.c shmbchar.c strdup.c \
-          utf8.c random.c gettimeofday.c timers.c
+          strvis.c utf8.c random.c gettimeofday.c timers.c
 
 # The header files for this library.
 HSOURCES = 
@@ -108,7 +108,7 @@ OBJECTS = clktck.o clock.o getenv.o oslib.o setlinebuf.o strnlen.o \
          strtrans.o snprintf.o mailstat.o fmtulong.o \
          fmtullong.o fmtumax.o zcatfd.o zmapfd.o winsize.o wcsdup.o \
          fpurge.o zgetline.o mbscmp.o uconvert.o ufuncs.o casemod.o \
-         input_avail.o mbscasecmp.o fnxform.o unicode.o shmbchar.o \
+         input_avail.o mbscasecmp.o fnxform.o unicode.o shmbchar.o strvis.o \
          utf8.o random.o gettimeofday.o timers.o wcsnwidth.o ${LIBOBJS}
 
 SUPPORT = Makefile
@@ -198,6 +198,7 @@ strtoul.o: strtoul.c
 strtoull.o: strtoull.c
 strtoumax.o: strtoumax.c
 strtrans.o: strtrans.c
+strvis.o: strvis.c
 timers.o: timers.c
 times.o: times.c
 timeval.o: timeval.c
@@ -279,6 +280,7 @@ strtoul.o: ${BUILD_DIR}/config.h
 strtoull.o: ${BUILD_DIR}/config.h
 strtoumax.o: ${BUILD_DIR}/config.h
 strtrans.o: ${BUILD_DIR}/config.h
+strvis.o: ${BUILD_DIR}/config.h
 timers.o: ${BUILD_DIR}/config.h
 times.o: ${BUILD_DIR}/config.h
 timeval.o: ${BUILD_DIR}/config.h
@@ -512,6 +514,11 @@ strtrans.o: ${BUILD_DIR}/pathnames.h ${topdir}/externs.h
 strtrans.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h
 #strtrans.o: ${BUILD_DIR}/version.h
 
+strvis.o: ${topdir}/bashansi.h
+strvis.o: ${BASHINCDIR}/ansi_stdlib.h ${BASHINCDIR}/chartypes.h
+strvis.o: ${BASHINCDIR}/shmbutil.h ${BASHINCDIR}/shmbchar.h
+strvis.o: ${topdir}/bashintl.h ${LIBINTL_H} $(BASHINCDIR)/gettext.h
+
 times.o: ${BASHINCDIR}/systimes.h
 times.o: ${BASHINCDIR}/posixtime.h
 
index 90fa3532c500aadbe15674cfe881afe25edcb42d..3d35f7c0aedd1d71b889eaadfe08a7a582576363 100644 (file)
@@ -17,7 +17,6 @@
    You should have received a copy of the GNU General Public License
    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
 */
-                              
 
 #include <config.h>
 
diff --git a/lib/sh/strvis.c b/lib/sh/strvis.c
new file mode 100644 (file)
index 0000000..2ad8b70
--- /dev/null
@@ -0,0 +1,145 @@
+/* strvis.c - make unsafe graphical characters in a string visible. */
+
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+   
+   Bash 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.
+
+   Bash 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 Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* This is a stripped-down version suitable for the shell's use. */
+#include <config.h>
+
+#include <unistd.h>
+
+#include "bashansi.h"
+#include <stdio.h>
+
+#include "chartypes.h"
+#include "bashintl.h"
+#include "shmbutil.h"
+
+#define SAFECHAR(c)   ((c) == ' ' || (c) == '\t')
+
+#ifndef RUBOUT
+#define RUBOUT 0x7f
+#endif
+
+#ifndef CTRL_CHAR
+#define CTRL_CHAR(c)   ((c) < 0x20)
+#endif
+
+#ifndef META_CHAR
+#define META_CHAR(c)   ((c) > 0x7f && (c) <= UCHAR_MAX)
+#endif
+
+#ifndef UNCTRL
+#define UNCTRL(c)      (TOUPPER ((c) | 0x40))
+#endif
+
+#ifndef UNMETA
+#define UNMETA(c)      ((c) & 0x7f)
+#endif
+
+static int
+charvis (s, sindp, ret, rindp)
+     const char *s;
+     size_t *sindp;
+     char *ret;
+     size_t *rindp;
+{
+  unsigned char c;
+  size_t si, ri;
+  const char *send;
+  DECLARE_MBSTATE;
+
+  si = *sindp;
+  ri = *rindp;
+  c = s[*sindp];
+
+  send = (locale_mb_cur_max > 1) ? s + strlen (s) : 0;
+
+  if (SAFECHAR (c))
+    {
+      ret[ri++] = c;
+      si++;
+    }
+  else if (c == RUBOUT)
+    {
+      ret[ri++] = '^';
+      ret[ri++] = '?';
+      si++;
+    }
+  else if (CTRL_CHAR (c))
+    {
+      ret[ri++] = '^';
+      ret[ri++] = UNCTRL (c);
+      si++;
+    }
+  else if (locale_mb_cur_max > 1)
+    COPY_CHAR_I (ret, ri, s, send, si);
+  else if (META_CHAR (c))
+    {
+      ret[ri++] = 'M';
+      ret[ri++] = '-';
+      ret[ri++] = UNMETA (c);
+      si++;
+    }
+  else
+    ret[ri++] = s[si++];
+  
+  *sindp = si;
+  *rindp = ri;
+
+  return si;    
+}
+
+/* Return a new string with `unsafe' non-graphical characters in S rendered
+   in a visible way. */
+char *
+sh_strvis (string)
+     const char *string;
+{
+  size_t slen, sind;
+  char *ret;
+  size_t retind, retsize;
+  unsigned char c;
+  DECLARE_MBSTATE;
+
+  if (string == 0)
+    return 0;
+  if (*string == '\0')
+    {
+      if ((ret = (char *)malloc (1)) == 0)
+       return 0;
+      ret[0] = '\0';
+      return ret;
+    }
+
+  slen = strlen (string);
+  retsize = 3 * slen + 1;
+
+  ret = (char *)malloc (retsize);
+  if (ret == 0)
+    return 0;
+
+  retind = 0;
+  sind = 0;
+
+  while (string[sind])
+    sind = charvis (string, &sind, ret, &retind);
+
+  ret[retind] = '\0';
+  return ret;
+}
diff --git a/parse.y b/parse.y
index 095203235f72a6873d9248d89067b2553e30ac7d..b97d2c88a7e852bbd7318770180aa5b0b2aafdc8 100644 (file)
--- a/parse.y
+++ b/parse.y
@@ -3805,7 +3805,7 @@ parse_matched_pair (qc, open, close, lenp, flags)
              pop_delimiter (dstack);
              CHECK_NESTRET_ERROR ();
 
-             if MBTEST((tflags & LEX_WASDOL) && ch == '\'' && (extended_quote || (rflags & P_DQUOTE) == 0))
+             if MBTEST((tflags & LEX_WASDOL) && ch == '\'' && (extended_quote || (rflags & P_DQUOTE) == 0 || dolbrace_state == DOLBRACE_QUOTE || dolbrace_state == DOLBRACE_QUOTE2))
                {
                  /* Translate $'...' here. */
                  /* PST_NOEXPAND */
@@ -3817,12 +3817,22 @@ parse_matched_pair (qc, open, close, lenp, flags)
                     make sure we single-quote the results of the ansi
                     expansion because quote removal should remove them later */
                  /* FLAG POSIX INTERP 221 */
-                 if ((shell_compatibility_level > 42) && (rflags & P_DQUOTE) && (dolbrace_state == DOLBRACE_QUOTE2) && (flags & P_DOLBRACE))
+                 if ((shell_compatibility_level > 42) && (rflags & P_DQUOTE) && (dolbrace_state == DOLBRACE_QUOTE2 || dolbrace_state == DOLBRACE_QUOTE) && (flags & P_DOLBRACE))
                    {
                      nestret = sh_single_quote (ttrans);
                      free (ttrans);
                      nestlen = strlen (nestret);
                    }
+#if 0 /* TAG:bash-5.3 */
+                 /* This single-quotes PARAM in ${PARAM OP WORD} when PARAM
+                    contains a $'...' even when extended_quote is set. */
+                 else if ((rflags & P_DQUOTE) && (dolbrace_state == DOLBRACE_PARAM) && (flags & P_DOLBRACE))
+                   {
+                     nestret = sh_single_quote (ttrans);
+                     free (ttrans);
+                     nestlen = strlen (nestret);
+                   }
+#endif
                  else if ((rflags & P_DQUOTE) == 0)
                    {
                      nestret = sh_single_quote (ttrans);
diff --git a/subst.c b/subst.c
index 94b2c17f50780522a8bdf990f7c63a163b1deaec..33f5a22454761e27ebc750a118bf8a3503320164 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -195,6 +195,7 @@ int patsub_replacement = 1;
 extern struct fd_bitmap *current_fds_to_close;
 extern int wordexp_only;
 extern int singlequote_translations;
+extern int extended_quote;
 
 #if defined (JOB_CONTROL) && defined (PROCESS_SUBSTITUTION)
 extern PROCESS *last_procsub_child;
@@ -1523,7 +1524,9 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
    path doesn't. It's separate because we don't want to mess with the fast
    common path. We already know we're going to allocate and return a new
    string and quoted == Q_HERE_DOCUMENT. We might be able to cut it down
-   some more, but extracting strings and adding them as we go adds complexity. */
+   some more, but extracting strings and adding them as we go adds complexity.
+   This needs to match the logic in parse.y:parse_matched_pair so we get
+   consistent behavior between here-documents and double-quoted strings. */
 static char *
 extract_heredoc_dolbrace_string (string, sindex, quoted, flags)
      char *string;
@@ -1574,6 +1577,15 @@ extract_heredoc_dolbrace_string (string, sindex, quoted, flags)
          char *ttrans;
          int ttranslen;
 
+         if ((posixly_correct || extended_quote == 0) && dolbrace_state != DOLBRACE_QUOTE && dolbrace_state != DOLBRACE_QUOTE2)
+           {
+             RESIZE_MALLOCED_BUFFER (result, result_index, 3, result_size, 64);
+             result[result_index++] = '$';
+             result[result_index++] = '\'';
+             i += 2;
+             continue;
+           }
+
          si = i + 2;
          t = string_extract_single_quoted (string, &si, 1);    /* XXX */
          CHECK_STRING_OVERRUN (i, si, slen, c);
@@ -1583,9 +1595,18 @@ extract_heredoc_dolbrace_string (string, sindex, quoted, flags)
          free (t);
 
          /* needed to correctly quote any embedded single quotes. */
-         t = sh_single_quote (ttrans);
-         tlen = strlen (t);
-         free (ttrans);
+         if (dolbrace_state == DOLBRACE_QUOTE || dolbrace_state == DOLBRACE_QUOTE2)
+           {
+             t = sh_single_quote (ttrans);
+             tlen = strlen (t);
+             free (ttrans);
+           }
+         else if (extended_quote) /* dolbrace_state == DOLBRACE_PARAM */
+           {
+             /* This matches what parse.y:parse_matched_pair() does */
+             t = ttrans;
+             tlen = strlen (t);
+           }
 
          RESIZE_MALLOCED_BUFFER (result, result_index, tlen + 1, result_size, 64);
          strncpy (result + result_index, t, tlen);
@@ -1629,7 +1650,7 @@ extract_heredoc_dolbrace_string (string, sindex, quoted, flags)
          result[result_index++] = c;
          result[result_index++] = string[i+1];
          i += 2;
-         if (dolbrace_state == DOLBRACE_QUOTE || dolbrace_state == DOLBRACE_WORD)
+         if (dolbrace_state == DOLBRACE_QUOTE || dolbrace_state == DOLBRACE_QUOTE2 || dolbrace_state == DOLBRACE_WORD)
            dolbrace_state = DOLBRACE_PARAM;
          continue;
        }
index 8664e97f2be3697860a63335f7582793689f3a49..ef02fbb419d5fdc4e3fe5f4b24b25a4534de9b18 100644 (file)
@@ -142,3 +142,31 @@ w
 x
 y
 z
+=====
+WORKS
+done
+WORKS
+a
+b
+c
+d
+a
+b
+c
+d
+e
+A
+B
+c
+d
+c
+d
+e
+x
+y
+z
+WORKS
+w
+x
+y
+z
index 3402fb95fbc4b1774aecc0fb65c091d597fbbee8..eddd33fa015939f5d366579d3da997b36e8e1092 100644 (file)
@@ -45,3 +45,20 @@ $THIS_SH -c '$binecho c && $binecho d && echo e'
 $THIS_SH -c 'trap "echo WORKS" EXIT ; $binecho x ; $binecho y ; $binecho z'
 
 ${THIS_SH} -c 'echo w ; { echo x ; $binecho y; }; $binecho z'
+
+echo =====
+
+( trap "echo WORKS && rm $TMPDIR/x$$" EXIT && touch $TMPDIR/x$$ )
+( trap "echo WORKS && rm $TMPDIR/x$$" EXIT && touch $TMPDIR/x$$ ; $binecho done )
+
+( echo a && { $binecho b && $binecho c ; } && echo d )
+( echo a && { $binecho b && $binecho c ; } && echo d ; $binecho e )
+
+( echo A && $binecho B )
+( $binecho c && echo d )
+
+( $binecho c && $binecho d && echo e )
+
+( trap "echo WORKS" EXIT ; $binecho x ; $binecho y ; $binecho z )
+
+( echo w ; { echo x ; $binecho y; }; $binecho z )
index 0955995c19274f9bec65f9a7c0ffef7946320926..7204b960a99fc39d7abd94e4f53356c86e7bc006 100644 (file)
@@ -275,10 +275,31 @@ argv[2] = <b>
 [  abc    def  ghi  jkl /  abc    def  ghi  jkl ]
 [  abc    def  ghi  jkl ]
 [  abc    def  ghi  jkl /  abc    def  ghi  jkl /  abc    def  ghi  jkl ]
-5: OK
+1: OK
+2: $'not'
+3: OK
+4: OK
+5: tOK
 OK
 OK
-5: $'not\ttoo\nbad'
+$'not'
+OK
+tOK
+6: $'not\ttoo\nbad'
+OKa    '       b
+OKa    '       b
+7: OK
+8: OKa '       b
+9: OKa "       b
+10: OKa        "       b
+tOK
+tOK
+tOK
+tOK
+./posixexp7.sub: line 69: ${'x1'%'t'}: bad substitution
+./posixexp7.sub: line 70: ${'x1'%'t'}: bad substitution
+./posixexp7.sub: line 73: ${'x1'%'t'}: bad substitution
+./posixexp7.sub: line 74: ${'x1'%'t'}: bad substitution
 "A"
 A
 argv[1] = <"A">
index 2e230cf855c7fd34ad8e6d760730fde5563e37db..4e3fa007c9badc183b5838dfeb6a53ddd0ccc648 100644 (file)
@@ -1,13 +1,76 @@
+#   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 <http://www.gnu.org/licenses/>.
+#
 # test the effect of quotes on the WORD in the posix pattern removal operators
-# a here document does not behave the same as double quotes
+#
 x=notOK
+x1=not
+
 cat <<EOF
-5: ${x#$'not'}
+1: ${x#$'not'}
+2: $'not'
+3: ${x#"not"}
+4: ${x#'not'}
+5: ${x#${x1%'t'}}
 EOF
 
 echo "${x#'not'}"
 echo "${x#$'not'}"
 
+echo "$'not'"
+echo "${x#"not"}"
+echo "${x#${x1%'t'}}"
+
+cat <<EOF
+6: $'not\ttoo\nbad'
+EOF
+
+x=OK$'a\t\'\tb'
+echo OK$'a\t\'\tb'
+echo "$x"
+
+cat <<EOF
+7: ${x%$'a\t\'\tb'}
+8: ${x#$'a\t\'\tb'}
+EOF
+
+x=OK'a "       b'
+
+cat <<EOF
+9: ${x#'a      "       b'}
+10: ${x#$'a    "       b'}
+EOF
+
+x=notOK
+x1=not
+
+# extquote makes these work
+echo "${x#${$'x1'%$'t'}}"
+cat <<EOF
+${x#${$'x1'%$'t'}}
+EOF
+echo "${x#${$'x1'%'t'}}"
+cat <<EOF
+${x#${$'x1'%'t'}}
+EOF
+
+# syntax errors
+
+echo "${x#${'x1'%'t'}}"
+cat <<EOF
+${x#${'x1'%'t'}}
+EOF
+echo "${x#${'x1'%$'t'}}"
 cat <<EOF
-5: $'not\ttoo\nbad'
+${x#${'x1'%$'t'}}
 EOF