aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--builtin.cpp56
-rw-r--r--doc_src/read.txt1
-rw-r--r--reader.cpp23
-rw-r--r--reader.h10
-rw-r--r--tests/read.in16
-rw-r--r--tests/read.out8
6 files changed, 103 insertions, 11 deletions
diff --git a/builtin.cpp b/builtin.cpp
index da9429b1..cde4477f 100644
--- a/builtin.cpp
+++ b/builtin.cpp
@@ -2313,6 +2313,8 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
const wchar_t *commandline = L"";
int exit_res=STATUS_BUILTIN_OK;
const wchar_t *mode_name = READ_MODE_NAME;
+ int nchars=0;
+ wchar_t *end;
int shell = 0;
int array = 0;
@@ -2356,6 +2358,10 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
}
,
{
+ L"nchars", required_argument, 0, 'n'
+ }
+ ,
+ {
L"shell", no_argument, 0, 's'
}
,
@@ -2377,7 +2383,7 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
int opt = wgetopt_long(argc,
argv,
- L"xglUup:c:hm:sa",
+ L"xglUup:c:hm:n:sa",
long_options,
&opt_index);
if (opt == -1)
@@ -2428,6 +2434,32 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
mode_name = woptarg;
break;
+ case L'n':
+ errno = 0;
+ nchars = fish_wcstoi(woptarg, &end, 10);
+ if (errno || *end != 0)
+ {
+ switch (errno)
+ {
+ case ERANGE:
+ append_format(stderr_buffer,
+ _(L"%ls: Argument '%ls' is out of range\n"),
+ argv[0],
+ woptarg);
+ builtin_print_help(parser, argv[0], stderr_buffer);
+ return STATUS_BUILTIN_ERROR;
+
+ default:
+ append_format(stderr_buffer,
+ _(L"%ls: Argument '%ls' must be an integer\n"),
+ argv[0],
+ woptarg);
+ builtin_print_help(parser, argv[0], stderr_buffer);
+ return STATUS_BUILTIN_ERROR;
+ }
+ }
+ break;
+
case 's':
shell = 1;
break;
@@ -2530,11 +2562,24 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
proc_push_interactive(1);
event_fire_generic(L"fish_prompt");
- line = reader_readline();
+ line = reader_readline(nchars);
proc_pop_interactive();
if (line)
{
- buff = wcsdup(line);
+ if (0 < nchars && nchars < wcslen(line))
+ {
+ // line may be longer than nchars if a keybinding used `commandline -i`
+ // note: we're deliberately throwing away the tail of the commandline.
+ // It shouldn't be unread because it was produced with `commandline -i`,
+ // not typed.
+ buff = (wchar_t *)malloc(((size_t)nchars + 1) * sizeof(wchar_t));
+ wmemcpy(buff, line, (size_t)nchars);
+ buff[nchars] = 0;
+ }
+ else
+ {
+ buff = wcsdup(line);
+ }
}
else
{
@@ -2594,6 +2639,11 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
break;
sb.push_back(res);
+
+ if (0 < nchars && (size_t)nchars <= sb.size())
+ {
+ break;
+ }
}
if (sb.size() < 2 && eof)
diff --git a/doc_src/read.txt b/doc_src/read.txt
index 4f1760a5..48ae9b05 100644
--- a/doc_src/read.txt
+++ b/doc_src/read.txt
@@ -14,6 +14,7 @@ The following options are available:
- <tt>-g</tt> or <tt>--global</tt> makes the variables global.
- <tt>-l</tt> or <tt>--local</tt> makes the variables local.
- <tt>-m NAME</tt> or <tt>--mode-name=NAME</tt> specifies that the name NAME should be used to save/load the history file. If NAME is fish, the regular fish history will be available.
+- <tt>-n NCHARS</tt> or <tt>--nchars=NCHARS</tt> causes \c read to return after reading NCHARS characters rather than waiting for a complete line of input.
- <tt>-p PROMPT_CMD</tt> or <tt>--prompt=PROMPT_CMD</tt> uses the output of the shell command \c PROMPT_CMD as the prompt for the interactive mode. The default prompt command is <tt>set_color green; echo read; set_color normal; echo "> "</tt>.
- <code>-s</code> or <code>--shell</code> enables syntax highlighting, tab completions and command termination suitable for entering shellscript code in the interactive mode.
- <code>-u</code> or <code>--unexport</code> prevents the variables from being exported to child processes (default behaviour).
diff --git a/reader.cpp b/reader.cpp
index c45e62b2..2a2c0897 100644
--- a/reader.cpp
+++ b/reader.cpp
@@ -2955,7 +2955,7 @@ static int read_i(void)
during evaluation.
*/
- const wchar_t *tmp = reader_readline();
+ const wchar_t *tmp = reader_readline(0);
if (data->end_loop)
{
@@ -3044,7 +3044,7 @@ static wchar_t unescaped_quote(const wcstring &str, size_t pos)
}
-const wchar_t *reader_readline(void)
+const wchar_t *reader_readline(int nchars)
{
wint_t c;
int last_char=0;
@@ -3084,6 +3084,13 @@ const wchar_t *reader_readline(void)
while (!finished && !data->end_loop)
{
+ if (0 < nchars && (size_t)nchars <= data->command_line.size())
+ {
+ // we've already hit the specified character limit
+ finished = 1;
+ break;
+ }
+
/*
Sometimes strange input sequences seem to generate a zero
byte. I believe these simply mean a character was pressed
@@ -3104,12 +3111,14 @@ const wchar_t *reader_readline(void)
{
wchar_t arr[READAHEAD_MAX+1];
- int i;
+ size_t i;
+ size_t limit = 0 < nchars ? std::min((size_t)nchars - data->command_line.size(), (size_t)READAHEAD_MAX)
+ : READAHEAD_MAX;
memset(arr, 0, sizeof(arr));
arr[0] = c;
- for (i=1; i<READAHEAD_MAX; i++)
+ for (i = 1; i < limit; ++i)
{
if (!can_read(0))
@@ -3137,6 +3146,12 @@ const wchar_t *reader_readline(void)
if (c != 0)
break;
+
+ if (0 < nchars && (size_t)nchars <= data->command_line.size())
+ {
+ c = R_NULL;
+ break;
+ }
}
/* If we get something other than a repaint, then stop coalescing them */
diff --git a/reader.h b/reader.h
index f89bd50e..3c82f5d1 100644
--- a/reader.h
+++ b/reader.h
@@ -200,11 +200,13 @@ int reader_reading_interrupted();
bool reader_thread_job_is_stale();
/**
- Read one line of input. Before calling this function, reader_push()
- must have been called in order to set up a valid reader
- environment.
+ Read one line of input. Before calling this function, reader_push() must have
+ been called in order to set up a valid reader environment. If nchars > 0,
+ return after reading that many characters even if a full line has not yet
+ been read. Note: the returned value may be longer than nchars if a single
+ keypress resulted in multiple characters being inserted into the commandline.
*/
-const wchar_t *reader_readline();
+const wchar_t *reader_readline(int nchars);
/**
Push a new reader environment.
diff --git a/tests/read.in b/tests/read.in
index f8787c5c..f864287f 100644
--- a/tests/read.in
+++ b/tests/read.in
@@ -75,3 +75,19 @@ print_vars ary
echo '' | read -la ary
print_vars ary
set -le IFS
+
+# read -n tests
+
+echo
+echo '# read -n tests'
+echo 'testing' | read -n 3 foo
+echo $foo
+echo 'test' | read -n 10 foo
+echo $foo
+echo 'test' | read -n 0 foo
+echo $foo
+echo 'testing' | begin; read -n 3 foo; read -n 3 bar; end
+echo $foo
+echo $bar
+echo 'test' | read -n 1 foo
+echo $foo
diff --git a/tests/read.out b/tests/read.out
index 1098045d..0de98831 100644
--- a/tests/read.out
+++ b/tests/read.out
@@ -34,3 +34,11 @@ two
5 'h' 'e' 'l' 'l' 'o'
1 'h'
0
+
+# read -n tests
+tes
+test
+test
+tes
+tin
+t