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
This commit is contained in:
Juraj Lutter 2022-12-08 21:30:26 +01:00
parent 65308195e8
commit 3cf65f8a7f
3 changed files with 59 additions and 10 deletions

View File

@ -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);

View File

@ -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.
*/

View File

@ -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