From 2971887bbdd34813c86bd3e2f1959e4f608eb4f0 Mon Sep 17 00:00:00 2001 From: Jeff Kowalski Date: Wed, 16 Sep 2015 21:31:08 -0700 Subject: [PATCH] Access fish_history from $XDG_DATA_HOME Add new functions path_get_data and path_create_data which parallel existing functions path_get_config and path_create_data. The new functions refer to XDG_DATA_HOME, if it is defined, or ./local/share if not. Modify history_filename to use the new function path_get_data. As a consequence, fish_history will now be located in XDG_DATA_HOME, not XDG_CONFIG_HOME. Note that these changes mirror what is already used in fish-shell/share/tools/create_manpage_completions.py, which stores the completions in XDG_DATA_HOME This change matches recommendations in the xdg basedir spec at http://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html ($XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.) It addresses suggestions from the following issues: 1. Don't put history in $XDG_CONFIG_HOME (closes #744) https://github.com/fish-shell/fish-shell/issues/744 2. Fish is placing non-config files in $XDG_CONFIG_HOME #1257 https://github.com/fish-shell/fish-shell/issues/1257 3. Move non-config data out of $XDG_CONFIG_HOME #1669 https://github.com/fish-shell/fish-shell/issues/1669 --- src/history.cpp | 16 ++++++++-------- src/path.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/path.h | 15 ++++++++++++++- 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/history.cpp b/src/history.cpp index 5d9528b20..6b4582128 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -388,8 +388,8 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length ! memcmp(line_start, "---", 3) || ! memcmp(line_start, "...", 3)) continue; - - + + /* Hackish: fish 1.x rewriting a fish 2.0 history file can produce lines with lots of leading "- cmd: - cmd: - cmd:". Trim all but one leading "- cmd:". */ const char *double_cmd = "- cmd: - cmd: "; const size_t double_cmd_len = strlen(double_cmd); @@ -398,13 +398,13 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length /* Skip over just one of the - cmd. In the end there will be just one left. */ line_start += strlen("- cmd: "); } - + /* Hackish: fish 1.x rewriting a fish 2.0 history file can produce commands like "when: 123456". Ignore those. */ const char *cmd_when = "- cmd: when:"; const size_t cmd_when_len = strlen(cmd_when); if (newline - line_start >= cmd_when_len && ! memcmp(line_start, cmd_when, cmd_when_len)) continue; - + /* At this point, we know line_start is at the beginning of an item. But maybe we want to skip this item because of timestamps. A 0 cutoff means we don't care; if we do care, then try parsing out a timestamp. */ if (cutoff_timestamp != 0) @@ -733,7 +733,7 @@ history_item_t history_t::item_at_index(size_t idx) { resolved_new_item_count -= 1; } - + /* idx=0 corresponds to the last resolved item */ if (idx < resolved_new_item_count) { @@ -816,7 +816,7 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len) size_t indent = 0, cursor = 0; std::string key, value, line; - + /* Read the "- cmd:" line */ size_t advance = read_line(base, cursor, len, line); trim_leading_spaces(line); @@ -824,7 +824,7 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len) { goto done; } - + cursor += advance; cmd = str2wcstring(value); @@ -1275,7 +1275,7 @@ static void unescape_yaml(std::string *str) static wcstring history_filename(const wcstring &name, const wcstring &suffix) { wcstring path; - if (! path_get_config(path)) + if (! path_get_data(path)) return L""; wcstring result = path; diff --git a/src/path.cpp b/src/path.cpp index 62a31a635..b8cfd5165 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -276,6 +276,42 @@ static wcstring path_create_config() return res; } +static wcstring path_create_data() +{ + bool done = false; + wcstring res; + + const env_var_t xdg_dir = env_get_string(L"XDG_DATA_HOME"); + if (! xdg_dir.missing()) + { + res = xdg_dir + L"/fish"; + if (!create_directory(res)) + { + done = true; + } + } + else + { + const env_var_t home = env_get_string(L"HOME"); + if (! home.missing()) + { + res = home + L"/.local/share/fish"; + if (!create_directory(res)) + { + done = true; + } + } + } + + if (! done) + { + res.clear(); + + debug(0, _(L"Unable to create a data directory for fish. Your history will not be saved. Please set the $XDG_DATA_HOME variable to a directory where the current user has write access.")); + } + return res; +} + /* Cache the config path */ bool path_get_config(wcstring &path) { @@ -284,6 +320,14 @@ bool path_get_config(wcstring &path) return ! result.empty(); } +/* Cache the data path */ +bool path_get_data(wcstring &path) +{ + static const wcstring result = path_create_data(); + path = result; + return ! result.empty(); +} + __attribute__((unused)) static void replace_all(wcstring &str, const wchar_t *needle, const wchar_t *replacement) { diff --git a/src/path.h b/src/path.h index eb79ee8a1..973e8a3ae 100644 --- a/src/path.h +++ b/src/path.h @@ -20,13 +20,26 @@ /** Returns the user configuration directory for fish. If the directory - or one of it's parents doesn't exist, they are first created. + or one of its parents doesn't exist, they are first created. \param path The directory as an out param \return whether the directory was returned successfully */ bool path_get_config(wcstring &path); +/** + Returns the user data directory for fish. If the directory + or one of its parents doesn't exist, they are first created. + + Volatile files presumed to be local to the machine, + such as the fish_history and all the generated_completions, + will be stored in this directory. + + \param path The directory as an out param + \return whether the directory was returned successfully +*/ +bool path_get_data(wcstring &path); + /** Finds the full path of an executable. Returns YES if successful.