commit eb2ed626f304f3f1e00711c20d76ecfd8dcc5ce7 from: Andrea Feletto via: Omar Polo date: Thu Oct 07 21:57:04 2021 UTC support xdg basedir Use XDG-compliant paths unless ~/.telescope already exists. Provide a script to migrate to XDG-style directory for users who wish to do so. commit - 633bf6d87d247b0fddf6259b176f491c3f736318 commit + eb2ed626f304f3f1e00711c20d76ecfd8dcc5ce7 blob - 18eb35022c9575129ab163d736402e4e0dff5bfb blob + 75f3482e4c2a078554ed59ba188c66b6d06af796 --- README.md +++ README.md @@ -120,9 +120,9 @@ that could be helpful to others, consider adding it to ## User files -Telescope stores user files in `~/.telescope`. The usage and contents -of these files are described in [the man page](telescope.1), under -"FILES". There's no support yet for XDG-style directories. +Telescope stores user files according to the +[XDG Base Directory Specification][xdg]. The usage and contents of these files +are described in [the man page](telescope.1), under "FILES". Only one instance of Telescope can be running at time per user. @@ -137,3 +137,4 @@ distributed under the [UNICODE, Inc license agreement] [unicode-license]: https://www.unicode.org/license.html +[xdg]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html blob - e46a362c97d9a10cb14ba7d4679a8c1e6e47497b blob + 2c6daa4565ce45e2f7eb957d4e0a447c58baf4e6 --- contrib/README.md +++ contrib/README.md @@ -3,7 +3,7 @@ - `brutalist.config`: a brutalist theme - `light.config`: an opinionated theme for light terminals. Load it with `telescope -c contrib/light.config` or copy it to - `~/.telescope/config` + `~/.config/telescope/config` - `dark.config`: an opinionated theme for dark terminals. Load it with `telescope -c contrib/dark.config` or copy it to - `~/.telecsope/config` + `~/.config/telescope/config` blob - /dev/null blob + 79a734da3fd082f4e974a0d403a049261db88678 (mode 755) --- /dev/null +++ contrib/xdg-migrate.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +old_path="$HOME/.telescope" + +Die() { + printf 'error: %s\n' "$1" 1>&2 + exit 1 +} + +[ -e "$old_path" ] || Die "$old_path does not exist." +[ -d "$old_path" ] || Die "$old_path is not a directory." + +xdg_config="${XDG_CONFIG_HOME:-$HOME/.config}/telescope" +xdg_data="${XDG_DATA_HOME:-$HOME/.local/share}/telescope" +xdg_cache="${XDG_CACHE_HOME:-$HOME/.cache}/telescope" + +mkdir -p "$xdg_config" "$xdg_data" "$xdg_cache" + +for filepath in \ + "$xdg_config/config" \ + "$xdg_config/pages" \ + "$xdg_data/bookmarks.gmi" \ + "$xdg_data/known_hosts" +do + old_file="$old_path/${filepath##*/}" + [ -e "$old_file" ] && cp -R "$old_file" "filepath" +done + +printf "\ +WARNING: the old ~/.telescope directory will be removed. + +Every file/directory other than the followings has not been copyied: + - config + - bookmarks.gmi + - known_hosts + - pages/ + +Are you sure? [Y/n] " + +read -r reply +case $reply in + [yY]) rm -r "$old_path" && printf 'done\n' ;; +esac blob - 0a10064c4146364f37a288587015018ce0c4b532 blob + fe5290ef682562a940933715bda1bf34554c84d7 --- fs.c +++ fs.c @@ -15,7 +15,7 @@ */ /* - * Handles the data in ~/.telescope + * Handles config and runtime files */ #include "compat.h" @@ -27,11 +27,13 @@ #include #include #include +#include #include #include #include #include +#include "fs.h" #include "pages.h" #include "telescope.h" @@ -52,11 +54,25 @@ static void handle_session_tab_title(struct imsg*, s static void handle_session_end(struct imsg*, size_t); static void handle_dispatch_imsg(int, short, void*); static int fs_send_ui(int, uint32_t, int, const void *, uint16_t); +static size_t join_path(char*, const char*, const char*, size_t); +static void getenv_default(char*, const char*, const char*, size_t); +static void mkdirs(const char*, mode_t); +static void xdg_init(void); static struct imsgev *iev_ui; static FILE *session; -static char base_path[PATH_MAX]; +/* WARNING: xdg_*_base variables are not initialized if ~/.telescope exists */ +static char xdg_config_base[PATH_MAX]; +static char xdg_data_base[PATH_MAX]; +static char xdg_cache_base[PATH_MAX]; + +/* *_path_base variables are all equal to $HOME/.telescope if it exists */ +static char config_path_base[PATH_MAX]; +static char data_path_base[PATH_MAX]; +static char cache_path_base[PATH_MAX]; + +char config_path[PATH_MAX]; static char lockfile_path[PATH_MAX]; static char bookmark_file[PATH_MAX]; static char known_hosts_file[PATH_MAX], known_hosts_tmp[PATH_MAX]; @@ -140,7 +156,7 @@ handle_get(struct imsg *imsg, size_t datalen) if (page == NULL) goto notfound; - strlcpy(path, base_path, sizeof(path)); + strlcpy(path, config_path_base, sizeof(path)); strlcat(path, "/", sizeof(path)); if (page->path != NULL) strlcat(path, page->path, sizeof(path)); @@ -514,7 +530,7 @@ handle_dispatch_imsg(int fd, short ev, void *d) if (dispatch_imsg(iev, ev, handlers, sizeof(handlers)) == -1) { /* - * This should leave a ~/.telescope/crashed file to + * This should leave a ~/.cache/telescope/crashed file to * trigger about:crash on next run. Unfortunately, if * the main process dies the fs sticks around and * doesn't notice that the fd was closed. Why EV_READ @@ -537,32 +553,115 @@ fs_send_ui(int type, uint32_t peerid, int fd, const vo data, datalen); } -int -fs_init(void) +static size_t +join_path(char *buf, const char *lhs, const char *rhs, size_t buflen) { - strlcpy(base_path, getenv("HOME"), sizeof(base_path)); - strlcat(base_path, "/.telescope", sizeof(base_path)); - mkdir(base_path, 0700); + strlcpy(buf, lhs, buflen); + return strlcat(buf, rhs, buflen); +} - strlcpy(lockfile_path, base_path, sizeof(lockfile_path)); - strlcat(lockfile_path, "/lock", sizeof(lockfile_path)); - - strlcpy(bookmark_file, base_path, sizeof(bookmark_file)); - strlcat(bookmark_file, "/bookmarks.gmi", sizeof(bookmark_file)); +static void +getenv_default(char *buf, const char *name, const char *def, size_t buflen) +{ + size_t ret; + char *home, *env; - strlcpy(known_hosts_file, base_path, sizeof(known_hosts_file)); - strlcat(known_hosts_file, "/known_hosts", sizeof(known_hosts_file)); + if ((home = getenv("HOME")) == NULL) + errx(1, "HOME is not defined"); - strlcpy(known_hosts_tmp, base_path, sizeof(known_hosts_tmp)); - strlcat(known_hosts_tmp, "/known_hosts.tmp.XXXXXXXXXX", - sizeof(known_hosts_file)); + if ((env = getenv(name)) != NULL) + ret = strlcpy(buf, env, buflen); + else + ret = join_path(buf, home, def, buflen); - strlcpy(session_file, base_path, sizeof(session_file)); - strlcat(session_file, "/session", sizeof(session_file)); + if (ret >= buflen) + errx(1, "buffer too small for %s", name); +} - strlcpy(crashed_file, base_path, sizeof(crashed_file)); - strlcat(crashed_file, "/crashed", sizeof(crashed_file)); +static void +mkdirs(const char *path, mode_t mode) +{ + char copy[PATH_MAX+1], *parent; + strlcpy(copy, path, sizeof(copy)); + parent = dirname(copy); + if (!strcmp(parent, "/")) + return; + mkdirs(parent, mode); + + if (mkdir(path, mode) != 0) { + if (errno == EEXIST) + return; + err(1, "can't mkdir %s", path); + } +} + +static void +xdg_init(void) +{ + char *home, old_path[PATH_MAX]; + struct stat info; + + /* old path */ + if ((home = getenv("HOME")) == NULL) + errx(1, "HOME is not defined"); + join_path(old_path, home, "/.telescope", sizeof(old_path)); + + /* if ~/.telescope exists, use that instead of xdg dirs */ + if (stat(old_path, &info) == 0 && S_ISDIR(info.st_mode)) { + join_path(config_path_base, home, "/.telescope", + sizeof(config_path_base)); + join_path(data_path_base, home, "/.telescope", + sizeof(data_path_base)); + join_path(cache_path_base, home, "/.telescope", + sizeof(cache_path_base)); + return; + } + + /* xdg paths */ + getenv_default(xdg_config_base, "XDG_CONFIG_HOME", "/.config", + sizeof(xdg_config_base)); + getenv_default(xdg_data_base, "XDG_DATA_HOME", "/.local/share", + sizeof(xdg_data_base)); + getenv_default(xdg_cache_base, "XDG_CACHE_HOME", "/.cache", + sizeof(xdg_cache_base)); + + join_path(config_path_base, xdg_config_base, "/telescope", + sizeof(config_path_base)); + join_path(data_path_base, xdg_data_base, "/telescope", + sizeof(data_path_base)); + join_path(cache_path_base, xdg_cache_base, "/telescope", + sizeof(cache_path_base)); + + mkdirs(xdg_config_base, S_IRWXU); + mkdirs(xdg_data_base, S_IRWXU); + mkdirs(xdg_cache_base, S_IRWXU); + + mkdirs(config_path_base, S_IRWXU); + mkdirs(data_path_base, S_IRWXU); + mkdirs(cache_path_base, S_IRWXU); +} + +int +fs_init(void) +{ + xdg_init(); + + join_path(config_path, config_path_base, "/config", + sizeof(config_path)); + join_path(lockfile_path, cache_path_base, "/lock", + sizeof(lockfile_path)); + join_path(bookmark_file, data_path_base, "/bookmarks.gmi", + sizeof(bookmark_file)); + join_path(known_hosts_file, data_path_base, "/known_hosts", + sizeof(known_hosts_file)); + join_path(known_hosts_tmp, cache_path_base, + "/known_hosts.tmp.XXXXXXXXXX", sizeof(known_hosts_tmp)); + join_path(session_file, cache_path_base, "/session", + sizeof(session_file)); + join_path(crashed_file, cache_path_base, "/crashed", + sizeof(crashed_file)); + return 1; } blob - /dev/null blob + d7b6931d477c232dd73707897491781aa4150416 (mode 644) --- /dev/null +++ fs.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef FS_H +#define FS_H + +#include + +extern char config_path[PATH_MAX]; + +#endif blob - 7af4b34ab6e3ceb2b2293fad3d137a3d6463f0c0 blob + b29b85415704dfea07ae512b66885e20f79ea08e --- pages/about_help.gmi +++ pages/about_help.gmi @@ -56,7 +56,7 @@ Telescope strives to be familiar for vi/vim users too! Telescope is fully customizable. The configuration file is -> ~/.telescope/config +> ~/.config/telescope/config By default Telescope doesn’t use colours too much in order to adapt to both light and dark-themed terminals. This doesn’t mean that Telescope cannot use colours though! See for example contrib/light.config and contrib/dark.config. @@ -67,11 +67,11 @@ By default Telescope doesn’t use colours too much in All the ‘about:*’ pages can be locally overridden. For example, to customise about:new create: -> ~/.telescope/pages/about_new.gmi +> ~/.config/telescope/pages/about_new.gmi about:bookmarks is the only page that doesn’t follow this pattern; it’s located at -> ~/.telescope/bookmarks.gmi +> ~/.local/share/telescope/bookmarks.gmi ## Protocol Proxies blob - b7d11fff56505b844c2b07147aa9b9b5982456eb blob + 9dd8771c3768e5133f73b60628a815f3eb6d4f01 --- pages/bookmarks.gmi +++ pages/bookmarks.gmi @@ -2,4 +2,4 @@ No bookmarks yet! -Create ~/.telescope/bookmarks.gmi or use ‘bookmark-page’. +Create ~/.local/share/telescope/bookmarks.gmi or use ‘bookmark-page’. blob - 4e30e954ab7655bb3ef4d8d6513b644df781192b blob + 1546d7a1499832c05dec9700952b543cff9fd5d9 --- telescope.1 +++ telescope.1 @@ -39,7 +39,7 @@ Show all available colors and exit. .It Fl c Pa config Specify an alternative configuration file. By default -.Pa $HOME/.telescope/config +.Pa $HOME/.config/telescope/config is loaded. .It Fl h , Fl -help Display version and usage. @@ -635,7 +635,7 @@ The following aliases are available during During the startup, .Nm reads the configuration file at -.Pa ~/.telescope/config +.Pa ~/.config/telescope/config or the one given with the .Fl c flag. @@ -880,21 +880,21 @@ The user's terminal name. .El .Sh FILES .Bl -tag -width Ds -compact -.It Pa ~/.telescope/bookmarks.gmi -Bookmarks file. -.It Pa ~/.telescope/config +.It Pa ~/.config/telescope/config Default configuration file. -.It Pa ~/.telescope/known_hosts +.It Pa ~/.config/telescope/pages/about_*.gmi +Overrides for built-in about: pages. +.It Pa ~/.local/share/telescope/bookmarks.gmi +Bookmarks file. +.It Pa ~/.local/share/telescope/known_hosts Hash of the certificates for all the known hosts. Each line contains three fields: hostname with optional port number, hash of the certificate and a numeric flag. -.It Pa ~/.telescope/lock +.It Pa ~/.cache/telescope/lock Lock file used to prevent multiple instance of .Nm from running at the same time. -.It Pa ~/.telescope/pages/about_*.gmi -Overrides for built-in about: pages. -.It Pa ~/.telescope/session +.It Pa ~/.cache/telescope/session The list of tabs from the last session. Every line identifies a tab and contains three space-separated fields: the full URL, a comma-separated list of attributes and the cached @@ -908,7 +908,7 @@ It's possible to browse .Dq the small web .Pq i.e. simple websites by using programs like the duckling-proxy by defining a proxy in -.Pa ~/.telescope/config : +.Pa ~/.config/telescope/config : .Bd -literal -offset indent proxy http via "gemini://127.0.0.1:1965" proxy https via "gemini://127.0.0.1:1965" blob - bee474089af045b48224937ec8bd34bc6ca4793e blob + 31a077546609381a7b422b13497c50095ac2e693 --- telescope.c +++ telescope.c @@ -28,6 +28,7 @@ #include #include "defaults.h" +#include "fs.h" #include "minibuffer.h" #include "parser.h" #include "session.h" @@ -1066,7 +1067,7 @@ main(int argc, char * const *argv) int proc = -1; int sessionfd = -1; int status; - char path[PATH_MAX], url[GEMINI_URL_LEN+1]; + char url[GEMINI_URL_LEN+1]; const char *argv0; argv0 = argv[0]; @@ -1077,8 +1078,7 @@ main(int argc, char * const *argv) if (getenv("NO_COLOR") != NULL) enable_colors = 0; - strlcpy(path, getenv("HOME"), sizeof(path)); - strlcat(path, "/.telescope/config", sizeof(path)); + fs_init(); while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) { switch (ch) { @@ -1086,7 +1086,7 @@ main(int argc, char * const *argv) exit(ui_print_colors()); case 'c': fail = 1; - strlcpy(path, optarg, sizeof(path)); + strlcpy(config_path, optarg, sizeof(config_path)); break; case 'n': configtest = 1; @@ -1143,7 +1143,7 @@ main(int argc, char * const *argv) TAILQ_INIT(&minibuffer_map.m); config_init(); - parseconfig(path, fail); + parseconfig(config_path, fail); if (configtest) { puts("config OK"); exit(0); @@ -1153,7 +1153,6 @@ main(int argc, char * const *argv) (download_path = strdup("/tmp/")) == NULL) errx(1, "strdup"); - fs_init(); if (!safe_mode && (sessionfd = lock_session()) == -1) errx(1, "can't lock session, is another instance of " "telescope already running?");