Skip to content

Commit

Permalink
Fix indexing of $history
Browse files Browse the repository at this point in the history
cherry-picked from krader1961/fish-shell commit b69df4fe72

Fixes #4353 (regression in indexing of history contents) and introduces
new unit tests to catch bad $history indexing in the future.
  • Loading branch information
Kurtis Rader authored and mqudsi committed Aug 26, 2017
1 parent 1872fb4 commit 99d2a34
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 37 deletions.
4 changes: 2 additions & 2 deletions src/env.cpp
Expand Up @@ -1364,8 +1364,8 @@ env_var_t env_get(const wcstring &key, env_mode_flags_t mode) {
if (!history) {
history = &history_t::history_with_name(history_session_id());
}
wcstring result;
if (history) history->get_string_representation(&result, ARRAY_SEP_STR);
wcstring_list_t result;
if (history) history->get_history(result);
return env_var_t(L"history", result);
} else if (key == L"status") {
return env_var_t(L"status", to_string(proc_get_last_status()));
Expand Down
10 changes: 5 additions & 5 deletions src/fish_tests.cpp
Expand Up @@ -3079,12 +3079,12 @@ void history_tests_t::test_history_merge(void) {
}

// Everyone should also have items in the same order (#2312)
wcstring string_rep;
hists[0]->get_string_representation(&string_rep, L"\n");
wcstring_list_t hist_vals1;
hists[0]->get_history(hist_vals1);
for (size_t i = 0; i < count; i++) {
wcstring string_rep2;
hists[i]->get_string_representation(&string_rep2, L"\n");
do_test(string_rep == string_rep2);
wcstring_list_t hist_vals2;
hists[i]->get_history(hist_vals2);
do_test(hist_vals1 == hist_vals2);
}

// Add some more per-history items.
Expand Down
21 changes: 4 additions & 17 deletions src/history.cpp
Expand Up @@ -842,15 +842,12 @@ void history_t::set_valid_file_paths(const wcstring_list_t &valid_file_paths,
}
}

void history_t::get_string_representation(wcstring *result, const wcstring &separator) {
void history_t::get_history(wcstring_list_t &result) {
scoped_lock locker(lock);

bool first = true;

std::unordered_set<wcstring> seen;

// If we have a pending item, we skip the first encountered (i.e. last) new item.
bool next_is_pending = this->has_pending_item;
std::unordered_set<wcstring> seen;

// Append new items. Note that in principle we could use const_reverse_iterator, but we do not
// because reverse_iterator is not convertible to const_reverse_iterator. See
Expand All @@ -863,12 +860,7 @@ void history_t::get_string_representation(wcstring *result, const wcstring &sepa
continue;
}

// Skip duplicates.
if (!seen.insert(iter->str()).second) continue;

if (!first) result->append(separator);
result->append(iter->str());
first = false;
if (seen.insert(iter->str()).second) result.push_back(iter->str());
}

// Append old items.
Expand All @@ -879,12 +871,7 @@ void history_t::get_string_representation(wcstring *result, const wcstring &sepa
const history_item_t item =
decode_item(mmap_start + offset, mmap_length - offset, mmap_type);

// Skip duplicates.
if (!seen.insert(item.str()).second) continue;

if (!first) result->append(separator);
result->append(item.str());
first = false;
if (seen.insert(item.str()).second) result.push_back(item.str());
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/history.h
Expand Up @@ -257,9 +257,9 @@ class history_t {
// Incorporates the history of other shells into this history.
void incorporate_external_changes();

// Gets all the history into a string. This is intended for the $history environment variable.
// Gets all the history into a list. This is intended for the $history environment variable.
// This may be long!
void get_string_representation(wcstring *result, const wcstring &separator);
void get_history(wcstring_list_t &result);

// Sets the valid file paths for the history item with the given identifier.
void set_valid_file_paths(const wcstring_list_t &valid_file_paths, history_identifier_t ident);
Expand Down
30 changes: 19 additions & 11 deletions tests/history.expect
Expand Up @@ -30,7 +30,7 @@ expect_prompt
send "echo start1; builtin history; echo end1\r"
expect_prompt -re {start1\r\nend1\r\n} {
puts "empty history detected as expected"
} unmatched {
} timeout {
puts stderr "empty history not detected as expected"
}

Expand All @@ -39,7 +39,7 @@ expect_prompt -re {start1\r\nend1\r\n} {
send "echo start2; builtin history; echo end2\r"
expect_prompt -re {start2\r\necho start1; builtin history; echo end1\r\nend2\r\n} {
puts "first history command detected as expected"
} unmatched {
} timeout {
puts stderr "first history command not detected as expected"
}

Expand All @@ -53,7 +53,7 @@ expect_prompt -re {start2\r\necho start1; builtin history; echo end1\r\nend2\r\n
send "history search echo start\r"
expect_prompt -re {\r\necho start1.*\r\necho start2} {
puts "history function explicit search succeeded"
} unmatched {
} timeout {
puts stderr "history function explicit search failed"
}

Expand All @@ -62,7 +62,7 @@ expect_prompt -re {\r\necho start1.*\r\necho start2} {
send "history -p 'echo start'\r"
expect_prompt -re {\r\necho start2.*\r\necho start1} {
puts "history function implicit search succeeded"
} unmatched {
} timeout {
puts stderr "history function implicit search failed"
}

Expand All @@ -71,7 +71,7 @@ expect_prompt -re {\r\necho start2.*\r\necho start1} {
send "history search --show-time='# %F %T%n' --prefix 'echo start'\r"
expect_prompt -re {\r\n# \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\r\necho start2; .*\r\n# \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\r\necho start1; } {
puts "history function implicit search with timestamps succeeded"
} unmatched {
} timeout {
puts stderr "history function implicit search with timestamps failed"
}

Expand All @@ -90,14 +90,14 @@ expect_prompt
send "history search --exact 'echo goodbye'\r"
expect_prompt -re {\r\necho goodbye\r\n} {
puts "history function explicit exact search 'echo goodbye' succeeded"
} unmatched {
} timeout {
puts stderr "history function explicit exact search 'echo goodbye' failed"
}

send "history search --exact 'echo hello'\r"
expect_prompt -re {\r\necho hello\r\n} {
puts "history function explicit exact search 'echo hello' succeeded"
} unmatched {
} timeout {
puts stderr "history function explicit exact search 'echo hello' failed"
}

Expand All @@ -106,7 +106,7 @@ expect_prompt -re {\r\necho hello\r\n} {
send "history search --exact 'echo hell'\r"
expect_prompt -re {history search --exact 'echo hell'\r\n} {
puts "history function explicit exact search 'echo hell' succeeded"
} unmatched {
} timeout {
puts stderr "history function explicit exact search 'echo hell' failed"
}

Expand All @@ -117,7 +117,7 @@ expect -re {history delete -e -C 'echo hello'\r\n}
send "echo count hello (history search -e -C 'echo hello' | wc -l | string trim)\r"
expect -re {\r\ncount hello 0\r\n} {
puts "history function explicit exact delete 'echo hello' succeeded"
} unmatched {
} timeout {
puts stderr "history function explicit exact delete 'echo hello' failed"
}

Expand All @@ -139,13 +139,21 @@ expect -re {Deleting history entry 1: "echo hello AGAIN"\r\n}
send "echo count AGAIN (history search -e -C 'echo hello AGAIN' | wc -l | string trim)\r"
expect -re {\r\ncount AGAIN 0\r\n} {
puts "history function explicit prefix delete 'echo hello AGAIN' succeeded"
} unmatched {
} timeout {
puts stderr "history function explicit prefix delete 'echo hello AGAIN' failed"
}

send "echo count again (history search -e -C 'echo hello again' | wc -l | string trim)\r"
expect -re {\r\ncount again 1\r\n} {
puts "history function explicit exact search 'echo hello again' succeeded"
} unmatched {
} timeout {
puts stderr "history function explicit exact search 'echo hello again' failed"
}

# Verify that the $history var has the expected content.
send "echo history2=\$history\[2\]\r"
expect -re {\r\nhistory2=echo count AGAIN .*\r\n} {
puts "history\[2\] had the correct data"
} timeout {
puts stderr "history\[2\] had the wrong data"
}
1 change: 1 addition & 0 deletions tests/history.expect.out
Expand Up @@ -9,3 +9,4 @@ history function explicit exact search 'echo hell' succeeded
history function explicit exact delete 'echo hello' succeeded
history function explicit prefix delete 'echo hello AGAIN' succeeded
history function explicit exact search 'echo hello again' succeeded
history[2] had the correct data

0 comments on commit 99d2a34

Please sign in to comment.