diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3a352c11b..76c58e787 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
- The `functions --metadata --verbose` output now includes the function description (#597).
- Completions for `helm` added (#3829).
- Empty CDPATH elements are now equivalent to "." (#2106).
+- The `read` command now accepts simple strings for the prompt rather than fish script via the new `-P` and `--prompt-str` flags (#802).
---
diff --git a/doc_src/read.txt b/doc_src/read.txt
index 9810bf661..0e16747d1 100644
--- a/doc_src/read.txt
+++ b/doc_src/read.txt
@@ -23,6 +23,8 @@ The following options are available:
- `-p PROMPT_CMD` or `--prompt=PROMPT_CMD` uses the output of the shell command `PROMPT_CMD` as the prompt for the interactive mode. The default prompt command is set_color green; echo read; set_color normal; echo "> "
.
+- `-P PROMPT_STR` or `--prompt-str=PROMPT_STR` uses the string as the prompt for the interactive mode. It is equivalent to echo PROMPT_STR
and is provided solely to avoid the need to frame the prompt as a command. All special characters in the string are automatically escaped before being passed to the echo
command.
+
- `-R RIGHT_PROMPT_CMD` or `--right-prompt=RIGHT_PROMPT_CMD` uses the output of the shell command `RIGHT_PROMPT_CMD` as the right prompt for the interactive mode. There is no default right prompt command.
- `-s` or `--shell` enables syntax highlighting, tab completions and command termination suitable for entering shellscript code in the interactive mode.
diff --git a/src/builtin.cpp b/src/builtin.cpp
index 76e64b5c6..f1ef3dbcc 100644
--- a/src/builtin.cpp
+++ b/src/builtin.cpp
@@ -2123,11 +2123,13 @@ static int read_one_char_at_a_time(int fd, wcstring &buff, int nchars, bool spli
/// The read builtin. Reads from stdin and stores the values in environment variables.
static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
- wgetopter_t w;
wcstring buff;
+ wchar_t *cmd = argv[0];
int argc = builtin_count_args(argv);
int place = ENV_USER;
- const wchar_t *prompt = DEFAULT_READ_PROMPT;
+ wcstring prompt_cmd;
+ const wchar_t *prompt = NULL;
+ const wchar_t *prompt_str = NULL;
const wchar_t *right_prompt = L"";
const wchar_t *commandline = L"";
int exit_res = STATUS_BUILTIN_OK;
@@ -2137,36 +2139,28 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv)
bool array = false;
bool split_null = false;
- while (1) {
- static const struct woption long_options[] = {{L"export", no_argument, 0, 'x'},
- {L"global", no_argument, 0, 'g'},
- {L"local", no_argument, 0, 'l'},
- {L"universal", no_argument, 0, 'U'},
- {L"unexport", no_argument, 0, 'u'},
- {L"prompt", required_argument, 0, 'p'},
- {L"right-prompt", required_argument, 0, 'R'},
- {L"command", required_argument, 0, 'c'},
- {L"mode-name", required_argument, 0, 'm'},
- {L"nchars", required_argument, 0, 'n'},
- {L"shell", no_argument, 0, 's'},
- {L"array", no_argument, 0, 'a'},
- {L"null", no_argument, 0, 'z'},
- {L"help", no_argument, 0, 'h'},
- {0, 0, 0, 0}};
-
- int opt_index = 0;
-
- int opt = w.wgetopt_long(argc, argv, L"xglUup:R:c:hm:n:saz", long_options, &opt_index);
- if (opt == -1) break;
+ const wchar_t *short_options = L"ac:ghlm:n:p:suxzP:UR:";
+ const struct woption long_options[] = {{L"export", no_argument, NULL, 'x'},
+ {L"global", no_argument, NULL, 'g'},
+ {L"local", no_argument, NULL, 'l'},
+ {L"universal", no_argument, NULL, 'U'},
+ {L"unexport", no_argument, NULL, 'u'},
+ {L"prompt", required_argument, NULL, 'p'},
+ {L"prompt-str", required_argument, NULL, 'P'},
+ {L"right-prompt", required_argument, NULL, 'R'},
+ {L"command", required_argument, NULL, 'c'},
+ {L"mode-name", required_argument, NULL, 'm'},
+ {L"nchars", required_argument, NULL, 'n'},
+ {L"shell", no_argument, NULL, 's'},
+ {L"array", no_argument, NULL, 'a'},
+ {L"null", no_argument, NULL, 'z'},
+ {L"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}};
+ int opt;
+ wgetopter_t w;
+ while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
switch (opt) {
- case 0: {
- if (long_options[opt_index].flag != 0) break;
- streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
- long_options[opt_index].name);
- builtin_print_help(parser, streams, argv[0], streams.err);
- return STATUS_BUILTIN_ERROR;
- }
case L'x': {
place |= ENV_EXPORT;
break;
@@ -2191,6 +2185,10 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv)
prompt = w.woptarg;
break;
}
+ case L'P': {
+ prompt_str = w.woptarg;
+ break;
+ }
case L'R': {
right_prompt = w.woptarg;
break;
@@ -2236,20 +2234,36 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv)
builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
}
+ case ':': {
+ streams.err.append_format(BUILTIN_ERR_MISSING, cmd, argv[w.woptind - 1]);
+ return STATUS_BUILTIN_ERROR;
+ }
case L'?': {
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return STATUS_BUILTIN_ERROR;
}
default: {
- DIE("unexpected opt");
+ DIE("unexpected retval from wgetopt_long");
break;
}
}
}
+ if (prompt && prompt_str) {
+ streams.err.append_format(_(L"%ls: You can't specify both -p and -P\n"), argv[0]);
+ builtin_print_help(parser, streams, argv[0], streams.err);
+ return STATUS_BUILTIN_ERROR;
+ }
+
+ if (prompt_str) {
+ prompt_cmd = L"echo " + escape_string(prompt_str, ESCAPE_ALL);
+ prompt = prompt_cmd.c_str();
+ } else if (!prompt) {
+ prompt = DEFAULT_READ_PROMPT;
+ }
+
if ((place & ENV_UNEXPORT) && (place & ENV_EXPORT)) {
streams.err.append_format(BUILTIN_ERR_EXPUNEXP, argv[0]);
-
builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}