loksh-noxz

[fork] a Linux port of OpenBSD's ksh
git clone https://noxz.tech/git/loksh-noxz.git
Log | Files | README

commit: b1863a7a8a9384b7c4791336080a1a0b3e768cee
parent: 19a1311b43128b37987332c02d3fe7cab4b395fc
author: Chris Noxz <chris@noxz.tech>
date:   Sat, 10 Jun 2023 12:44:23 +0000
Match upstream
MREADME46+++++---------
AREADME.md36+++++++++++
Memacs.c21++++---
Mexec.c10++--
Mksh.163+++++++++++++++-----
Mlex.c4+-
Mmain.c10+++-
Mmeson.build7++-
Mmisc.c10++--
Msh.137+++++++++---
Msubprojects/lolibc2+-
Msyn.c4+-
Mtty.c4+-
Mvar.c4+-
Mvi.c24+++++---
15 files changed, 193 insertions(+), 89 deletions(-)
diff --git a/README b/README
@@ -1,36 +1,22 @@
-```
- _       _        _
-| | ___ | | _____| |__
-| |/ _ \| |/ / __| '_ \
-| | (_) |   <\__ \ | | |
-|_|\___/|_|\_\___/_| |_|
-```
+$OpenBSD: README,v 1.16 2017/05/11 20:17:17 jmc Exp $
 
-[![Build Status](https://github.com/dimkr/loksh/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/dimkr/loksh/actions)
+Last updated Jul '99 for pdksh-5.2.14.
 
-## Overview
+PDksh is a mostly complete AT&T ksh look-alike (see NOTES file for a list
+of things not supported).  Work is mostly finished to make it fully
+compatible with both POSIX and AT&T ksh (when the two don't conflict).
 
-loksh is a [Linux](https://www.kernel.org/) port of [OpenBSD](http://www.openbsd.org/)'s *ksh*.
+PDksh was being maintained by Michael Rendell (michael@cs.mun.ca),
+who took over from Simon J. Gerraty (sjg@zen.void.oz.au) at the latter's
+suggestion.
 
-Unlike other ports of *ksh*, loksh targets only one platform, follows upstream closely and [keeps changes to a minimum](https://github.com/dimkr/loksh/compare/upstream%2Fmaster...master). loksh does not add any extra features; this reduces the risk of introducing security vulnerabilities and makes loksh a good fit for resource-constrained systems.
+Files of interest:
+	CONTRIBUTORS	short history of pdksh, people who contributed, etc.
+	NOTES		lists of known bugs in pdksh, at&t ksh, and posix.
+	PROJECTS	list of things that need to be done in pdksh.
+	LEGAL		A file detailing legal issues concerning pdksh.
 
-## Installation
 
-loksh is included in many distributions:
-
-[![Packaging status](https://repology.org/badge/vertical-allrepos/loksh.svg?minversion=6.7.5&header=)](https://repology.org/project/loksh/versions)
-
-For example, under [Alpine Linux](https://alpinelinux.org/), loksh can be installed using `apk add loksh`.
-
-Alternatively, to build and install loksh from source:
-
-```bash
-meson --prefix=/usr build
-ninja -C build install
-```
-
-## Legal Information
-
-loksh is licensed under the same license as the upstream code it is based on.
-
-The ASCII art logo at the top was made using [FIGlet](http://www.figlet.org/).
+BTW, THE MOST FREQUENTLY REPORTED BUG IS
+	echo hi | read a; echo $a	# Does not print hi
+I'm aware of this and there is no need to report it.
diff --git a/README.md b/README.md
@@ -0,0 +1,36 @@
+```
+ _       _        _
+| | ___ | | _____| |__
+| |/ _ \| |/ / __| '_ \
+| | (_) |   <\__ \ | | |
+|_|\___/|_|\_\___/_| |_|
+```
+
+[![Build Status](https://github.com/dimkr/loksh/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/dimkr/loksh/actions)
+
+## Overview
+
+loksh is a [Linux](https://www.kernel.org/) port of [OpenBSD](http://www.openbsd.org/)'s *ksh*.
+
+Unlike other ports of *ksh*, loksh targets only one platform, follows upstream closely and [keeps changes to a minimum](https://github.com/dimkr/loksh/compare/upstream%2Fmaster...master). loksh does not add any extra features; this reduces the risk of introducing security vulnerabilities and makes loksh a good fit for resource-constrained systems.
+
+## Installation
+
+loksh is included in many distributions:
+
+[![Packaging status](https://repology.org/badge/vertical-allrepos/loksh.svg?minversion=6.7.5&header=)](https://repology.org/project/loksh/versions)
+
+For example, under [Alpine Linux](https://alpinelinux.org/), loksh can be installed using `apk add loksh`.
+
+Alternatively, to build and install loksh from source:
+
+```bash
+meson --prefix=/usr build
+ninja -C build install
+```
+
+## Legal Information
+
+loksh is licensed under the same license as the upstream code it is based on.
+
+The ASCII art logo at the top was made using [FIGlet](http://www.figlet.org/).
diff --git a/emacs.c b/emacs.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: emacs.c,v 1.87 2020/05/08 14:30:42 jca Exp $	*/
+/*	$OpenBSD: emacs.c,v 1.89 2021/10/09 21:38:00 halex Exp $	*/
 
 /*
  *  Emacs-like command line editing and history
@@ -897,7 +897,7 @@ x_search_hist(int c)
 		if ((c = x_e_getc()) < 0)
 			return KSTD;
 		f = kb_find_hist_func(c);
-		if (c == CTRL('[')) {
+		if (c == CTRL('[') || c == CTRL('@')) {
 			x_e_ungetc(c);
 			break;
 		} else if (f == x_search_hist)
@@ -1851,11 +1851,17 @@ x_e_getu8(char *buf, int off)
 		return -1;
 	buf[off++] = c;
 
-	if (c == 0xf4)
+	/*
+	 * In the following, comments refer to violations of
+	 * the inequality tests at the ends of the lines.
+	 * See the utf8(7) manual page for details.
+	 */
+
+	if ((c & 0xf8) == 0xf0 && c < 0xf5)  /* beyond Unicode */
 		len = 4;
 	else if ((c & 0xf0) == 0xe0)
 		len = 3;
-	else if ((c & 0xe0) == 0xc0 && c > 0xc1)
+	else if ((c & 0xe0) == 0xc0 && c > 0xc1)  /* use single byte */
 		len = 2;
 	else
 		len = 1;
@@ -1865,9 +1871,10 @@ x_e_getu8(char *buf, int off)
 		if (cc == -1)
 			break;
 		if (isu8cont(cc) == 0 ||
-		    (c == 0xe0 && len == 3 && cc < 0xa0) ||
-		    (c == 0xed && len == 3 && cc & 0x20) ||
-		    (c == 0xf4 && len == 4 && cc & 0x30)) {
+		    (c == 0xe0 && len == 3 && cc < 0xa0) ||  /* use 2 bytes */
+		    (c == 0xed && len == 3 && cc > 0x9f) ||  /* surrogates  */
+		    (c == 0xf0 && len == 4 && cc < 0x90) ||  /* use 3 bytes */
+		    (c == 0xf4 && len == 4 && cc > 0x8f)) {  /* beyond Uni. */
 			x_e_ungetc(cc);
 			break;
 		}
diff --git a/exec.c b/exec.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: exec.c,v 1.74 2019/06/28 13:34:59 deraadt Exp $	*/
+/*	$OpenBSD: exec.c,v 1.76 2022/10/10 14:57:48 kn Exp $	*/
 
 /*
  * execute command tree
@@ -114,10 +114,12 @@ execute(struct op *volatile t,
 		for (iowp = t->ioact; *iowp != NULL; iowp++) {
 			if (iosetup(*iowp, tp) < 0) {
 				exstat = rv = 1;
-				/* Redirection failures for special commands
+				/* Except in the permanent case (exec 2>afile),
+				 * redirection failures for special commands
 				 * cause (non-interactive) shell to exit.
 				 */
-				if (tp && tp->type == CSHELL &&
+				if (tp && tp->val.f != c_exec &&
+				    tp->type == CSHELL &&
 				    (tp->flag & SPEC_BI))
 					errorf(NULL);
 				/* Deal with FERREXIT, quitenv(), etc. */
@@ -1197,7 +1199,7 @@ herein(const char *content, int sub)
 	 * doesn't get removed too soon).
 	 */
 	h = maketemp(ATEMP, TT_HEREDOC_EXP, &genv->temps);
-	if (!(shf = h->shf) || (fd = open(h->name, O_RDONLY, 0)) == -1) {
+	if (!(shf = h->shf) || (fd = open(h->name, O_RDONLY)) == -1) {
 		warningf(true, "can't %s temporary file %s: %s",
 		    !shf ? "create" : "open",
 		    h->name, strerror(errno));
diff --git a/ksh.1 b/ksh.1
@@ -1,8 +1,8 @@
-.\"	$OpenBSD: ksh.1,v 1.210 2020/09/20 14:40:45 millert Exp $
+.\"	$OpenBSD: ksh.1,v 1.218 2022/12/26 17:45:27 jmc Exp $
 .\"
 .\"	Public Domain
 .\"
-.Dd $Mdocdate: September 20 2020 $
+.Dd $Mdocdate: December 26 2022 $
 .Dt KSH 1
 .Os
 .Sh NAME
@@ -745,9 +745,10 @@ Operators (e.g.\&
 must be unquoted.
 .It
 The second operand of the
-.Sq !=
+.Sq = ,
+.Sq ==
 and
-.Sq =
+.Sq !=
 expressions are patterns (e.g. the comparison
 .Ic [[ foobar = f*r ]]
 succeeds).
@@ -2712,9 +2713,7 @@ Exit status is set to zero.
 .Cm +-x Oc
 .Op Fl p
 .Op Cm +
-.Oo Ar name
-.Op Ns = Ns Ar value
-.Ar ... Oc
+.Op Ar name Ns Oo = Ns Ar value Oc Ar ...
 .Xc
 Without arguments,
 .Ic alias
@@ -3219,6 +3218,25 @@ resetting
 .Ev OPTIND ,
 may lead to unexpected results.
 .Pp
+The following code fragment shows how one might process the arguments
+for a command that can take the option
+.Fl a
+and the option
+.Fl o ,
+which requires an argument.
+.Bd -literal -offset indent
+while getopts ao: name
+do
+	case $name in
+	a)	flag=1 ;;
+	o)	oarg=$OPTARG ;;
+	?)	echo "Usage: ..."; exit 2 ;;
+	esac
+done
+shift $(($OPTIND - 1))
+echo "Non-option arguments: " "$@"
+.Ed
+.Pp
 .It Xo
 .Ic hash
 .Op Fl r
@@ -3434,9 +3452,7 @@ option is used, input is saved to the history file.
 .It Xo
 .Ic readonly
 .Op Fl p
-.Oo Ar parameter
-.Op Ns = Ns Ar value
-.Ar ... Oc
+.Op Ar parameter Ns Oo = Ns Ar value Oc Ar ...
 .Xc
 Sets the read-only attribute of the named parameters.
 If values are given,
@@ -3993,7 +4009,7 @@ Times are reported to standard error; the format of the output is:
 .Pp
 If the
 .Fl p
-option is given the output is slightly longer:
+option is given, the output is slightly longer:
 .Bd -literal -offset indent
 real     0.00
 user     0.00
@@ -4105,11 +4121,7 @@ Short form of
 .Op Fl i Ns Op Ar n
 .No \&| Fl f Op Fl tux
 .Oc
-.Oo
-.Ar name
-.Op Ns = Ns Ar value
-.Ar ...
-.Oc
+.Op Ar name Ns Oo = Ns Ar value Oc Ar ...
 .Xc
 Display or set parameter attributes.
 With no
@@ -5060,6 +5072,8 @@ See the
 command in
 .Sx Emacs editing mode
 for more information.
+.It ^R
+Redraw the current line.
 .It ^V
 Literal next.
 The next character typed is not treated specially (can be used
@@ -5560,6 +5574,23 @@ Privileged shell profile.
 .Xr environ 7 ,
 .Xr script 7
 .Rs
+.%A S. R. Bourne
+.%T The UNIX Shell
+.%J Bell System Technical Journal
+.%V 57:6
+.%P pp. 1971-1990
+.%D 1978
+.Re
+.Rs
+.\" 4.4BSD USD:3
+.%A S. R. Bourne
+.%T \&An Introduction to the UNIX Shell
+.%I AT&T Bell Laboratories
+.%R Computing Science Technical Report
+.%N 70
+.%D 1978
+.Re
+.Rs
 .%A Morris Bolsky
 .%A David Korn
 .%B The KornShell Command and Programming Language
diff --git a/lex.c b/lex.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: lex.c,v 1.78 2018/01/15 14:58:05 jca Exp $	*/
+/*	$OpenBSD: lex.c,v 1.79 2023/02/08 17:22:10 kn Exp $	*/
 
 /*
  * lexical analysis and source input
@@ -1335,6 +1335,7 @@ dopprompt(const char *sp, int ntruncate, const char **spp, int doprint)
 			case 'u':	/* '\' 'u' username */
 				strlcpy(strbuf, username, sizeof strbuf);
 				break;
+#ifndef SMALL
 			case 'v':	/* '\' 'v' version (short) */
 				p = strchr(ksh_version, ' ');
 				if (p)
@@ -1350,6 +1351,7 @@ dopprompt(const char *sp, int ntruncate, const char **spp, int doprint)
 			case 'V':	/* '\' 'V' version (long) */
 				strlcpy(strbuf, ksh_version, sizeof strbuf);
 				break;
+#endif /* SMALL */
 			case 'w':	/* '\' 'w' cwd */
 				p = str_val(global("PWD"));
 				n = strlen(str_val(global("HOME")));
diff --git a/main.c b/main.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: main.c,v 1.98 2019/06/28 13:34:59 deraadt Exp $	*/
+/*	$OpenBSD: main.c,v 1.99 2023/02/08 17:22:10 kn Exp $	*/
 
 /*
  * startup, main loop, environments and error handling
@@ -81,7 +81,9 @@ static const char initifs[] = "IFS= \t\n";
 static const char initsubs[] = "${PS2=> } ${PS3=#? } ${PS4=+ }";
 
 static const char *initcoms [] = {
+#ifndef SMALL
 	"typeset", "-r", "KSH_VERSION", NULL,
+#endif /* SMALL */
 	"typeset", "-x", "SHELL", "PATH", "HOME", "PWD", "OLDPWD", NULL,
 	"typeset", "-ir", "PPID", NULL,
 	"typeset", "-i", "OPTIND=1", NULL,
@@ -110,7 +112,9 @@ static const char *initcoms [] = {
 
 char username[_PW_NAME_LEN + 1];
 
+#ifndef SMALL
 #define version_param  (initcoms[2])
+#endif /* SMALL */
 
 /* The shell uses its own variation on argv, to build variables like
  * $0 and $@.
@@ -247,7 +251,9 @@ main(int argc, char *argv[])
 	    (strlen(kshname) >= 3 &&
 	    !strcmp(&kshname[strlen(kshname) - 3], "/sh"))) {
 		Flag(FSH) = 1;
+#ifndef SMALL
 		version_param = "SH_VERSION";
+#endif /* SMALL */
 	}
 
 	/* Set edit mode to emacs by default, may be overridden
@@ -296,8 +302,10 @@ main(int argc, char *argv[])
 	}
 	ppid = getppid();
 	setint(global("PPID"), (int64_t) ppid);
+#ifndef SMALL
 	/* setstr can't fail here */
 	setstr(global(version_param), ksh_version, KSH_RETURN_ERROR);
+#endif /* SMALL */
 
 	/* execute initialization statements */
 	for (wp = (char**) initcoms; *wp != NULL; wp++) {
diff --git a/meson.build b/meson.build
@@ -1,11 +1,14 @@
-project('loksh', 'c', version: '6.8')
+project('loksh', 'c', version: '7.3')
 
 lolibc = subproject('lolibc')
 
 c_args = ['-D_GNU_SOURCE', '-DEMACS', '-DVI', '-Wno-format-security', '-Wno-pointer-sign']
 deps = []
 
-ncurses = dependency('ncursesw', required: false)
+ncurses = dependency('ncurses', required: false)
+if not ncurses.found()
+	ncurses = dependency('ncursesw', required: false)
+endif
 if ncurses.found()
 	deps += [ncurses]
 else
diff --git a/misc.c b/misc.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: misc.c,v 1.75 2020/07/22 19:20:41 millert Exp $	*/
+/*	$OpenBSD: misc.c,v 1.78 2021/12/24 22:08:37 deraadt Exp $	*/
 
 /*
  * Miscellaneous functions
@@ -713,7 +713,7 @@ do_gmatch(const unsigned char *s, const unsigned char *se,
 static int
 posix_cclass(const unsigned char *pattern, int test, const unsigned char **ep)
 {
-	struct cclass *cc;
+	const struct cclass *cc;
 	const unsigned char *colon;
 	size_t len;
 	int rval = 0;
@@ -905,7 +905,7 @@ ksh_getopt(char **argv, Getopt *go, const char *options)
 			go->buf[0] = c;
 			go->optarg = go->buf;
 		} else {
-			warningf(true, "%s%s-%c: unknown option",
+			warningf(false, "%s%s-%c: unknown option",
 			    (go->flags & GF_NONAME) ? "" : argv[0],
 			    (go->flags & GF_NONAME) ? "" : ": ", c);
 			if (go->flags & GF_ERROR)
@@ -931,7 +931,7 @@ ksh_getopt(char **argv, Getopt *go, const char *options)
 				go->optarg = go->buf;
 				return ':';
 			}
-			warningf(true, "%s%s-`%c' requires argument",
+			warningf(false, "%s%s-`%c' requires argument",
 			    (go->flags & GF_NONAME) ? "" : argv[0],
 			    (go->flags & GF_NONAME) ? "" : ": ", c);
 			if (go->flags & GF_ERROR)
@@ -1135,7 +1135,7 @@ ksh_get_wd(char *buf, int bsize)
 	/* Assume getcwd() available */
 	if (!buf) {
 		bsize = PATH_MAX;
-		b = alloc(PATH_MAX + 1, ATEMP);
+		b = alloc(bsize, ATEMP);
 	} else
 		b = buf;
 
diff --git a/sh.1 b/sh.1
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: sh.1,v 1.152 2019/05/22 15:23:23 schwarze Exp $
+.\"	$OpenBSD: sh.1,v 1.156 2022/12/19 08:19:50 sdk Exp $
 .\"
 .\" Copyright (c) 2015 Jason McIntyre <jmc@openbsd.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: May 22 2019 $
+.Dd $Mdocdate: December 19 2022 $
 .Dt SH 1
 .Os
 .Sh NAME
@@ -508,6 +508,25 @@ is a colon,
 .Ev OPTARG
 is set to the unsupported option,
 otherwise an error message is displayed.
+.Pp
+The following code fragment shows how one might process the arguments
+for a command that can take the option
+.Fl a
+and the option
+.Fl o ,
+which requires an argument.
+.Bd -literal -offset indent
+while getopts ao: name
+do
+	case $name in
+	a)	flag=1 ;;
+	o)	oarg=$OPTARG ;;
+	?)	echo "Usage: ..."; exit 2 ;;
+	esac
+done
+shift $(($OPTIND - 1))
+echo "Non-option arguments: " "$@"
+.Ed
 .It Ic hash Op Fl r | Ar utility
 Add
 .Ar utility
@@ -733,9 +752,9 @@ parameter 2 takes
 and so on.
 Parameters
 .Sq #
-to
+down to
 .Sq Po #\(mi Ns Ar n Pc Ns +1
-and downwards are unset and
+are unset and
 .Sq #
 is updated to the new number of positional parameters.
 If
@@ -1033,9 +1052,9 @@ argument is ignored for the commands
 .Ic 0 , ^ , $ ,
 and
 .Ic c .
-If the motion moves towards the beginning of the line
+If the motion moves towards the beginning of the line,
 the character under the cursor is not deleted;
-if it moves towards the end of the line
+if it moves towards the end of the line,
 it is deleted.
 .It Ic C
 Delete the characters between the cursor and the line end,
@@ -1070,7 +1089,7 @@ placing them in the save buffer.
 A special motion command,
 .Ic d ,
 may be used to delete the entire line.
-If the motion moves towards the beginning of the line
+If the motion moves towards the beginning of the line,
 the character under the cursor is not deleted.
 .It Oo Ar count Oc Ns Ic D
 Delete the characters between the cursor and the line end,
@@ -1081,7 +1100,7 @@ placing them in the save buffer.
 A special motion command,
 .Ic y ,
 may be used to yank the entire line.
-If the motion moves towards the beginning of the line
+If the motion moves towards the beginning of the line,
 the character under the cursor is not yanked.
 .It Oo Ar count Oc Ns Ic Y
 Yank (copy) the characters between the cursor and the line end,
@@ -1371,7 +1390,7 @@ The format is:
 .Pp
 Where
 .Ar expression
-is an integer, parameter name, or array reference,
+is an integer or parameter name,
 optionally combined with any of the operators described below,
 listed and grouped according to precedence:
 .Bl -tag -width Ds
diff --git a/subprojects/lolibc b/subprojects/lolibc
@@ -1 +1 @@
-Subproject commit 801784932948e693c9cf172bec73fd26a244e3d0
+Subproject commit f6e9da78ba0ca66a8711a7e8cc64e418e68eaa00
diff --git a/syn.c b/syn.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: syn.c,v 1.39 2018/04/24 08:25:16 kn Exp $	*/
+/*	$OpenBSD: syn.c,v 1.40 2021/07/05 13:41:46 millert Exp $	*/
 
 /*
  * shell parser (C version)
@@ -331,6 +331,8 @@ get_command(int cf)
 		nesting_push(&old_nesting, c);
 		t = newtp((c == WHILE) ? TWHILE : TUNTIL);
 		t->left = c_list(true);
+		if (t->left == NULL)
+			syntaxerr(NULL);
 		t->right = dogroup();
 		nesting_pop(&old_nesting);
 		break;
diff --git a/tty.c b/tty.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: tty.c,v 1.18 2019/06/28 13:34:59 deraadt Exp $	*/
+/*	$OpenBSD: tty.c,v 1.19 2021/10/24 21:24:21 deraadt Exp $	*/
 
 #include <errno.h>
 #include <fcntl.h>
@@ -33,7 +33,7 @@ tty_init(int init_ttystate)
 	tty_close();
 	tty_devtty = 1;
 
-	tfd = open("/dev/tty", O_RDWR, 0);
+	tfd = open("/dev/tty", O_RDWR);
 	if (tfd == -1) {
 		tty_devtty = 0;
 		warningf(false, "No controlling tty (open /dev/tty: %s)",
diff --git a/var.c b/var.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: var.c,v 1.71 2020/02/21 18:21:23 tb Exp $	*/
+/*	$OpenBSD: var.c,v 1.72 2021/03/05 15:22:03 zhuk Exp $	*/
 
 #include <sys/stat.h>
 #include <sys/time.h>
@@ -644,7 +644,7 @@ typeset(const char *var, int set, int clr, int field, int base)
 	    global(tvar);
 	set &= ~(LOCAL|LOCAL_COPY);
 
-	vpbase = (vp->flag & ARRAY) ? global(arrayname(var)) : vp;
+	vpbase = (vp->flag & ARRAY) ? global(arrayname(tvar)) : vp;
 
 	/* only allow export flag to be set.  at&t ksh allows any attribute to
 	 * be changed, which means it can be truncated or modified
diff --git a/vi.c b/vi.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: vi.c,v 1.57 2020/09/20 14:40:45 millert Exp $	*/
+/*	$OpenBSD: vi.c,v 1.60 2021/03/12 02:10:25 millert Exp $	*/
 
 /*
  *	vi command editing
@@ -23,6 +23,9 @@
 #include "sh.h"
 #include "edit.h"
 
+#undef CTRL
+#define	CTRL(x)		((x) & 0x1F)	/* ASCII */
+
 struct edstate {
 	char	*cbuf;		/* main buffer to build the command line */
 	int	cbufsize;	/* number of bytes allocated for cbuf */
@@ -60,7 +63,7 @@ static int	Endword(int);
 static int	grabhist(int, int);
 static int	grabsearch(int, int, int, char *);
 static void	do_clear_screen(void);
-static void	redraw_line(int);
+static void	redraw_line(int, int);
 static void	refresh_line(int);
 static int	outofwin(void);
 static void	rewindow(void);
@@ -663,6 +666,10 @@ vi_insert(int ch)
 		do_clear_screen();
 		break;
 
+	case CTRL('r'):
+		redraw_line(1, 0);
+		break;
+
 	case CTRL('i'):
 		if (Flag(FVITABCOMPLETE)) {
 			complete_word(0, 0);
@@ -724,7 +731,7 @@ vi_cmd(int argcnt, const char *cmd)
 			break;
 
 		case CTRL('r'):
-			redraw_line(1);
+			redraw_line(1, 0);
 			break;
 
 		case '@':
@@ -1742,18 +1749,19 @@ do_clear_screen(void)
 			neednl = 0;
 	}
 #endif
-	redraw_line(neednl);
+	/* Only print the full prompt if we cleared the screen. */
+	redraw_line(neednl, !neednl);
 }
 
 static void
-redraw_line(int neednl)
+redraw_line(int neednl, int full)
 {
 	(void) memset(wbuf[win], ' ', wbuf_len);
 	if (neednl) {
 		x_putc('\r');
 		x_putc('\n');
 	}
-	vi_pprompt(0);
+	vi_pprompt(full);
 	cur_col = pwidth;
 	morec = ' ';
 }
@@ -2114,7 +2122,7 @@ complete_word(int command, int count)
 			vi_error();
 			x_print_expansions(nwords, words, is_command);
 			x_free_words(nwords, words);
-			redraw_line(0);
+			redraw_line(0, 0);
 			return -1;
 		}
 		/*
@@ -2188,7 +2196,7 @@ print_expansions(struct edstate *e)
 	}
 	x_print_expansions(nwords, words, is_command);
 	x_free_words(nwords, words);
-	redraw_line(0);
+	redraw_line(0, 0);
 	return 0;
 }