New -n option for read builtin

Usage: read -n nchars
Reads maximum of nchars characters. If nchars <= 0, there's no limit.
This commit is contained in:
Joseph Tannhuber 2014-08-19 14:28:08 +02:00 committed by Kevin Ballard
parent 677cee44ad
commit 4acea72700
6 changed files with 103 additions and 11 deletions

View file

@ -2313,6 +2313,8 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
const wchar_t *commandline = L""; const wchar_t *commandline = L"";
int exit_res=STATUS_BUILTIN_OK; int exit_res=STATUS_BUILTIN_OK;
const wchar_t *mode_name = READ_MODE_NAME; const wchar_t *mode_name = READ_MODE_NAME;
int nchars=0;
wchar_t *end;
int shell = 0; int shell = 0;
int array = 0; int array = 0;
@ -2355,6 +2357,10 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
L"mode-name", required_argument, 0, 'm' L"mode-name", required_argument, 0, 'm'
} }
, ,
{
L"nchars", required_argument, 0, 'n'
}
,
{ {
L"shell", no_argument, 0, 's' 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, int opt = wgetopt_long(argc,
argv, argv,
L"xglUup:c:hm:sa", L"xglUup:c:hm:n:sa",
long_options, long_options,
&opt_index); &opt_index);
if (opt == -1) if (opt == -1)
@ -2428,6 +2434,32 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
mode_name = woptarg; mode_name = woptarg;
break; 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': case 's':
shell = 1; shell = 1;
break; break;
@ -2530,12 +2562,25 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
proc_push_interactive(1); proc_push_interactive(1);
event_fire_generic(L"fish_prompt"); event_fire_generic(L"fish_prompt");
line = reader_readline(); line = reader_readline(nchars);
proc_pop_interactive(); proc_pop_interactive();
if (line) if (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); buff = wcsdup(line);
} }
}
else else
{ {
exit_res = STATUS_BUILTIN_ERROR; exit_res = STATUS_BUILTIN_ERROR;
@ -2594,6 +2639,11 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
break; break;
sb.push_back(res); sb.push_back(res);
if (0 < nchars && (size_t)nchars <= sb.size())
{
break;
}
} }
if (sb.size() < 2 && eof) if (sb.size() < 2 && eof)

View file

@ -14,6 +14,7 @@ The following options are available:
- <tt>-g</tt> or <tt>--global</tt> makes the variables global. - <tt>-g</tt> or <tt>--global</tt> makes the variables global.
- <tt>-l</tt> or <tt>--local</tt> makes the variables local. - <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>-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>. - <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>-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). - <code>-u</code> or <code>--unexport</code> prevents the variables from being exported to child processes (default behaviour).

View file

@ -2955,7 +2955,7 @@ static int read_i(void)
during evaluation. during evaluation.
*/ */
const wchar_t *tmp = reader_readline(); const wchar_t *tmp = reader_readline(0);
if (data->end_loop) 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; wint_t c;
int last_char=0; int last_char=0;
@ -3084,6 +3084,13 @@ const wchar_t *reader_readline(void)
while (!finished && !data->end_loop) 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 Sometimes strange input sequences seem to generate a zero
byte. I believe these simply mean a character was pressed 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]; 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)); memset(arr, 0, sizeof(arr));
arr[0] = c; arr[0] = c;
for (i=1; i<READAHEAD_MAX; i++) for (i = 1; i < limit; ++i)
{ {
if (!can_read(0)) if (!can_read(0))
@ -3137,6 +3146,12 @@ const wchar_t *reader_readline(void)
if (c != 0) if (c != 0)
break; 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 */ /* If we get something other than a repaint, then stop coalescing them */

View file

@ -200,11 +200,13 @@ int reader_reading_interrupted();
bool reader_thread_job_is_stale(); bool reader_thread_job_is_stale();
/** /**
Read one line of input. Before calling this function, reader_push() Read one line of input. Before calling this function, reader_push() must have
must have been called in order to set up a valid reader been called in order to set up a valid reader environment. If nchars > 0,
environment. 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. Push a new reader environment.

View file

@ -75,3 +75,19 @@ print_vars ary
echo '' | read -la ary echo '' | read -la ary
print_vars ary print_vars ary
set -le IFS 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

View file

@ -34,3 +34,11 @@ two
5 'h' 'e' 'l' 'l' 'o' 5 'h' 'e' 'l' 'l' 'o'
1 'h' 1 'h'
0 0
# read -n tests
tes
test
test
tes
tin
t