If a funsub declares a local OPTIND, the calling context's getopts state
gets messed up.
For example, the following loop will not terminate:
OPTIND=1; while getopts ab opt -ab; do
echo $opt ${ local OPTIND; }
done
From f7f16f70cf0ef623541ff3b6a36cff10b4933914 Mon Sep 17 00:00:00 2001
From: Grisha Levit <[email protected]>
Date: Tue, 28 Nov 2023 01:13:12 -0500
Subject: [PATCH] restore getopt state after funsub
---
execute_cmd.c | 2 +-
execute_cmd.h | 1 +
subst.c | 8 ++++++++
3 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/execute_cmd.c b/execute_cmd.c
index 56707b98..ae20a4cb 100644
--- a/execute_cmd.c
+++ b/execute_cmd.c
@@ -5035,7 +5035,7 @@ execute_builtin (sh_builtin_func_t *builtin, WORD_LIST *words, int flags, int su
return (result);
}
-static void
+void
uw_maybe_restore_getopt_state (void *arg)
{
sh_getopt_state_t *gs;
diff --git a/execute_cmd.h b/execute_cmd.h
index f8004342..97a70509 100644
--- a/execute_cmd.h
+++ b/execute_cmd.h
@@ -121,6 +121,7 @@ extern void restore_funcarray_state (struct func_array_state *);
extern void uw_restore_funcarray_state (void *);
#endif
+extern void uw_maybe_restore_getopt_state (void *);
extern void uw_lastpipe_cleanup (void *);
extern void bind_lastarg (char *);
diff --git a/subst.c b/subst.c
index aa7f2f56..019e6aa9 100644
--- a/subst.c
+++ b/subst.c
@@ -6867,6 +6867,8 @@ function_substitute (char *string, int quoted, int flags)
int afd;
char *afn;
sigset_t set, oset;
+ sh_getopt_state_t *gs;
+ SHELL_VAR *gv;
#if defined (ARRAY_VARS)
ARRAY *ps;
#endif
@@ -6895,8 +6897,10 @@ function_substitute (char *string, int quoted, int flags)
exp_jump_to_top_level (DISCARD); /* XXX */
}
}
+ gs = sh_getopt_save_istate ();
begin_unwind_frame ("nofork comsub");
+ add_unwind_protect (uw_maybe_restore_getopt_state, gs);
/* Save command and expansion state we need. */
if (valsub == 0)
@@ -7024,6 +7028,10 @@ function_substitute (char *string, int quoted, int flags)
istring = s ? comsub_quote_string (s, quoted, flags) : savestring ("");
}
+ gv = find_variable ("OPTIND");
+ if (gv && gv->context == variable_context)
+ gs->gs_flags |= 1;
+
run_unwind_frame ("nofork comsub"); /* restores stdout, job control stuff */
last_command_subst_status = result;
--
2.43.0