Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
bin/ksh: add bash-like expand-tilde option
To:
OpenBSD tech <tech@openbsd.org>
Date:
Fri, 24 Apr 2026 21:08:03 +0200

Download raw body.

Thread
tech@,

default ksh completion already matches bash with expand-tilde enabled:
typing ~/sr and pressing TAB expands and completes the word to
/home/user/src.

Here a new option expand-tilde which using the bash name, but keep
current ksh behaviour by enabling it by default.

Disabling the option preserves the original ~ prefix, so the same input
completes to ~/src.

Ok?

Index: bin/ksh/edit.c
===================================================================
RCS file: /home/cvs/src/bin/ksh/edit.c,v
diff -u -p -r1.71 edit.c
--- bin/ksh/edit.c	23 Apr 2024 13:34:50 -0000	1.71
+++ bin/ksh/edit.c	18 Apr 2026 22:50:42 -0000
@@ -30,6 +30,7 @@ static void check_sigwinch(void);
 static int	x_file_glob(int, const char *, int, char ***);
 static int	x_command_glob(int, const char *, int, char ***);
 static int	x_locate_word(const char *, int, int, int *, int *);
+static void	x_preserve_tilde(char **, int, const char *, const char *);
 
 
 /* Called from main */
@@ -417,10 +418,24 @@ x_file_glob(int flags, const char *str, 
 	int nwords;
 	XPtrV w;
 	struct source *s, *sold;
+	char *tilde = NULL;
+	char *tilde_expanded = NULL;
+	int preserve_tilde = 0;
+	int tlen = 0;
 
 	if (slen < 0)
 		return 0;
 
+	if (!Flag(FEXPANDTILDE) && slen > 0 && str[0] == '~') {
+		for (tlen = 1; tlen < slen; tlen++)
+			if (str[tlen] == '/')
+				break;
+		tilde = str_nsave(str, tlen, ATEMP);
+		tilde_expanded = evalstr(tilde, DOTILDE);
+		if (tilde_expanded != null && tilde_expanded[0] != '~')
+			preserve_tilde = 1;
+	}
+
 	toglob = add_glob(str, slen);
 
 	/*
@@ -433,7 +448,8 @@ x_file_glob(int flags, const char *str, 
 	if (yylex(ONEWORD|UNESCAPE) != LWORD) {
 		source = sold;
 		internal_warningf("%s: substitute error", __func__);
-		return 0;
+		nwords = 0;
+		goto done;
 	}
 	source = sold;
 	XPinit(w, 32);
@@ -461,13 +477,52 @@ x_file_glob(int flags, const char *str, 
 	afree(toglob, ATEMP);
 
 	if (nwords) {
+		if (preserve_tilde)
+			x_preserve_tilde(words, nwords, tilde, tilde_expanded);
 		*wordsp = words;
 	} else if (words) {
 		x_free_words(nwords, words);
 		*wordsp = NULL;
 	}
+done:
+	if (tilde)
+		afree(tilde, ATEMP);
+	if (tilde_expanded && tilde_expanded != null)
+		afree(tilde_expanded, ATEMP);
 
 	return nwords;
+}
+
+static void
+x_preserve_tilde(char **words, int nwords, const char *tilde,
+    const char *tilde_expanded)
+{
+	size_t tilde_len, exp_len;
+	int i;
+
+	tilde_len = strlen(tilde);
+	exp_len = strlen(tilde_expanded);
+	if (exp_len == 0)
+		return;
+
+	for (i = 0; i < nwords; i++) {
+		char *w = words[i];
+		size_t rest_len;
+		char *nw;
+
+		if (strncmp(w, tilde_expanded, exp_len) != 0)
+			continue;
+		if (w[exp_len] != '\0' && w[exp_len] != '/')
+			continue;
+
+		rest_len = strlen(w + exp_len);
+		nw = areallocarray(NULL, tilde_len + rest_len + 1,
+		    sizeof(char), ATEMP);
+		memcpy(nw, tilde, tilde_len);
+		memcpy(nw + tilde_len, w + exp_len, rest_len + 1);
+		afree(w, ATEMP);
+		words[i] = nw;
+	}
 }
 
 /* Data structure used in x_command_glob() */
Index: bin/ksh/ksh.1
===================================================================
RCS file: /home/cvs/src/bin/ksh/ksh.1,v
diff -u -p -r1.223 ksh.1
--- bin/ksh/ksh.1	31 Dec 2025 22:12:25 -0000	1.223
+++ bin/ksh/ksh.1	18 Apr 2026 22:50:42 -0000
@@ -3630,6 +3630,9 @@ is read 13 times in a row.
 The shell is an interactive shell.
 This option can only be used when the shell is invoked.
 See above for a description of what this means.
+.It Ic expand-tilde
+Expand leading tilde expressions in file name completion results.
+Enabled by default.
 .It Ic login
 The shell is a login shell.
 This option can only be used when the shell is invoked.
Index: bin/ksh/main.c
===================================================================
RCS file: /home/cvs/src/bin/ksh/main.c,v
diff -u -p -r1.100 main.c
--- bin/ksh/main.c	23 Jul 2023 23:42:03 -0000	1.100
+++ bin/ksh/main.c	18 Apr 2026 22:50:42 -0000
@@ -261,11 +261,12 @@ main(int argc, char *argv[])
 	}
 
 	/* Set edit mode to emacs by default, may be overridden
-	 * by the environment or the user.  Also, we want tab completion
-	 * on in vi by default. */
+	 * by the environment or the user.  Also, we want expand-tilde and
+	 * tab completion on in vi by default. */
 #if defined(EMACS)
 	change_flag(FEMACS, OF_SPECIAL, 1);
 #endif /* EMACS */
+	Flag(FEXPANDTILDE) = 1;
 #if defined(VI)
 	Flag(FVITABCOMPLETE) = 1;
 #endif /* VI */
Index: bin/ksh/misc.c
===================================================================
RCS file: /home/cvs/src/bin/ksh/misc.c,v
diff -u -p -r1.78 misc.c
--- bin/ksh/misc.c	24 Dec 2021 22:08:37 -0000	1.78
+++ bin/ksh/misc.c	18 Apr 2026 22:50:42 -0000
@@ -130,6 +130,7 @@ const struct option sh_options[] = {
 	{ "emacs",	  0,		OF_ANY },
 #endif
 	{ "errexit",	'e',		OF_ANY },
+	{ "expand-tilde", 0,		OF_ANY }, /* non-standard */
 #ifdef EMACS
 	{ "gmacs",	  0,		OF_ANY },
 #endif
Index: bin/ksh/sh.h
===================================================================
RCS file: /home/cvs/src/bin/ksh/sh.h,v
diff -u -p -r1.78 sh.h
--- bin/ksh/sh.h	5 Mar 2026 05:38:58 -0000	1.78
+++ bin/ksh/sh.h	18 Apr 2026 22:50:42 -0000
@@ -141,6 +141,7 @@ enum sh_flag {
 	FEMACS,		/* emacs command editing */
 #endif
 	FERREXIT,	/* -e: quit on error */
+	FEXPANDTILDE,	/* expand ~ in completion */
 #ifdef EMACS
 	FGMACS,		/* gmacs command editing */
 #endif

-- 
wbr, Kirill