From 821388549135abbd6ff1dce66601da29df12c1d4 Mon Sep 17 00:00:00 2001 From: Marc Garcia Sastre Date: Sun, 9 Apr 2017 20:48:21 +0200 Subject: [PATCH] Capture read command contents without displaying it via a silent flag. Implement a `read --silent` flag. This echos the input using an obfuscation character. --- src/builtin.cpp | 19 +++++++++++++------ src/common.cpp | 4 ++++ src/common.h | 3 +++ src/reader.cpp | 38 ++++++++++++++++++++++++-------------- src/reader.h | 2 ++ tests/read.expect | 9 +++++++++ tests/read.expect.out | 1 + 7 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/builtin.cpp b/src/builtin.cpp index fc5cac404..b9ccece2f 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -1976,9 +1976,9 @@ static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **arg /// Read from the tty. This is only valid when the stream is stdin and it is attached to a tty and /// we weren't asked to split on null characters. -static int read_interactive(wcstring &buff, int nchars, bool shell, const wchar_t *mode_name, - const wchar_t *prompt, const wchar_t *right_prompt, - const wchar_t *commandline) { +static int read_interactive(wcstring &buff, int nchars, bool shell, bool silent, + const wchar_t *mode_name, const wchar_t *prompt, + const wchar_t *right_prompt, const wchar_t *commandline) { int exit_res = STATUS_BUILTIN_OK; const wchar_t *line; @@ -1994,6 +1994,7 @@ static int read_interactive(wcstring &buff, int nchars, bool shell, const wchar_ reader_set_allow_autosuggesting(false); reader_set_expand_abbreviations(false); reader_set_exit_on_interrupt(true); + reader_set_silent_status(silent); reader_set_buffer(commandline, wcslen(commandline)); proc_push_interactive(1); @@ -2137,9 +2138,10 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) int nchars = 0; bool shell = false; bool array = false; + bool silent = false; bool split_null = false; - const wchar_t *short_options = L"ac:ghlm:n:p:suxzP:UR:"; + const wchar_t *short_options = L"ac:ghilm: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'}, @@ -2150,6 +2152,7 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) {L"right-prompt", required_argument, NULL, 'R'}, {L"command", required_argument, NULL, 'c'}, {L"mode-name", required_argument, NULL, 'm'}, + {L"silent", no_argument, NULL, 'i'}, {L"nchars", required_argument, NULL, 'n'}, {L"shell", no_argument, NULL, 's'}, {L"array", no_argument, NULL, 'a'}, @@ -2226,6 +2229,10 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) array = true; break; } + case L'i': { + silent = true; + break; + } case L'z': { split_null = true; break; @@ -2302,8 +2309,8 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) // We should read interactively using reader_readline(). This does not support splitting on // null. The call to reader_readline may change woptind, so we save and restore it. int saved_woptind = w.woptind; - exit_res = - read_interactive(buff, nchars, shell, mode_name, prompt, right_prompt, commandline); + exit_res = read_interactive(buff, nchars, shell, silent, mode_name, prompt, right_prompt, + commandline); w.woptind = saved_woptind; } else if (!nchars && !stream_stdin_is_a_tty && lseek(streams.stdin_fd, 0, SEEK_CUR) != -1) { exit_res = read_in_chunks(streams.stdin_fd, buff, split_null); diff --git a/src/common.cpp b/src/common.cpp index 3b5c2e55d..76008f5c4 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -50,6 +50,7 @@ static bool thread_asserts_cfg_for_testing = false; wchar_t ellipsis_char; wchar_t omitted_newline_char; +wchar_t obfuscation_read_char; bool g_profiling_active = false; const wchar_t *program_name; int debug_level = 1; // default maximum debug output level (errors and warnings) @@ -457,6 +458,9 @@ void fish_setlocale() { // Use the Unicode "return" symbol if it can be encoded using the current locale. omitted_newline_char = can_be_encoded(L'\x23CE') ? L'\x23CE' : L'~'; + + // solid circle unicode character if it is able, fallback to the hash character + obfuscation_read_char = can_be_encoded(L'\u25cf') ? L'\u25cf' : L'#'; } long read_blocked(int fd, void *buf, size_t count) { diff --git a/src/common.h b/src/common.h index 9b5c2b9d6..cbdfa05f5 100644 --- a/src/common.h +++ b/src/common.h @@ -159,6 +159,9 @@ extern wchar_t ellipsis_char; /// Character representing an omitted newline at the end of text. extern wchar_t omitted_newline_char; +/// Character used for the silent mode of the read command +extern wchar_t obfuscation_read_char; + /// The verbosity level of fish. If a call to debug has a severity level higher than \c debug_level, /// it will not be printed. extern int debug_level; diff --git a/src/reader.cpp b/src/reader.cpp index 05ee6ef9b..5447a3335 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -168,6 +168,8 @@ class reader_data_t { bool suppress_autosuggestion; /// Whether abbreviations are expanded. bool expand_abbreviations; + /// Silent mode used for password input on the read command + bool silent; /// The representation of the current screen contents. screen_t screen; /// The history. @@ -256,13 +258,14 @@ class reader_data_t { /// Constructor reader_data_t() - : allow_autosuggestion(0), - suppress_autosuggestion(0), - expand_abbreviations(0), + : allow_autosuggestion(false), + suppress_autosuggestion(false), + expand_abbreviations(false), + silent(false), history(0), token_history_pos(0), search_pos(0), - sel_active(0), + sel_active(false), sel_begin_pos(0), sel_start_pos(0), sel_stop_pos(0), @@ -270,13 +273,13 @@ class reader_data_t { complete_func(0), highlight_function(0), test_func(0), - end_loop(0), - prev_end_loop(0), + end_loop(false), + prev_end_loop(false), next(0), search_mode(0), - repaint_needed(0), - screen_reset_needed(0), - exit_on_interrupt(0) {} + repaint_needed(false), + screen_reset_needed(false), + exit_on_interrupt(false) {} }; /// Sets the command line contents, without clearing the pager. @@ -412,8 +415,13 @@ static void reader_repaint() { // Update the indentation. data->indents = parse_util_compute_indents(cmd_line->text); - // Combine the command and autosuggestion into one string. - wcstring full_line = combine_command_and_autosuggestion(cmd_line->text, data->autosuggestion); + wcstring full_line; + if (data->silent) { + full_line = wcstring(cmd_line->text.length(), obfuscation_read_char); + } else { + // Combine the command and autosuggestion into one string. + full_line = combine_command_and_autosuggestion(cmd_line->text, data->autosuggestion); + } size_t len = full_line.size(); if (len < 1) len = 1; @@ -439,9 +447,9 @@ static void reader_repaint() { bool focused_on_pager = data->active_edit_line() == &data->pager.search_field_line; size_t cursor_position = focused_on_pager ? data->pager.cursor_position() : cmd_line->position; - s_write(&data->screen, data->left_prompt_buff, data->right_prompt_buff, full_line, - cmd_line->size(), &colors[0], &indents[0], cursor_position, - data->current_page_rendering, focused_on_pager); + s_write(&data->screen, data->left_prompt_buff, data->right_prompt_buff, full_line, cmd_line->size(), + &colors[0], &indents[0], cursor_position, data->current_page_rendering, + focused_on_pager); data->repaint_needed = false; } @@ -2044,6 +2052,8 @@ void reader_set_test_function(parser_test_error_bits_t (*f)(const wchar_t *)) { void reader_set_exit_on_interrupt(bool i) { data->exit_on_interrupt = i; } +void reader_set_silent_status(bool flag) { data->silent = flag; } + void reader_import_history_if_necessary(void) { // Import history from older location (config path) if our current history is empty. if (data->history && data->history->is_empty()) { diff --git a/src/reader.h b/src/reader.h index 72680bd87..c773ea1ed 100644 --- a/src/reader.h +++ b/src/reader.h @@ -189,6 +189,8 @@ void reader_set_expand_abbreviations(bool flag); /// Sets whether the reader should exit on ^C. void reader_set_exit_on_interrupt(bool flag); +void reader_set_silent_status(bool f); + /// Returns true if the shell is exiting, 0 otherwise. bool shell_is_exiting(); diff --git a/tests/read.expect b/tests/read.expect index 45594c21e..d26153ea5 100644 --- a/tests/read.expect +++ b/tests/read.expect @@ -36,6 +36,15 @@ expect_prompt expect_marker 2 print_var_contents foo +# read -i + +send_line "read -i foo" +expect_read_prompt +send_line -h "read_i\r_marker 3" +expect_prompt +expect_marker 3 +print_var_contents foo + # read -n send_line "read -n 3 foo" diff --git a/tests/read.expect.out b/tests/read.expect.out index 2d02931f6..691851b9f 100644 --- a/tests/read.expect.out +++ b/tests/read.expect.out @@ -1,6 +1,7 @@ $foo: 'text' $foo: 'again' $foo: 'bar' +$foo: 'read_i' $foo: '123' $foo: '456' $foo: 'hello'