git: 3cf65f8a7f8e - main - sh(1): Allow non-printing characters in prompt strings

From: Juraj Lutter <otis_at_FreeBSD.org>
Date: Thu, 22 Dec 2022 18:13:05 UTC
The branch main has been updated by otis (ports committer):

URL: https://cgit.FreeBSD.org/src/commit/?id=3cf65f8a7f8ee993118fe06f3c187ad30f875132

commit 3cf65f8a7f8ee993118fe06f3c187ad30f875132
Author:     Juraj Lutter <otis@FreeBSD.org>
AuthorDate: 2022-12-08 20:30:26 +0000
Commit:     Juraj Lutter <otis@FreeBSD.org>
CommitDate: 2022-12-22 18:10:48 +0000

    sh(1): Allow non-printing characters in prompt strings
    
    Introduce new prompt format characters:
    
    - '\[' starts the sequence of non-printing chatacters
    - '\]' ends the sequence of non-printing characters
    
    Within these sequences, the following characters are now supported:
    
    - '\a' emits ASCII BEL (0x07, 007) character
    - '\e' emits ASCII ESC (0x1b, 033) character
    - '\r' emits ASCII CR (0x0d, 015) character
    - '\n' emits ASCII CRLF sequence
    
    These can be used to embed ANSI sequences into prompt strings.
    
    Example in .shrc:
    
    PS1="\[\e[7m\]\u@\h\[\e[0m\]:\w \\$ "
    
    This tries to maintain some degree of compatibility with GNU bash,
    that uses GNU readline library (which behaves slightly different from
    BSD editline): It has two "non-printing boundary" characters:
    
    - RL_PROMPT_START_IGNORE (\001)
    - RL_PROMPT_END_IGNORE (\002)
    
    while BSD editline only has one (when using EL_PROMPT_ESC setting), so
    for this purpose, ASCII \001 was chosen and both \[ and \] emits
    this character.
    
    And while here, enlarge PROMPTLEN from 128 to 192 characters.
    
    Reviewed by:            jilles
    Approved by:            jilles
    Differential Revision:  https://reviews.freebsd.org/D37701
---
 bin/sh/histedit.c |  2 +-
 bin/sh/parser.c   | 47 +++++++++++++++++++++++++++++++++++++++--------
 bin/sh/sh.1       | 20 +++++++++++++++++++-
 3 files changed, 59 insertions(+), 10 deletions(-)

diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c
index 8812200279f0..0eb8c6c1784f 100644
--- a/bin/sh/histedit.c
+++ b/bin/sh/histedit.c
@@ -190,7 +190,7 @@ histedit(void)
 			if (el != NULL) {
 				if (hist)
 					el_set(el, EL_HIST, history, hist);
-				el_set(el, EL_PROMPT, getprompt);
+				el_set(el, EL_PROMPT_ESC, getprompt, '\001');
 				el_set(el, EL_ADDFN, "sh-complete",
 				    "Filename completion",
 				    sh_complete);
diff --git a/bin/sh/parser.c b/bin/sh/parser.c
index e75798800edf..7f8283ca4dff 100644
--- a/bin/sh/parser.c
+++ b/bin/sh/parser.c
@@ -70,7 +70,7 @@ __FBSDID("$FreeBSD$");
  * Shell command parser.
  */
 
-#define	PROMPTLEN	128
+#define	PROMPTLEN	192
 
 /* values of checkkwd variable */
 #define CHKALIAS	0x1
@@ -2060,6 +2060,44 @@ getprompt(void *unused __unused)
 		if (*fmt == '\\')
 			switch (*++fmt) {
 
+				/*
+				 * Non-printing sequence begin and end.
+				 */
+			case '[':
+			case ']':
+				ps[i] = '\001';
+				break;
+
+				/*
+				 * Literal \ and some ASCII characters:
+				 * \a	BEL
+				 * \e	ESC
+				 * \r	CR
+				 */
+			case '\\':
+			case 'a':
+			case 'e':
+			case 'r':
+				if (*fmt == 'a')
+					ps[i] = '\007';
+				else if (*fmt == 'e')
+					ps[i] = '\033';
+				else if (*fmt == 'r')
+					ps[i] = '\r';
+				else
+					ps[i] = '\\';
+				break;
+
+				/*
+				 * CRLF sequence
+				 */
+			case 'n':
+				if (i < PROMPTLEN - 3) {
+					ps[i++] = '\r';
+					ps[i] = '\n';
+				}
+				break;
+
 				/*
 				 * Hostname.
 				 *
@@ -2136,13 +2174,6 @@ getprompt(void *unused __unused)
 				ps[i] = (geteuid() != 0) ? '$' : '#';
 				break;
 
-				/*
-				 * A literal \.
-				 */
-			case '\\':
-				ps[i] = '\\';
-				break;
-
 				/*
 				 * Emit unrecognized formats verbatim.
 				 */
diff --git a/bin/sh/sh.1 b/bin/sh/sh.1
index 7a30020f2f7f..330685a911e1 100644
--- a/bin/sh/sh.1
+++ b/bin/sh/sh.1
@@ -32,7 +32,7 @@
 .\"	from: @(#)sh.1	8.6 (Berkeley) 5/4/95
 .\" $FreeBSD$
 .\"
-.Dd May 10, 2021
+.Dd December 14, 2022
 .Dt SH 1
 .Os
 .Sh NAME
@@ -1446,6 +1446,24 @@ for normal users and
 for superusers.
 .It Li \e\e
 A literal backslash.
+.It Li \e[
+Start of a sequence of non-printing characters (used, for example,
+to embed ANSI CSI sequences into the prompt).
+.It Li \e]
+End of a sequence of non-printing characters.
+.El
+.Pp
+The following special and non-printing characters are supported
+within the sequence of non-printing characters:
+.Bl -tag -width indent
+.It Li \ea
+Emits ASCII BEL (0x07, 007) character.
+.It Li \ee
+Emits ASCII ESC (0x1b, 033) character.
+.It Li \er
+Emits ASCII CR (0x0d, 015) character.
+.It Li \en
+Emits CRLF sequence.
 .El
 .It Va PS2
 The secondary prompt string, which defaults to