2016-04-18 13:47:37 +08:00
|
|
|
// Prototypes for history functions, part of the user interface.
|
2005-10-04 23:11:39 +08:00
|
|
|
#ifndef FISH_HISTORY_H
|
|
|
|
#define FISH_HISTORY_H
|
|
|
|
|
2016-04-21 14:00:54 +08:00
|
|
|
// IWYU pragma: no_include <cstddef>
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <pthread.h>
|
2017-02-13 12:24:22 +08:00
|
|
|
#include <stddef.h>
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <time.h>
|
2016-11-15 13:31:51 +08:00
|
|
|
#include <wctype.h>
|
2017-02-14 12:37:27 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
#include <deque>
|
2016-05-04 07:23:30 +08:00
|
|
|
#include <memory>
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <string>
|
2018-01-31 10:30:30 +08:00
|
|
|
#include <unordered_map>
|
|
|
|
#include <unordered_set>
|
2016-04-18 13:47:37 +08:00
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
2016-04-21 14:00:54 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
#include "common.h"
|
2016-04-21 14:00:54 +08:00
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2012-02-06 08:42:24 +08:00
|
|
|
|
2016-07-14 13:33:50 +08:00
|
|
|
struct io_streams_t;
|
2020-02-09 04:23:39 +08:00
|
|
|
class env_stack_t;
|
2018-09-19 00:18:47 +08:00
|
|
|
class environment_t;
|
2016-07-14 13:33:50 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Fish supports multiple shells writing to history at once. Here is its strategy:
|
|
|
|
//
|
|
|
|
// 1. All history files are append-only. Data, once written, is never modified.
|
|
|
|
//
|
|
|
|
// 2. A history file may be re-written ("vacuumed"). This involves reading in the file and writing a
|
|
|
|
// new one, while performing maintenance tasks: discarding items in an LRU fashion until we reach
|
|
|
|
// the desired maximum count, removing duplicates, and sorting them by timestamp (eventually, not
|
|
|
|
// implemented yet). The new file is atomically moved into place via rename().
|
|
|
|
//
|
|
|
|
// 3. History files are mapped in via mmap(). Before the file is mapped, the file takes a fcntl read
|
|
|
|
// lock. The purpose of this lock is to avoid seeing a transient state where partial data has been
|
|
|
|
// written to the file.
|
|
|
|
//
|
|
|
|
// 4. History is appended to under a fcntl write lock.
|
|
|
|
//
|
|
|
|
// 5. The chaos_mode boolean can be set to true to do things like lower buffer sizes which can
|
|
|
|
// trigger race conditions. This is useful for testing.
|
2012-12-03 17:53:52 +08:00
|
|
|
|
2012-07-25 13:31:31 +08:00
|
|
|
typedef std::vector<wcstring> path_list_t;
|
2012-02-16 03:33:41 +08:00
|
|
|
|
2019-11-03 09:56:16 +08:00
|
|
|
enum class history_search_type_t {
|
2016-07-30 12:24:26 +08:00
|
|
|
// Search for commands exactly matching the given string.
|
2019-11-03 09:56:16 +08:00
|
|
|
exact,
|
2016-07-30 12:24:26 +08:00
|
|
|
// Search for commands containing the given string.
|
2019-11-03 09:56:16 +08:00
|
|
|
contains,
|
2016-07-30 12:24:26 +08:00
|
|
|
// Search for commands starting with the given string.
|
2019-11-03 09:56:16 +08:00
|
|
|
prefix,
|
2017-09-16 04:43:45 +08:00
|
|
|
// Search for commands containing the given glob pattern.
|
2019-11-03 09:56:16 +08:00
|
|
|
contains_glob,
|
2017-09-16 04:43:45 +08:00
|
|
|
// Search for commands starting with the given glob pattern.
|
2019-11-03 09:56:16 +08:00
|
|
|
prefix_glob,
|
2019-11-03 10:33:45 +08:00
|
|
|
// Matches everything.
|
|
|
|
match_everything,
|
2012-02-07 02:52:13 +08:00
|
|
|
};
|
|
|
|
|
2019-04-29 09:13:55 +08:00
|
|
|
typedef uint64_t history_identifier_t;
|
2014-03-29 14:22:03 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
class history_item_t {
|
2012-02-06 08:42:24 +08:00
|
|
|
friend class history_t;
|
2019-06-15 08:18:43 +08:00
|
|
|
friend struct history_impl_t;
|
2019-08-12 05:26:22 +08:00
|
|
|
friend class history_lru_cache_t;
|
2012-02-16 16:24:27 +08:00
|
|
|
friend class history_tests_t;
|
2012-02-06 08:42:24 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
private:
|
|
|
|
// Attempts to merge two compatible history items together.
|
2012-03-02 06:56:34 +08:00
|
|
|
bool merge(const history_item_t &item);
|
2012-02-06 08:42:24 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// The actual contents of the entry.
|
2019-08-12 04:41:04 +08:00
|
|
|
wcstring contents;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Original creation time for the entry.
|
2012-11-19 08:30:30 +08:00
|
|
|
time_t creation_timestamp;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Sometimes unique identifier used for hinting.
|
2014-03-29 14:22:03 +08:00
|
|
|
history_identifier_t identifier;
|
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Paths that we require to be valid for this item to be autosuggested.
|
2012-02-16 03:33:41 +08:00
|
|
|
path_list_t required_paths;
|
2015-09-19 11:47:38 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
public:
|
2019-08-26 04:37:06 +08:00
|
|
|
explicit history_item_t(wcstring str = wcstring(), time_t when = 0,
|
2019-08-18 11:01:44 +08:00
|
|
|
history_identifier_t ident = 0);
|
2016-05-06 06:09:31 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
const wcstring &str() const { return contents; }
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
bool empty() const { return contents.empty(); }
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Whether our contents matches a search term.
|
2016-09-24 11:12:15 +08:00
|
|
|
bool matches_search(const wcstring &term, enum history_search_type_t type,
|
|
|
|
bool case_sensitive) const;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
time_t timestamp() const { return creation_timestamp; }
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
const path_list_t &get_required_paths() const { return required_paths; }
|
2017-05-05 13:42:42 +08:00
|
|
|
void set_required_paths(const path_list_t &paths) { required_paths = paths; }
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
bool operator==(const history_item_t &other) const {
|
|
|
|
return contents == other.contents && creation_timestamp == other.creation_timestamp &&
|
2012-02-16 16:24:27 +08:00
|
|
|
required_paths == other.required_paths;
|
|
|
|
}
|
2012-06-16 07:22:37 +08:00
|
|
|
};
|
2012-06-05 12:24:42 +08:00
|
|
|
|
2014-03-29 14:22:03 +08:00
|
|
|
typedef std::deque<history_item_t> history_item_list_t;
|
|
|
|
|
2018-07-15 05:29:19 +08:00
|
|
|
class history_file_contents_t;
|
2019-06-15 08:18:43 +08:00
|
|
|
struct history_impl_t;
|
2012-02-06 08:42:24 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
class history_t {
|
2012-02-16 16:24:27 +08:00
|
|
|
friend class history_tests_t;
|
2019-06-15 08:18:43 +08:00
|
|
|
const std::unique_ptr<owning_lock<history_impl_t>> impl_;
|
2012-02-06 08:42:24 +08:00
|
|
|
|
2018-07-15 05:29:19 +08:00
|
|
|
// No copying or moving.
|
|
|
|
history_t() = delete;
|
|
|
|
history_t(const history_t &) = delete;
|
|
|
|
history_t(history_t &&) = delete;
|
|
|
|
history_t &operator=(const history_t &) = delete;
|
|
|
|
history_t &operator=(history_t &&) = delete;
|
2016-04-18 13:47:37 +08:00
|
|
|
|
2019-06-15 08:18:43 +08:00
|
|
|
acquired_lock<history_impl_t> impl();
|
|
|
|
acquired_lock<const history_impl_t> impl() const;
|
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Privately add an item. If pending, the item will not be returned by history searches until a
|
|
|
|
// call to resolve_pending.
|
2015-04-20 17:04:17 +08:00
|
|
|
void add(const history_item_t &item, bool pending = false);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
public:
|
2018-07-15 05:29:19 +08:00
|
|
|
explicit history_t(wcstring name);
|
|
|
|
~history_t();
|
2016-03-19 06:14:16 +08:00
|
|
|
|
2018-07-16 02:50:02 +08:00
|
|
|
// Whether we're in maximum chaos mode, useful for testing.
|
|
|
|
// This causes things like locks to fail.
|
|
|
|
static bool chaos_mode;
|
|
|
|
|
|
|
|
// Whether to force the read path instead of mmap. This is useful for testing.
|
|
|
|
static bool never_mmap;
|
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Returns history with the given name, creating it if necessary.
|
|
|
|
static history_t &history_with_name(const wcstring &name);
|
2016-03-19 06:14:16 +08:00
|
|
|
|
2018-09-19 00:18:47 +08:00
|
|
|
/// Returns whether this is using the default name.
|
|
|
|
bool is_default() const;
|
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Determines whether the history is empty. Unfortunately this cannot be const, since it may
|
|
|
|
// require populating the history.
|
2018-07-15 02:47:34 +08:00
|
|
|
bool is_empty();
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Add a new history item to the end. If pending is set, the item will not be returned by
|
|
|
|
// item_at_index until a call to resolve_pending(). Pending items are tracked with an offset
|
|
|
|
// into the array of new items, so adding a non-pending item has the effect of resolving all
|
|
|
|
// pending items.
|
2015-04-20 17:04:17 +08:00
|
|
|
void add(const wcstring &str, history_identifier_t ident = 0, bool pending = false);
|
2012-06-05 12:24:42 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Remove a history item.
|
2012-06-05 12:24:42 +08:00
|
|
|
void remove(const wcstring &str);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Add a new pending history item to the end, and then begin file detection on the items to
|
|
|
|
// determine which arguments are paths
|
2018-09-11 13:29:52 +08:00
|
|
|
void add_pending_with_file_detection(const wcstring &str, const wcstring &working_dir_slash);
|
2015-09-19 11:47:38 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Resolves any pending history items, so that they may be returned in history searches.
|
2015-04-20 17:04:17 +08:00
|
|
|
void resolve_pending();
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Saves history.
|
2012-02-06 08:42:24 +08:00
|
|
|
void save();
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2016-07-14 13:33:50 +08:00
|
|
|
// Searches history.
|
2019-06-15 08:18:43 +08:00
|
|
|
bool search(history_search_type_t search_type, const wcstring_list_t &search_args,
|
2017-09-15 06:44:17 +08:00
|
|
|
const wchar_t *show_time_format, size_t max_items, bool case_sensitive,
|
2020-01-17 07:11:24 +08:00
|
|
|
bool null_terminate, bool reverse, const cancel_checker_t &cancel_check,
|
|
|
|
io_streams_t &streams);
|
2018-08-12 15:30:57 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Irreversibly clears history.
|
2012-02-16 16:24:27 +08:00
|
|
|
void clear();
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2019-06-15 08:18:43 +08:00
|
|
|
// Populates from older location (in config path, rather than data path).
|
2015-09-19 11:47:38 +08:00
|
|
|
void populate_from_config_path();
|
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Populates from a bash history file.
|
2012-06-16 07:22:37 +08:00
|
|
|
void populate_from_bash(FILE *f);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Incorporates the history of other shells into this history.
|
2014-07-26 01:08:21 +08:00
|
|
|
void incorporate_external_changes();
|
|
|
|
|
2017-08-25 09:59:50 +08:00
|
|
|
// Gets all the history into a list. This is intended for the $history environment variable.
|
2017-07-09 04:14:30 +08:00
|
|
|
// This may be long!
|
2017-08-25 09:59:50 +08:00
|
|
|
void get_history(wcstring_list_t &result);
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2018-01-31 10:30:30 +08:00
|
|
|
// Let indexes be a list of one-based indexes into the history, matching the interpretation of
|
|
|
|
// $history. That is, $history[1] is the most recently executed command. Values less than one
|
|
|
|
// are skipped. Return a mapping from index to history item text.
|
|
|
|
std::unordered_map<long, wcstring> items_at_indexes(const std::vector<long> &idxs);
|
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Return the specified history at the specified index. 0 is the index of the current
|
|
|
|
// commandline. (So the most recent item is at index 1.)
|
2012-02-06 08:42:24 +08:00
|
|
|
history_item_t item_at_index(size_t idx);
|
2017-09-15 06:44:17 +08:00
|
|
|
|
|
|
|
// Return the number of history entries.
|
|
|
|
size_t size();
|
2012-02-06 08:42:24 +08:00
|
|
|
};
|
|
|
|
|
2018-08-12 15:30:57 +08:00
|
|
|
/// Flags for history searching.
|
|
|
|
enum {
|
|
|
|
// If set, ignore case.
|
|
|
|
history_search_ignore_case = 1 << 0,
|
|
|
|
|
|
|
|
// If set, do not deduplicate, which can help performance.
|
|
|
|
history_search_no_dedup = 1 << 1
|
|
|
|
};
|
|
|
|
using history_search_flags_t = uint32_t;
|
|
|
|
|
|
|
|
/// Support for searching a history backwards.
|
|
|
|
/// Note this does NOT de-duplicate; it is the caller's responsibility to do so.
|
2016-04-18 13:47:37 +08:00
|
|
|
class history_search_t {
|
2016-07-14 13:33:50 +08:00
|
|
|
private:
|
2016-04-18 13:47:37 +08:00
|
|
|
// The history in which we are searching.
|
2018-08-12 15:30:57 +08:00
|
|
|
history_t *history_;
|
|
|
|
|
|
|
|
// The original search term.
|
|
|
|
wcstring orig_term_;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2018-08-12 15:30:57 +08:00
|
|
|
// The (possibly lowercased) search term.
|
|
|
|
wcstring canon_term_;
|
2016-10-17 12:15:40 +08:00
|
|
|
|
|
|
|
// Our search type.
|
2019-11-03 09:56:16 +08:00
|
|
|
enum history_search_type_t search_type_ { history_search_type_t::contains };
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2018-08-12 15:30:57 +08:00
|
|
|
// Our flags.
|
|
|
|
history_search_flags_t flags_{0};
|
2012-02-06 14:30:42 +08:00
|
|
|
|
2018-08-12 15:30:57 +08:00
|
|
|
// The current history item.
|
|
|
|
maybe_t<history_item_t> current_item_;
|
2012-02-06 08:42:24 +08:00
|
|
|
|
2018-08-12 15:30:57 +08:00
|
|
|
// Index of the current history item.
|
|
|
|
size_t current_index_{0};
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2018-08-12 15:30:57 +08:00
|
|
|
// If deduping, the items we've seen.
|
|
|
|
std::unordered_set<wcstring> deduper_;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2018-08-12 15:30:57 +08:00
|
|
|
// return whether we are case insensitive.
|
|
|
|
bool ignores_case() const { return flags_ & history_search_ignore_case; }
|
2012-02-06 08:42:24 +08:00
|
|
|
|
2018-08-12 15:30:57 +08:00
|
|
|
// return whether we deduplicate items.
|
|
|
|
bool dedup() const { return !(flags_ & history_search_no_dedup); }
|
2012-02-07 03:52:24 +08:00
|
|
|
|
2018-08-12 15:30:57 +08:00
|
|
|
public:
|
|
|
|
// Gets the original search term.
|
|
|
|
const wcstring &original_term() const { return orig_term_; }
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Finds the previous search result (backwards in time). Returns true if one was found.
|
2018-07-15 02:47:34 +08:00
|
|
|
bool go_backwards();
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Returns the current search result item. asserts if there is no current item.
|
2019-11-03 09:27:35 +08:00
|
|
|
const history_item_t ¤t_item() const;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Returns the current search result item contents. asserts if there is no current item.
|
2019-11-03 09:27:35 +08:00
|
|
|
const wcstring ¤t_string() const;
|
2012-02-16 16:24:27 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Constructor.
|
|
|
|
history_search_t(history_t &hist, const wcstring &str,
|
2019-11-03 09:56:16 +08:00
|
|
|
enum history_search_type_t type = history_search_type_t::contains,
|
2018-08-12 15:30:57 +08:00
|
|
|
history_search_flags_t flags = 0)
|
|
|
|
: history_(&hist), orig_term_(str), canon_term_(str), search_type_(type), flags_(flags) {
|
|
|
|
if (ignores_case()) {
|
|
|
|
std::transform(canon_term_.begin(), canon_term_.end(), canon_term_.begin(), towlower);
|
2016-09-24 11:12:15 +08:00
|
|
|
}
|
|
|
|
}
|
2012-02-06 08:42:24 +08:00
|
|
|
|
2016-04-18 13:47:37 +08:00
|
|
|
// Default constructor.
|
2018-08-12 15:30:57 +08:00
|
|
|
history_search_t() = default;
|
2012-02-06 08:42:24 +08:00
|
|
|
};
|
|
|
|
|
2017-07-01 12:03:05 +08:00
|
|
|
/// Saves the new history to disk.
|
2018-02-11 09:21:55 +08:00
|
|
|
void history_save_all();
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2017-07-01 12:03:05 +08:00
|
|
|
/// Return the prefix for the files to be used for command and read history.
|
2018-09-19 00:18:47 +08:00
|
|
|
wcstring history_session_id(const environment_t &vars);
|
2017-07-01 08:13:02 +08:00
|
|
|
|
2017-07-01 12:03:05 +08:00
|
|
|
/// Given a list of paths and a working directory, return the paths that are valid
|
|
|
|
/// This does disk I/O and may only be called in a background thread
|
2017-01-25 09:52:28 +08:00
|
|
|
path_list_t valid_paths(const path_list_t &paths, const wcstring &working_directory);
|
|
|
|
|
2017-07-01 12:03:05 +08:00
|
|
|
/// Given a list of paths and a working directory,
|
|
|
|
/// return true if all paths in the list are valid
|
|
|
|
/// Returns true for if paths is empty
|
2017-01-25 09:52:28 +08:00
|
|
|
bool all_paths_are_valid(const path_list_t &paths, const wcstring &working_directory);
|
2018-09-20 07:56:08 +08:00
|
|
|
|
|
|
|
/// Sets private mode on. Once in private mode, it cannot be turned off.
|
2020-02-09 04:23:39 +08:00
|
|
|
void start_private_mode(env_stack_t &vars);
|
2018-09-20 07:56:08 +08:00
|
|
|
/// Queries private mode status.
|
|
|
|
bool in_private_mode();
|
2019-11-05 23:47:01 +08:00
|
|
|
|
2005-10-04 23:11:39 +08:00
|
|
|
#endif
|