From a26f68d63fb3780a1eb0c092b31b99bc67290933 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 16 Oct 2016 20:33:33 -0700 Subject: [PATCH] handle multiline commands in history search output Fixes #31 --- doc_src/history.txt | 4 +++- share/functions/history.fish | 12 +++++++++--- src/builtin.cpp | 12 +++++++++--- src/history.cpp | 12 +++++++----- src/history.h | 2 +- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/doc_src/history.txt b/doc_src/history.txt index 855a160ee..388ded19a 100644 --- a/doc_src/history.txt +++ b/doc_src/history.txt @@ -2,7 +2,7 @@ \subsection history-synopsis Synopsis \fish{synopsis} -history search [ --show-time ] [ --case-sensitive ] [ --exact | --prefix | --contains ] [ --max=n ] [ "search string"... ] +history search [ --show-time ] [ --case-sensitive ] [ --exact | --prefix | --contains ] [ --max=n ] [ --null ] [ "search string"... ] history delete [ --show-time ] [ --case-sensitive ] [ --exact | --prefix | --contains ] "search string"... history merge history save @@ -42,6 +42,8 @@ These flags can appear before or immediately after one of the sub-commands liste - `-t` or `--show-time` prepends each history entry with the date and time the entry was recorded . By default it uses the strftime format `# %c%n`. You can specify another format; e.g., `--show-time='%Y-%m-%d %H:%M:%S '` or `--show-time='%a%I%p'`. The short option, `-t` doesn't accept a stftime format string; it only uses the default format. Any strftime format is allowed, including `%s` to get the raw UNIX seconds since the epoch. Note that `--with-time` is also allowed but is deprecated and will be removed at a future date. +- `-z` or `--null` causes history entries written by the search operations to be terminated by a NUL character rather than a newline. This allows the output to be processed by `read -z` to correctly handle multiline history entries. + - `-` `-n ` or `--max=` limits the matched history items to the first "n" matching entries. This is only valid for `history search`. - `-h` or `--help` display help for this command. diff --git a/share/functions/history.fish b/share/functions/history.fish index 428ffe38f..6e130fd76 100644 --- a/share/functions/history.fish +++ b/share/functions/history.fish @@ -36,6 +36,7 @@ function history --description "display or manipulate interactive command histor set -l show_time set -l max_count set -l case_sensitive + set -l null # Check for a recognized subcommand as the first argument. if set -q argv[1] @@ -82,6 +83,8 @@ function history --description "display or manipulate interactive command histor set search_mode --contains case -e --exact set search_mode --exact + case -z --null + set null --null case -n --max if string match -- '-n?*' $argv[1] or string match -- '--max=*' $argv[1] @@ -128,9 +131,9 @@ function history --description "display or manipulate interactive command histor set -l pager less set -q PAGER and set pager $PAGER - builtin history search $search_mode $show_time $max_count $case_sensitive -- $argv | eval $pager + builtin history search $search_mode $show_time $max_count $case_sensitive $null -- $argv | eval $pager else - builtin history search $search_mode $show_time $max_count $case_sensitive -- $argv + builtin history search $search_mode $show_time $max_count $case_sensitive $null -- $argv end case delete # interactively delete history @@ -150,7 +153,10 @@ function history --description "display or manipulate interactive command histor # TODO: Fix this so that requesting history entries with a timestamp works: # set -l found_items (builtin history search $search_mode $show_time -- $argv) - set -l found_items (builtin history search $search_mode $case_sensitive -- $argv) + set -l found_items + builtin history search $search_mode $case_sensitive --null -- $argv | while read -lz x + set found_items $found_items $x + end if set -q found_items[1] set -l found_items_count (count $found_items) for i in (seq $found_items_count) diff --git a/src/builtin.cpp b/src/builtin.cpp index f23de8cca..06944591b 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -2853,7 +2853,7 @@ static bool set_hist_cmd(wchar_t *const cmd, hist_cmd_t *hist_cmd, hist_cmd_t su } #define CHECK_FOR_UNEXPECTED_HIST_ARGS(hist_cmd) \ - if (history_search_type_defined || show_time_format) { \ + if (history_search_type_defined || show_time_format || null_terminate) { \ streams.err.append_format(_(L"%ls: you cannot use any options with the %ls command\n"), \ cmd, hist_cmd_to_string(hist_cmd).c_str()); \ status = STATUS_BUILTIN_ERROR; \ @@ -2876,10 +2876,11 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar bool history_search_type_defined = false; const wchar_t *show_time_format = NULL; bool case_sensitive = false; + bool null_terminate = false; // TODO: Remove the long options that correspond to subcommands (e.g., '--delete') on or after // 2017-10 (which will be a full year after these flags have been deprecated). - const wchar_t *short_options = L":Cmn:epcht"; + const wchar_t *short_options = L":Cmn:epchtz"; const struct woption long_options[] = {{L"prefix", no_argument, NULL, 'p'}, {L"contains", no_argument, NULL, 'c'}, {L"help", no_argument, NULL, 'h'}, @@ -2887,6 +2888,7 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar {L"with-time", optional_argument, NULL, 't'}, {L"exact", no_argument, NULL, 'e'}, {L"max", required_argument, NULL, 'n'}, + {L"null", no_argument, 0, 'z'}, {L"case-sensitive", no_argument, 0, 'C'}, {L"delete", no_argument, NULL, 1}, {L"search", no_argument, NULL, 2}, @@ -2967,6 +2969,10 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar } break; } + case 'z': { + null_terminate = true; + break; + } case 'h': { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_BUILTIN_OK; @@ -3021,7 +3027,7 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar switch (hist_cmd) { case HIST_SEARCH: { if (!history->search(search_type, args, show_time_format, max_items, case_sensitive, - streams)) { + null_terminate, streams)) { status = STATUS_BUILTIN_ERROR; } break; diff --git a/src/history.cpp b/src/history.cpp index 82f41b26b..7c33166ef 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -1436,7 +1436,7 @@ void history_t::save(void) { // Formats a single history record, including a trailing newline. Returns true // if bytes were written to the output stream and false otherwise. static bool format_history_record(const history_item_t &item, const wchar_t *show_time_format, - io_streams_t &streams) { + bool null_terminate, io_streams_t &streams) { if (show_time_format) { const time_t seconds = item.timestamp(); struct tm timestamp; @@ -1449,18 +1449,19 @@ static bool format_history_record(const history_item_t &item, const wchar_t *sho streams.out.append(timestamp_string); } streams.out.append(item.str()); - streams.out.append(L"\n"); + streams.out.append(null_terminate ? L'\0' : L'\n'); return true; } bool history_t::search(history_search_type_t search_type, wcstring_list_t search_args, const wchar_t *show_time_format, long max_items, bool case_sensitive, - io_streams_t &streams) { + bool null_terminate, io_streams_t &streams) { // scoped_lock locker(lock); if (search_args.empty()) { // Start at one because zero is the current command. for (int i = 1; !this->item_at_index(i).empty() && max_items; ++i, --max_items) { - if (!format_history_record(this->item_at_index(i), show_time_format, streams)) { + if (!format_history_record(this->item_at_index(i), show_time_format, null_terminate, + streams)) { return false; } } @@ -1477,7 +1478,8 @@ bool history_t::search(history_search_type_t search_type, wcstring_list_t search history_search_t searcher = history_search_t(*this, search_string, search_type, case_sensitive); while (searcher.go_backwards()) { - if (!format_history_record(searcher.current_item(), show_time_format, streams)) { + if (!format_history_record(searcher.current_item(), show_time_format, null_terminate, + streams)) { return false; } if (--max_items == 0) return true; diff --git a/src/history.h b/src/history.h index bd990c196..a8aa51575 100644 --- a/src/history.h +++ b/src/history.h @@ -230,7 +230,7 @@ class history_t { // Searches history. bool search(history_search_type_t search_type, wcstring_list_t search_args, const wchar_t *show_time_format, long max_items, bool case_sensitive, - io_streams_t &streams); + bool null_terminate, io_streams_t &streams); // Enable / disable automatic saving. Main thread only! void disable_automatic_saving();