From 4e4cebb6dcf4522243b5b9e87918789aea8f73a1 Mon Sep 17 00:00:00 2001
From: Chet Ramey <chet.ramey@case.edu>
Date: Tue, 20 Jun 2023 11:10:39 -0400
Subject: [PATCH] fix for nofork comsub command printing; fix for crash caused
 by tempvar assignment converted to an array in a function

---
 arrayfunc.c          | 13 ++++++++++
 arrayfunc.h          |  2 ++
 builtins/declare.def | 13 +++++++---
 print_cmd.c          | 12 +++++----
 shell.h              |  2 ++
 subst.h              |  1 +
 variables.c          | 59 ++++++++++++++++++++++++++++++--------------
 7 files changed, 74 insertions(+), 28 deletions(-)

diff --git a/arrayfunc.c b/arrayfunc.c
index e4ae34d..44b051e 100644
--- a/arrayfunc.c
+++ b/arrayfunc.c
@@ -137,6 +137,19 @@ convert_var_to_assoc (var)
   return var;
 }
 
+/* Copy the array (ARRAY *) or assoc (HASH_TABLE *) from variable V1 to V2,
+   and return V2. */
+SHELL_VAR *
+arrayvar_copyval (SHELL_VAR *v1, SHELL_VAR *v2)
+{
+  FREE (value_cell (v2));
+  if (array_p (v1))
+    var_setarray (v2, array_copy (array_cell (v1)));
+  else if (assoc_p (v1))
+    var_setassoc (v2, assoc_copy (assoc_cell (v1)));
+  return v2;
+}
+
 char *
 make_array_variable_value (entry, ind, key, value, flags)
      SHELL_VAR *entry;
diff --git a/arrayfunc.h b/arrayfunc.h
index cd51ee0..27d8579 100644
--- a/arrayfunc.h
+++ b/arrayfunc.h
@@ -47,6 +47,8 @@ extern int array_expand_once;
 extern SHELL_VAR *convert_var_to_array __P((SHELL_VAR *));
 extern SHELL_VAR *convert_var_to_assoc __P((SHELL_VAR *));
 
+extern SHELL_VAR *arrayvar_copyval (SHELL_VAR *, SHELL_VAR *);
+
 extern char *make_array_variable_value __P((SHELL_VAR *, arrayind_t, char *, char *, int));
 
 extern SHELL_VAR *bind_array_variable __P((char *, arrayind_t, char *, int));
diff --git a/builtins/declare.def b/builtins/declare.def
index 7eac6f5..97c8efc 100644
--- a/builtins/declare.def
+++ b/builtins/declare.def
@@ -952,20 +952,25 @@ restart_new_var_name:
 	  if ((flags_on & (att_exported|att_readonly)) && tempvar_p (var))
 	    {
 	      SHELL_VAR *tv;
-	      char *tvalue;
 
 	      tv = find_tempenv_variable (var->name);
 	      if (tv)
 		{
-		  tvalue = var_isset (var) ? savestring (value_cell (var)) : savestring ("");
-	          tv = bind_variable (var->name, tvalue, 0);
+	      /* We don't bother with modifying the temporary env because
+		 we're already using it. */
+	      tv = bind_variable (name_cell (var), value_cell (var), ASS_NOTEMPENV);
 		  if (tv)
 		    {
+#if defined (ARRAY_VARS)
+		  /* copy array value if array variable */
+		  if ((array_p (var) || assoc_p (var)))
+		    arrayvar_copyval (var, tv);
+#endif
+			/* then copy attributes */
 		      tv->attributes |= var->attributes & ~att_tempvar;
 		      if (tv->context > 0)
 			VSETATTR (tv, att_propagate);
 		    }
-	          free (tvalue);
 		}
 	      VSETATTR (var, att_propagate);
 	    }
diff --git a/print_cmd.c b/print_cmd.c
index 9aa6557..6607993 100644
--- a/print_cmd.c
+++ b/print_cmd.c
@@ -666,7 +666,7 @@ print_group_command (group_command)
   group_command_nesting++;
   cprintf ("{ ");
 
-  if (inside_function_def == 0)
+  if (inside_function_def == 0 /* && pretty_print_mode == 0 */)
     skip_this_indent++;
   else
     {
@@ -680,7 +680,7 @@ print_group_command (group_command)
   make_command_string_internal (group_command->command);
   PRINT_DEFERRED_HEREDOCS ("");
 
-  if (inside_function_def)
+  if (inside_function_def /* || pretty_print_mode */)
     {
       cprintf ("\n");
       indentation -= indentation_amount;
@@ -1459,9 +1459,11 @@ indent (amount)
 static void
 semicolon ()
 {
-  if (command_string_index > 0 &&
-       (the_printed_command[command_string_index - 1] == '&' ||
-	the_printed_command[command_string_index - 1] == '\n'))
+  if ((command_string_index > 0 &&
+	the_printed_command[command_string_index - 1] == '\n') ||
+      (command_string_index > 1 && 
+	the_printed_command[command_string_index - 1] == '&' &&
+	the_printed_command[command_string_index - 2] == ' '))
     return;
   cprintf (";");
 }
diff --git a/shell.h b/shell.h
index 8072605..fef9347 100644
--- a/shell.h
+++ b/shell.h
@@ -106,6 +106,8 @@ extern int indirection_level;
 extern int shell_compatibility_level;
 extern int running_under_emacs;
 
+extern int pretty_print_mode;
+
 extern int posixly_correct;
 extern int no_line_editing;
 
diff --git a/subst.h b/subst.h
index faf831b..b161c3c 100644
--- a/subst.h
+++ b/subst.h
@@ -54,6 +54,7 @@
 #define ASS_NOEXPAND	0x0080	/* don't expand associative array subscripts */
 #define ASS_NOEVAL	0x0100	/* don't evaluate value as expression */
 #define ASS_NOLONGJMP	0x0200	/* don't longjmp on fatal assignment error */
+#define ASS_NOTEMPENV	0x2000	/* don't assign into temporary environment */
 
 /* Flags for the string extraction functions. */
 #define SX_NOALLOC	0x0001	/* just skip; don't return substring */
diff --git a/variables.c b/variables.c
index 7abd778..21be2cd 100644
--- a/variables.c
+++ b/variables.c
@@ -259,6 +259,8 @@ static SHELL_VAR *new_shell_variable __P((const char *));
 static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
 static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
 
+static void init_shell_variable (SHELL_VAR *);
+
 static void dispose_variable_value __P((SHELL_VAR *));
 static void free_variable_hash_data __P((PTR_T));
 
@@ -2683,6 +2685,21 @@ make_local_variable (name, flags)
     new_var = make_new_variable (name, vc->table);
   else
     {
+#if 0
+      /* This handles the case where a variable is found in both the temporary
+	 environment *and* declared as a local variable. If we want to avoid
+	 multiple entries with the same name in VC->table (that might mess up
+   unset), we need to use the existing variable entry and destroy the
+	 current value. Currently disabled because it doesn't matter -- the
+	 right things happen. */
+      new_var = 0;
+      if (was_tmpvar && (new_var = hash_lookup (name, vc->table)))
+	{
+	  dispose_variable_value (new_var);
+	  init_variable (new_var);
+	}
+      if (new_var == 0)
+#endif
       new_var = make_new_variable (name, vc->table);
 
       /* If we found this variable in one of the temporary environments,
@@ -2742,16 +2759,9 @@ set_local_var_flags:
   return (new_var);
 }
 
-/* Create a new shell variable with name NAME. */
-static SHELL_VAR *
-new_shell_variable (name)
-     const char *name;
+static void
+init_variable (SHELL_VAR *entry)
 {
-  SHELL_VAR *entry;
-
-  entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-
-  entry->name = savestring (name);
   var_setvalue (entry, (char *)NULL);
   CLEAR_EXPORTSTR (entry);
 
@@ -2764,7 +2774,18 @@ new_shell_variable (name)
      make_local_variable has the responsibility of changing the
      variable context. */
   entry->context = 0;
+}
 
+/* Create a new shell variable with name NAME. */
+static SHELL_VAR *
+new_shell_variable (const char *name)
+{
+  SHELL_VAR *entry;
+
+  entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
+
+  entry->name = savestring (name);
+  init_variable (entry);
   return (entry);
 }
 
@@ -3233,8 +3254,9 @@ bind_variable (name, value, flags)
      and, if found, modify the value there before modifying it in the
      shell_variables table.  This allows sourced scripts to modify values
      given to them in a temporary environment while modifying the variable
-     value that the caller sees. */
-  if (temporary_env && value)		/* XXX - can value be null here? */
+     value that the caller sees. The caller can inhibit this by setting
+     ASS_NOTEMPENV in FLAGS. */
+  if (temporary_env && value && (flags & ASS_NOTEMPENV) == 0)	/* XXX - can value be null here? */
     bind_tempenv_variable (name, value);
 
   /* XXX -- handle local variables here. */
@@ -4537,6 +4559,11 @@ push_temp_var (data)
 
   v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, ASS_FORCE|ASS_NOLONGJMP);
 
+#if defined (ARRAY_VARS)
+  if (v && (array_p (var) || assoc_p (var)))
+    arrayvar_copyval (var, v);
+#endif
+
   /* XXX - should we set the context here?  It shouldn't matter because of how
      assign_in_env works, but we do it anyway. */
   if (v)
@@ -5209,14 +5236,8 @@ push_posix_tempvar_internal (var, isbltin)
 	v->context = shell_variables->scope;
 #if defined (ARRAY_VARS)
       if (v && (array_p (var) || assoc_p (var)))
-	{
-	  FREE (value_cell (v));
-	  if (array_p (var))
-	    var_setarray (v, array_copy (array_cell (var)));
-	  else
-	    var_setassoc (v, assoc_copy (assoc_cell (var)));
-	}
-#endif	  
+    arrayvar_copyval (var, v);
+#endif
       if (shell_variables == global_variables)
 	var->attributes &= ~(att_tempvar|att_propagate);
       else
-- 
2.33.0
