Index | Thread | Search

From:
Miguel Landaeta <miguel@miguel.cc>
Subject:
bin/ksh: don't let local EDITOR/VISUAL assignments change edit mode
To:
tech@openbsd.org
Date:
Fri, 3 Jul 2026 10:51:39 +0000

Download raw body.

Thread
  • Miguel Landaeta:

    bin/ksh: don't let local EDITOR/VISUAL assignments change edit mode

Hi,

With the default ksh configuration, if you ever decide to run a command like:

EDITOR=vi true

Or, more realistically, something like this (how I stumbled upon this bug):

EDITOR=vim git rebase -i HEAD~50

Once the child process returns, the shell ends up stuck
in whichever mode the local assignment selected, breaking emacs
bindings such as up/down history and incremental search (^R).

You can work around this by running: set -o emacs

However, I think a local assignment only exists to influence the
child process's environment; the interactive shell's own line-editing
mode has no reason to react to it, and getting silently and
permanently stuck in the wrong mode is frankly annoying behavior.

Please see the patch below, is this an OK approach to fix this?

Thanks,
Miguel.


diff --git a/bin/ksh/var.c b/bin/ksh/var.c
index 9e0bd7c0c3f..c97c822d45f 100644
--- a/bin/ksh/var.c
+++ b/bin/ksh/var.c
@@ -641,9 +641,20 @@ typeset(const char *var, int set, int clr, int field, int base)
 	    strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))
 		errorf("%s: restricted", tvar);
 
-	vp = (set&LOCAL) ? local(tvar, (set & LOCAL_COPY) ? true : false) :
-	    global(tvar);
-	set &= ~(LOCAL|LOCAL_COPY);
+	{
+		int is_local = (set & LOCAL) != 0;
+
+		vp = is_local ? local(tvar, (set & LOCAL_COPY) ? true : false) :
+		    global(tvar);
+		set &= ~(LOCAL|LOCAL_COPY);
+
+		/* A local EDITOR/VISUAL only affects the child process; skip it
+		 * here because popblock() can't undo it later. */
+		if (is_local && (vp->flag & SPECIAL) &&
+		    (special(vp->name) == V_EDITOR ||
+		    special(vp->name) == V_VISUAL))
+			vp->flag &= ~SPECIAL;
+	}
 
 	vpbase = (vp->flag & ARRAY) ? global(arrayname(tvar)) : vp;