]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
fix parser state with funsubs in PS2 and line continuations; fix for @P transformatio...
authorChet Ramey <chet.ramey@case.edu>
Mon, 1 Apr 2024 16:20:58 +0000 (12:20 -0400)
committerChet Ramey <chet.ramey@case.edu>
Mon, 1 Apr 2024 16:20:58 +0000 (12:20 -0400)
14 files changed:
CWRU/CWRU.chlog
Makefile.in
bashline.c
bashline.h
eval.c
externs.h
parse.y
parser.h
print_cmd.c
shell.c
shell.h
sig.c
subst.c
xmalloc.c

index 8f18eaa1f3e0cf8271f61ca5c36596bd01fbb022..a4f78006f76070451151d43d9b0294ac118afecc 100644 (file)
@@ -8997,3 +8997,44 @@ Makefile.in
        - distclean,maintainer-clean: remove $(CREATED_HEADERS) and
          $(CREATED_MACOS)
        - maintainer-clean: remove ctags/etags files
+
+                                  3/28
+                                  ----
+bashline.c,bashline.h
+       - uw_restore_parser_state: moved to parse.y and declaration to shell.h
+
+shell.c,shell.h
+       - parsing_command: new flag, set to 1 when calling yyparse; saved and
+         restored by save_parser_state/restore_parser_state
+
+parse.y,shell.h
+       - parsing_command: new element of sh_parser_state_t
+
+sig.c
+       - throw_to_top_level: reset executing and parsing_command to 0
+
+parse.y
+       - parse_comsub: set parsing_command to 1 before calling yyparse()
+
+eval.c
+       - parse_command: set parsing_command to 1 before calling yyparse(),
+         restore old value when yyparse returns
+
+subst.c
+       - function_substitute: if we are parsing a command, save the parser
+         state with save_parser_state and add an unwind-protect to restore it
+         From a report from Grisha Levit <grishalevit@gmail.com>
+       - function_substitute: only save and restore the pipestatus array if
+         we are not parsing a command, since save_parser_state saves it
+
+parse.y,externs.h
+       - decode_prompt_string: now takes an additional argument to determine
+         whether this is expanding PS[0124] or the @P transformation
+       - decode_prompt_string: keep track of the (last) real prompt string
+         being decoded so a @P expansion embedded in the prompt returns the
+         same result as the prompt string itself for the \! and \# expansions
+         From a patch from Grisha Levit <grishalevit@gmail.com>
+
+parse.y,print_cmd.c,eval.c,subst.c
+       - decode_prompt_string: changed all callers
+
index 8069162aece00de8146850892a88d43c18eaa566..14092c41c9a17619ad2ebda4d9866645507dd526 100644 (file)
@@ -586,7 +586,8 @@ CREATED_SUPPORT = signames.h recho$(EXEEXT) zecho$(EXEEXT) printenv$(EXEEXT) \
 CREATED_MACOS = recho.dSYM zecho.dSYM printenv.dSYM xcase.dSYM \
                 bashversion.dSYM mksyntax.dSYM ${DEFDIR}/psize.aux.dSYM
 CREATED_CONFIGURE = config.h config.cache config.status config.log \
-                   stamp-h po/POTFILES config.status.lineno
+                   stamp-h po/POTFILES config.status.lineno \
+                   stdckdint.h
 CREATED_MAKEFILES = Makefile builtins/Makefile doc/Makefile \
                    lib/readline/Makefile lib/glob/Makefile \
                    lib/sh/Makefile lib/tilde/Makefile lib/malloc/Makefile \
@@ -595,7 +596,7 @@ CREATED_MAKEFILES = Makefile builtins/Makefile doc/Makefile \
                    examples/loadables/perl/Makefile support/Makefile \
                    lib/intl/Makefile po/Makefile po/Makefile.in
 CREATED_HEADERS = signames.h config.h pathnames.h version.h y.tab.h \
-                 ${DEFDIR}/builtext.h stdckdint.h
+                 ${DEFDIR}/builtext.h
 
 OTHER_DOCS = $(srcdir)/CHANGES $(srcdir)/COMPAT $(srcdir)/NEWS $(srcdir)/POSIX \
        $(srcdir)/RBASH $(srcdir)/README
@@ -988,7 +989,7 @@ maintainer-clean:   basic-clean
        done
        -( cd $(PO_DIR) ; $(MAKE) $(MFLAGS) DESTDIR=$(DESTDIR) $@ )
        -( cd $(LOADABLES_DIR) && $(MAKE) $(MFLAGS) DESTDIR=$(DESTDIR) $@ )
-       $(RM) $(CREATED_CONFIGURE) Makefile $(CREATED_MAKEFILES) tags TAGS
+       $(RM) $(CREATED_CONFIGURE) Makefile $(CREATED_MAKEFILES)
        $(RM) $(CREATED_SUPPORT) $(CREATED_HEADERS)
        $(RM) -rf $(CREATED_MACOS)
 
index 353e8b1e68aab2578cbcb2db8e003e14a8aa6dd3..eedb09ffe30696fe611319a2b73c394cdea6efc5 100644 (file)
@@ -4490,12 +4490,6 @@ readline_set_char_offset (int ind, int *varp)
     }
 }
 
-void
-uw_restore_parser_state (void *ps)
-{
-  restore_parser_state (ps);
-}
-
 void
 uw_rl_set_signals (void *ignore)
 {
index 514c07ce048d249e9cc0ef490caf7a1102ecde81..430e369fe650389778c5984ba44cbee5282711a6 100644 (file)
@@ -1,6 +1,6 @@
 /* bashline.h -- interface to the bash readline functions in bashline.c. */
 
-/* Copyright (C) 1993-2023 Free Software Foundation, Inc.
+/* Copyright (C) 1993-2024 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -46,7 +46,6 @@ extern int bash_re_edit (const char *);
 extern void bashline_set_event_hook (void);
 extern void bashline_reset_event_hook (void);
 
-extern void uw_restore_parser_state (void *);
 extern void uw_rl_set_signals (void *);
 
 extern int bind_keyseq_to_unix_command (char *);
diff --git a/eval.c b/eval.c
index a08cb57816b503c5b991ea0a5e343143a77e74d1..cbf408f0fedd7cace62e99ff1512cc1a4b2bbab0 100644 (file)
--- a/eval.c
+++ b/eval.c
@@ -1,6 +1,6 @@
 /* eval.c -- reading and evaluating commands. */
 
-/* Copyright (C) 1996-2022 Free Software Foundation, Inc.
+/* Copyright (C) 1996-2024 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -157,7 +157,7 @@ reader_loop (void)
 
                  old_eof = EOF_Reached;
                  EOF_Reached = 0;
-                 ps0_string = decode_prompt_string (ps0_prompt);
+                 ps0_string = decode_prompt_string (ps0_prompt, 1);
                  if (ps0_string && *ps0_string)
                    {
                      fprintf (stderr, "%s", ps0_string);
@@ -329,7 +329,7 @@ execute_prompt_command (void)
 int
 parse_command (void)
 {
-  int r;
+  int r, old_parsing;
 
   need_here_doc = 0;
   if ((parser_state & (PST_CMDSUBST|PST_FUNSUBST)) == 0)
@@ -352,11 +352,14 @@ parse_command (void)
        send_pwd_to_eterm ();   /* Yuck */
     }
 
+  old_parsing = parsing_command;
+  parsing_command = 1;
   current_command_line_count = 0;
   r = yyparse ();
 
   if (need_here_doc)
     gather_here_documents ();
+  parsing_command = old_parsing;
 
   return (r);
 }
index bc6f5cc17cc11cccc6a03c547e5f9a95b4a0dd45..a7c41928ac6f10d4dfe787b9555f0a0ff6b29242 100644 (file)
--- a/externs.h
+++ b/externs.h
@@ -128,7 +128,7 @@ extern void clear_shell_input_line (void);
 
 extern int handle_ignoreeof (int);
 
-extern char *decode_prompt_string (char *);
+extern char *decode_prompt_string (char *, int);
 
 extern int get_current_prompt_level (void);
 extern void set_current_prompt_level (int);
diff --git a/parse.y b/parse.y
index 368c34235ed451120942cffa743ec75d4607815c..d129dd284d4a30df7bf45208b824dc4e530ab884 100644 (file)
--- a/parse.y
+++ b/parse.y
@@ -4462,6 +4462,7 @@ parse_comsub (int qc, int open, int close, size_t *lenp, int flags)
   current_token = '\n';                                /* XXX */
   token_to_read = (open == '(') ? DOLPAREN : DOLBRACE; /* let's trick the parser ) */
 
+  parsing_command = 1; /* saved as part of sh_parser_state_t */
   r = yyparse ();
 
   if (open == '{')
@@ -5879,7 +5880,7 @@ reset_readline_prompt (void)
   if (prompt_string_pointer)
     {
       temp_prompt = (*prompt_string_pointer)
-                       ? decode_prompt_string (*prompt_string_pointer)
+                       ? decode_prompt_string (*prompt_string_pointer, 1)
                        : (char *)NULL;
 
       if (temp_prompt == 0)
@@ -6040,7 +6041,7 @@ prompt_again (int force)
     prompt_string_pointer = &ps1_prompt;
 
   temp_prompt = *prompt_string_pointer
-                       ? decode_prompt_string (*prompt_string_pointer)
+                       ? decode_prompt_string (*prompt_string_pointer, 1)
                        : (char *)NULL;
 
   if (temp_prompt == 0)
@@ -6140,13 +6141,16 @@ prompt_history_number (char *pmt)
        \\      a backslash
        \[      begin a sequence of non-printing chars
        \]      end a sequence of non-printing chars
+
+  IS_PROMPT is non-zero if we are decoding one of the PS[0124] prompt strings
+  instead of an arbitrary string applying the @P transformation.
 */
 #define PROMPT_GROWTH 48
 char *
-decode_prompt_string (char *string)
+decode_prompt_string (char *string, int is_prompt)
 {
   WORD_LIST *list;
-  char *result, *t, *orig_string, *last_lastarg;
+  char *result, *t, *last_lastarg;
   struct dstack save_dstack;
   int last_exit_value, last_comsub_pid, last_comsub_status;
 #if defined (PROMPT_STRING_DECODE)
@@ -6159,11 +6163,16 @@ decode_prompt_string (char *string)
   char timebuf[128];
   char *timefmt;
   size_t tslen;
+  static char *decoding_prompt;
 
   result = (char *)xmalloc (result_size = PROMPT_GROWTH);
   result[result_index = 0] = 0;
   temp = (char *)NULL;
-  orig_string = string;
+
+  /* Keep track of which (real) prompt string is being decoded so that we can
+     process embedded ${var@P} expansions correctly. */
+  if (is_prompt)
+    decoding_prompt = string;
 
   while (c = *string++)
     {
@@ -6179,7 +6188,7 @@ decode_prompt_string (char *string)
 #if !defined (HISTORY)
                temp = savestring ("1");
 #else /* HISTORY */
-               temp = itos (prompt_history_number (orig_string));
+               temp = itos (prompt_history_number (decoding_prompt));
 #endif /* HISTORY */
                string--;       /* add_string increments string again. */
                goto add_string;
@@ -6440,7 +6449,7 @@ decode_prompt_string (char *string)
              n = current_command_number;
              /* If we have already incremented current_command_number (PS4,
                 ${var@P}), compensate */
-             if (orig_string != ps0_prompt && orig_string != ps1_prompt && orig_string != ps2_prompt)
+             if (decoding_prompt != ps0_prompt && decoding_prompt != ps1_prompt && decoding_prompt != ps2_prompt)
                n--;
              temp = itos (n);
              goto add_string;
@@ -6449,7 +6458,7 @@ decode_prompt_string (char *string)
 #if !defined (HISTORY)
              temp = savestring ("1");
 #else /* HISTORY */
-             temp = itos (prompt_history_number (orig_string));
+             temp = itos (prompt_history_number (decoding_prompt));
 #endif /* HISTORY */
              goto add_string;
 
@@ -6574,6 +6583,10 @@ not_escape:
     }
 
   dstack = save_dstack;
+#if defined (PROMPT_STRING_DECODE)
+  if (is_prompt)
+    decoding_prompt = (char *)NULL;
+#endif
 
   return (result);
 }
@@ -7081,6 +7094,8 @@ save_parser_state (sh_parser_state_t *ps)
   ps->parser_state = parser_state;
   ps->token_state = save_token_state ();
 
+  ps->parsing_command = parsing_command;
+
   ps->input_line_terminator = shell_input_line_terminator;
   ps->eof_encountered = eof_encountered;
   ps->eol_lookahead = eol_ungetc_lookahead;
@@ -7143,6 +7158,7 @@ restore_parser_state (sh_parser_state_t *ps)
       restore_token_state (ps->token_state);
       free (ps->token_state);
     }
+  parsing_command = ps->parsing_command;
 
   shell_input_line_terminator = ps->input_line_terminator;
   eof_encountered = ps->eof_encountered;
@@ -7195,6 +7211,12 @@ restore_parser_state (sh_parser_state_t *ps)
   shell_eof_token = ps->eof_token;
 }
 
+void
+uw_restore_parser_state (void *ps)
+{
+  restore_parser_state (ps);
+}
+
 /* Free the parts of a parser state struct that have allocated memory. */
 void
 flush_parser_state (sh_parser_state_t *ps)
index 14cd41493e4c3af452c94bb17af518cad48f1404..bef71707fbdca041ce3c5fad67ead9a49e5169c1 100644 (file)
--- a/parser.h
+++ b/parser.h
@@ -1,7 +1,7 @@
 /* parser.h -- Everything you wanted to know about the parser, but were
    afraid to ask. */
 
-/* Copyright (C) 1995-2021 Free Software Foundation, Inc.
+/* Copyright (C) 1995-2024 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
index 9d3ab73b597d92239ab976dcd25bd0848e7b0b6c..36268e19d7f69920533549da3950dafcd17f6fb8 100644 (file)
@@ -460,7 +460,7 @@ indirection_level_string (void)
     return (indirection_string);
 
   old = change_flag ('x', FLAG_OFF);
-  ps4 = decode_prompt_string (ps4);
+  ps4 = decode_prompt_string (ps4, 1);
   if (old)
     change_flag ('x', FLAG_ON);
 
diff --git a/shell.c b/shell.c
index add8b1cd3ef7efc5deda09536f920357fe6b11f8..01fffac2d2d7870a67c05c8a1cf83a4191e5f6b3 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -170,6 +170,9 @@ int debugging_login_shell = 0;
 /* The environment that the shell passes to other commands. */
 char **shell_environment;
 
+/* Non-zero when we are parsing a command, managed by parse_command/parse_comsub */
+int parsing_command = 0;
+
 /* Non-zero when we are executing a top-level command. */
 int executing = 0;
 
diff --git a/shell.h b/shell.h
index 5a18f4051620eea4defe7db0774905cd8ebef895..b9d259a515e9216953e4f7c42194ccbabdac7370 100644 (file)
--- a/shell.h
+++ b/shell.h
@@ -97,6 +97,7 @@ extern char *command_execution_string;
 
 extern int debugging_mode;
 extern int executing, login_shell;
+extern int parsing_command;
 extern int interactive, interactive_shell;
 extern int startup_state;
 extern int reading_shell_script;
@@ -175,6 +176,7 @@ typedef struct _sh_parser_state_t
   /* parsing state */
   int parser_state;
   int *token_state;
+  int parsing_command;
 
   char *token;
   size_t token_buffer_size;
@@ -240,6 +242,7 @@ extern char *parser_remaining_input (void);
 extern sh_parser_state_t *save_parser_state (sh_parser_state_t *);
 extern void restore_parser_state (sh_parser_state_t *);
 extern void flush_parser_state (sh_parser_state_t *);
+extern void uw_restore_parser_state (void *);
 
 extern sh_input_line_state_t *save_input_line_state (sh_input_line_state_t *);
 extern void restore_input_line_state (sh_input_line_state_t *);
diff --git a/sig.c b/sig.c
index d902b5cf80fa786cc6c49569d25e09f68e88aa9b..8c1b95678b7879f48f04b727790370be8e7cfb98 100644 (file)
--- a/sig.c
+++ b/sig.c
@@ -468,6 +468,8 @@ throw_to_top_level (void)
   comsub_ignore_return = return_catch_flag = wait_intr_flag = 0;
   variable_context = 0;
 
+  executing = parsing_command = 0;
+
   if (interactive && print_newline)
     {
       fflush (stdout);
diff --git a/subst.c b/subst.c
index f4254b1308bfa319e8248c91d2445127a146664c..a29eb5bebb3f36707689d59d4b8e4f2abd433fec 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -6961,9 +6961,10 @@ function_substitute (char *string, int quoted, int flags)
   char *afn;
   sigset_t set, oset;
   sh_getopt_state_t *gs;
+  sh_parser_state_t ps;
   SHELL_VAR *gv;
 #if defined (ARRAY_VARS)
-  ARRAY *ps;
+  ARRAY *psa;
 #endif
 
   if (valsub = (string && *string == '|'))
@@ -7014,9 +7015,18 @@ function_substitute (char *string, int quoted, int flags)
   add_unwind_protect (uw_pop_var_context, 0);
   add_unwind_protect (uw_maybe_restore_getopt_state, gs);
 
+  if (parsing_command)
+    {
+      save_parser_state (&ps);
+      add_unwind_protect (uw_restore_parser_state, &ps);
+    }
+      
 #if defined (ARRAY_VARS)
-  ps = save_pipestatus_array ();
-  add_unwind_protect (uw_restore_pipestatus_array, ps);
+  if (parsing_command == 0)
+    {
+      psa = save_pipestatus_array ();
+      add_unwind_protect (uw_restore_pipestatus_array, psa);
+    }
 #endif
   
   subst_assign_varlist = 0;
@@ -8756,7 +8766,7 @@ string_transform (int xc, SHELL_VAR *v, char *s)
        ret = ansicstr (s, strlen (s), 0, 0, 0);
        break;
       case 'P':
-       ret = decode_prompt_string (s);
+       ret = decode_prompt_string (s, 0);
        break;
       case 'Q':
        ret = sh_quote_reusable (s, 0);
index 05de38747a0249f51d723e2858c04fe29b3de901..340ce84d9965c440d5cf7bbdbb2bd9b6245c7720 100644 (file)
--- a/xmalloc.c
+++ b/xmalloc.c
@@ -206,7 +206,7 @@ sh_xrealloc (PTR_T pointer, size_t bytes, char *file, int line)
 }
 
 PTR_T
-sh_xreallocarray (PTR_T ptr, size_t nmemb, size_t size, const char *file, int line)
+sh_xreallocarray (PTR_T ptr, size_t nmemb, size_t size, char *file, int line)
 {
   size_t nbytes;