2 * Copyright (c) 2021, 2024 Omar Polo <op@omarpolo.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * Handles config and runtime files
24 #include <sys/types.h>
37 #include "telescope.h"
44 #define nitems(x) (sizeof(x) / sizeof(x[0]))
47 static int select_non_dot(const struct dirent *);
48 static int select_non_dotdot(const struct dirent *);
49 static size_t join_path(char*, const char*, const char*, size_t);
50 static void getenv_default(char*, const char*, const char*, size_t);
51 static void mkdirs(const char*, mode_t);
52 static void init_paths(void);
55 * Where to store user data. These are all equal to ~/.telescope if
58 char config_path_base[PATH_MAX];
59 char data_path_base[PATH_MAX];
60 char cache_path_base[PATH_MAX];
62 char ctlsock_path[PATH_MAX];
63 char config_path[PATH_MAX];
64 char lockfile_path[PATH_MAX];
65 char bookmark_file[PATH_MAX];
66 char known_hosts_file[PATH_MAX], known_hosts_tmp[PATH_MAX];
67 char crashed_file[PATH_MAX];
68 char session_file[PATH_MAX], session_file_tmp[PATH_MAX];
69 char history_file[PATH_MAX], history_file_tmp[PATH_MAX];
70 char cert_dir[PATH_MAX], cert_dir_tmp[PATH_MAX];
71 char certs_file[PATH_MAX], certs_file_tmp[PATH_MAX];
76 select_non_dot(const struct dirent *d)
78 return strcmp(d->d_name, ".");
82 select_non_dotdot(const struct dirent *d)
84 return strcmp(d->d_name, ".") && strcmp(d->d_name, "..");
88 send_dir(struct tab *tab, const char *path)
90 struct buffer *buffer = &tab->buffer;
91 struct dirent **names;
92 int (*selector)(const struct dirent *) = select_non_dot;
97 * need something to fake a redirect
100 if (!has_suffix(path, "/")) {
101 xasprintf(&s, "%s/", path);
102 send_hdr(peerid, 30, s);
108 if (!strcmp(path, "/"))
109 selector = select_non_dotdot;
111 if ((len = scandir(path, &names, selector, alphasort)) == -1) {
112 load_page_from_str(tab, "# failure reading the directory\n");
116 parser_init(buffer, &gemtext_parser);
117 parser_parsef(buffer, "# Index of %s\n\n", path);
119 for (i = 0; i < len; ++i) {
120 const char *sufx = "";
122 if (names[i]->d_type == DT_DIR)
125 parser_parsef(buffer, "=> %s%s\n", names[i]->d_name, sufx);
137 if (fstat(fileno(fp), &sb) == -1)
140 return S_ISDIR(sb.st_mode);
143 static const struct parser *
144 file_type(const char *path)
146 const struct mapping {
148 const struct parser *parser;
150 {"diff", &textpatch_parser},
151 {"gemini", &gemtext_parser},
152 {"gmi", &gemtext_parser},
153 {"markdown", &textplain_parser},
154 {"md", &textplain_parser},
155 {"patch", &gemtext_parser},
160 if ((dot = strrchr(path, '.')) == NULL)
161 return &textplain_parser;
165 for (m = ms; m->ext != NULL; ++m)
166 if (!strcmp(m->ext, dot))
169 return &textplain_parser;
173 fs_load_url(struct tab *tab, const char *url)
175 const char *bpath = "bookmarks.gmi", *fallback = "# Not found\n";
176 const struct parser *parser = &gemtext_parser;
186 {"about", NULL, about_about},
187 {"blank", NULL, about_blank},
188 {"bookmarks", bpath, bookmarks},
189 {"crash", NULL, about_crash},
190 {"help", NULL, about_help},
191 {"license", NULL, about_license},
192 {"new", NULL, about_new},
195 if (!strncmp(url, "about:", 6)) {
198 for (i = 0; page == NULL && i < nitems(pages); ++i) {
199 if (!strcmp(url, pages[i].name))
206 strlcpy(path, data_path_base, sizeof(path));
207 strlcat(path, "/", sizeof(path));
208 if (page->path != NULL)
209 strlcat(path, page->path, sizeof(path));
211 strlcat(path, "page/about_", sizeof(path));
212 strlcat(path, page->name, sizeof(path));
213 strlcat(path, ".gmi", sizeof(path));
216 fallback = page->data;
217 } else if (!strncmp(url, "file://", 7)) {
219 strlcpy(path, url, sizeof(path));
220 parser = file_type(url);
224 if ((fp = fopen(path, "r")) == NULL)
232 parser_init(&tab->buffer, parser);
236 r = fread(buf, 1, sizeof(buf), fp);
237 if (!parser_parse(&tab->buffer, buf, r))
239 if (r != sizeof(buf))
248 load_page_from_str(tab, fallback);
252 join_path(char *buf, const char *lhs, const char *rhs, size_t buflen)
254 strlcpy(buf, lhs, buflen);
255 return strlcat(buf, rhs, buflen);
259 getenv_default(char *buf, const char *name, const char *def, size_t buflen)
264 if ((home = getenv("HOME")) == NULL)
265 errx(1, "HOME is not defined");
267 if ((env = getenv(name)) != NULL)
268 ret = strlcpy(buf, env, buflen);
270 ret = join_path(buf, home, def, buflen);
273 errx(1, "buffer too small for %s", name);
277 mkdirs(const char *path, mode_t mode)
279 char copy[PATH_MAX+1], orig[PATH_MAX+1], *parent;
281 strlcpy(copy, path, sizeof(copy));
282 strlcpy(orig, path, sizeof(orig));
283 parent = dirname(copy);
284 if (!strcmp(parent, "/"))
286 mkdirs(parent, mode);
288 if (mkdir(orig, mode) != 0) {
291 err(1, "can't mkdir %s", orig);
298 char xdg_config_base[PATH_MAX];
299 char xdg_data_base[PATH_MAX];
300 char xdg_cache_base[PATH_MAX];
301 char old_path[PATH_MAX];
305 if (getcwd(cwd, sizeof(cwd)) == NULL)
306 err(1, "getcwd failed");
309 if ((home = getenv("HOME")) == NULL)
310 errx(1, "HOME is not defined");
311 join_path(old_path, home, "/.telescope", sizeof(old_path));
313 /* if ~/.telescope exists, use that instead of xdg dirs */
314 if (stat(old_path, &info) == 0 && S_ISDIR(info.st_mode)) {
315 join_path(config_path_base, home, "/.telescope",
316 sizeof(config_path_base));
317 join_path(data_path_base, home, "/.telescope",
318 sizeof(data_path_base));
319 join_path(cache_path_base, home, "/.telescope",
320 sizeof(cache_path_base));
325 getenv_default(xdg_config_base, "XDG_CONFIG_HOME", "/.config",
326 sizeof(xdg_config_base));
327 getenv_default(xdg_data_base, "XDG_DATA_HOME", "/.local/share",
328 sizeof(xdg_data_base));
329 getenv_default(xdg_cache_base, "XDG_CACHE_HOME", "/.cache",
330 sizeof(xdg_cache_base));
332 join_path(config_path_base, xdg_config_base, "/telescope",
333 sizeof(config_path_base));
334 join_path(data_path_base, xdg_data_base, "/telescope",
335 sizeof(data_path_base));
336 join_path(cache_path_base, xdg_cache_base, "/telescope",
337 sizeof(cache_path_base));
339 mkdirs(xdg_config_base, S_IRWXU);
340 mkdirs(xdg_data_base, S_IRWXU);
341 mkdirs(xdg_cache_base, S_IRWXU);
343 mkdirs(config_path_base, S_IRWXU);
344 mkdirs(data_path_base, S_IRWXU);
345 mkdirs(cache_path_base, S_IRWXU);
353 join_path(ctlsock_path, cache_path_base, "/ctl",
354 sizeof(ctlsock_path));
355 join_path(config_path, config_path_base, "/config",
356 sizeof(config_path));
357 join_path(lockfile_path, cache_path_base, "/lock",
358 sizeof(lockfile_path));
359 join_path(bookmark_file, data_path_base, "/bookmarks.gmi",
360 sizeof(bookmark_file));
361 join_path(known_hosts_file, data_path_base, "/known_hosts",
362 sizeof(known_hosts_file));
363 join_path(known_hosts_tmp, cache_path_base,
364 "/known_hosts.tmp.XXXXXXXXXX", sizeof(known_hosts_tmp));
365 join_path(session_file, cache_path_base, "/session",
366 sizeof(session_file));
367 join_path(session_file_tmp, cache_path_base, "/session.XXXXXXXXXX",
368 sizeof(session_file_tmp));
369 join_path(history_file, cache_path_base, "/history",
370 sizeof(history_file));
371 join_path(history_file_tmp, cache_path_base, "/history.XXXXXXXXXX",
372 sizeof(history_file_tmp));
373 join_path(cert_dir, data_path_base, "/certs/",
375 join_path(cert_dir_tmp, data_path_base, "/certs/id.XXXXXXXXXX",
376 sizeof(cert_dir_tmp));
377 join_path(certs_file, config_path_base, "/certs.conf",
379 join_path(certs_file_tmp, config_path_base, "/certs.conf.XXXXXXXXXX",
380 sizeof(certs_file_tmp));
381 join_path(crashed_file, cache_path_base, "/crashed",
382 sizeof(crashed_file));
384 mkdirs(cert_dir, S_IRWXU);