From 4acea72700a7738f1a3258cab226f6153a65ea94 Mon Sep 17 00:00:00 2001 From: Joseph Tannhuber Date: Tue, 19 Aug 2014 14:28:08 +0200 Subject: [PATCH] New -n option for read builtin Usage: read -n nchars Reads maximum of nchars characters. If nchars <= 0, there's no limit. --- builtin.cpp | 56 +++++++++++++++++++++++++++++++++++++++++++++--- doc_src/read.txt | 1 + reader.cpp | 23 ++++++++++++++++---- reader.h | 10 +++++---- tests/read.in | 16 ++++++++++++++ tests/read.out | 8 +++++++ 6 files changed, 103 insertions(+), 11 deletions(-) diff --git a/builtin.cpp b/builtin.cpp index da9429b17..cde4477fb 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; @@ -2355,6 +2357,10 @@ static int builtin_read(parser_t &parser, wchar_t **argv) L"mode-name", required_argument, 0, 'm' } , + { + 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 4f1760a56..48ae9b059 100644 --- a/doc_src/read.txt +++ b/doc_src/read.txt @@ -14,6 +14,7 @@ The following options are available: - -g or --global makes the variables global. - -l or --local makes the variables local. - -m NAME or --mode-name=NAME 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. +- -n NCHARS or --nchars=NCHARS causes \c read to return after reading NCHARS characters rather than waiting for a complete line of input. - -p PROMPT_CMD or --prompt=PROMPT_CMD uses the output of the shell command \c PROMPT_CMD as the prompt for the interactive mode. The default prompt command is set_color green; echo read; set_color normal; echo "> ". - -s or --shell enables syntax highlighting, tab completions and command termination suitable for entering shellscript code in the interactive mode. - -u or --unexport prevents the variables from being exported to child processes (default behaviour). diff --git a/reader.cpp b/reader.cpp index c45e62b23..2a2c08974 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; icommand_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 f89bd50e9..3c82f5d18 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 f8787c5ce..f864287f3 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 1098045dd..0de988314 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