1
0
Fork 0
mirror of https://git.sr.ht/~sircmpwn/gmni synced 2024-05-04 15:06:01 +02:00

Implement bookmarks

This commit is contained in:
Drew DeVault 2020-09-20 23:50:50 -04:00
parent 852bc7198f
commit 601f900886
4 changed files with 152 additions and 8 deletions

3
configure vendored
View File

@ -16,7 +16,8 @@ gmnlm() {
src/escape.c \
src/gmnlm.c \
src/parser.c \
src/url.c
src/url.c \
src/util.c
}
all="gmni gmnlm"

12
include/util.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef GEMINI_UTIL_H
#define GEMINI_UTIL_H
struct pathspec {
const char *var;
const char *path;
};
char *getpath(const struct pathspec *paths, size_t npaths);
int mkdirs(char *path, mode_t mode);
#endif

View File

@ -12,6 +12,7 @@
#include <unistd.h>
#include "gmni.h"
#include "url.h"
#include "util.h"
struct link {
char *url;
@ -29,6 +30,7 @@ struct browser {
FILE *tty;
char *plain_url;
char *page_title;
struct Curl_URL *url;
struct link *links;
struct history *history;
@ -52,6 +54,8 @@ const char *help_msg =
"N\tFollow Nth link (where N is a number)\n"
"b\tBack (in the page history)\n"
"f\tForward (in the page history)\n"
"m\tSave bookmark\n"
"M\tBrowse bookmarks\n"
"\n"
"Other commands include:\n\n"
"<Enter>\tread more lines\n"
@ -98,6 +102,63 @@ set_url(struct browser *browser, char *new_url, struct history **history)
return true;
}
static char *
get_data_pathfmt()
{
const struct pathspec paths[] = {
{.var = "GMNIDATA", .path = "/%s"},
{.var = "XDG_DATA_HOME", .path = "/gmni/%s"},
{.var = "HOME", .path = "/.local/share/gmni/%s"}
};
return getpath(paths, sizeof(paths) / sizeof(paths[0]));
}
static char *
trim_ws(char *in)
{
while (*in && isspace(*in)) ++in;
return in;
}
static void
save_bookmark(struct browser *browser)
{
const char *path_fmt = get_data_pathfmt();
static char path[PATH_MAX+1];
snprintf(path, sizeof(path), path_fmt, "bookmarks.gmi");
mkdirs(path, 0755);
FILE *f = fopen(path, "a");
if (!f) {
fprintf(stderr, "Error opening %s for writing: %s\n",
path, strerror(errno));
return;
}
char *title = browser->page_title;
if (title) {
title = trim_ws(browser->page_title);
}
fprintf(f, "=> %s%s%s\n", browser->plain_url,
title ? " " : "", title ? title : "");
fclose(f);
fprintf(browser->tty, "Bookmark saved: %s\n",
title ? title : browser->plain_url);
}
static void
open_bookmarks(struct browser *browser)
{
const char *path_fmt = get_data_pathfmt();
static char path[PATH_MAX+1];
snprintf(path, sizeof(path), path_fmt, "bookmarks.gmi");
static char url[PATH_MAX+1+7];
snprintf(url, sizeof(url), "file://%s", path);
set_url(browser, url, &browser->history);
}
static enum prompt_result
do_prompts(const char *prompt, struct browser *browser)
{
@ -134,6 +195,16 @@ do_prompts(const char *prompt, struct browser *browser)
set_url(browser, browser->history->url, NULL);
result = PROMPT_ANSWERED;
goto exit;
case 'm':
if (in[1]) break;
save_bookmark(browser);
result = PROMPT_AGAIN;
goto exit;
case 'M':
if (in[1]) break;
open_bookmarks(browser);
result = PROMPT_ANSWERED;
goto exit;
case 'f':
if (in[1]) break;
if (!browser->history->next) {
@ -205,13 +276,6 @@ exit_re:
return result;
}
static char *
trim_ws(char *in)
{
while (*in && isspace(*in)) ++in;
return in;
}
static int
wrap(FILE *f, char *s, struct winsize *ws, int *row, int *col)
{
@ -258,6 +322,8 @@ display_gemini(struct browser *browser, struct gemini_response *resp)
int nlinks = 0;
struct gemini_parser p;
gemini_parser_init(&p, resp->bio);
free(browser->page_title);
browser->page_title = NULL;
struct winsize ws;
ioctl(fileno(browser->tty), TIOCGWINSZ, &ws);
@ -302,6 +368,9 @@ repeat:
}
break;
case GEMINI_HEADING:
if (!browser->page_title) {
browser->page_title = strdup(tok.heading.title);
}
if (text == NULL) {
for (int n = tok.heading.level; n; --n) {
col += fprintf(out, "#");

62
src/util.c Normal file
View File

@ -0,0 +1,62 @@
#include <assert.h>
#include <errno.h>
#include <libgen.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "util.h"
static void
posix_dirname(char *path, char *dname)
{
char p[PATH_MAX+1];
char *t;
assert(strlen(path) <= PATH_MAX);
strcpy(p, path);
t = dirname(path);
memmove(dname, t, strlen(t) + 1);
/* restore the path if dirname worked in-place */
if (t == path && path != dname) {
strcpy(path, p);
}
}
/** Make directory and all of its parents */
int
mkdirs(char *path, mode_t mode)
{
char dname[PATH_MAX + 1];
posix_dirname(path, dname);
if (strcmp(dname, "/") == 0) {
return 0;
}
if (mkdirs(dname, mode) != 0) {
return -1;
}
if (mkdir(path, mode) != 0 && errno != EEXIST) {
return -1;
}
errno = 0;
return 0;
}
char *
getpath(const struct pathspec *paths, size_t npaths) {
for (size_t i = 0; i < npaths; i++) {
const char *var = "";
if (paths[i].var) {
var = getenv(paths[i].var);
}
if (var) {
char *out = calloc(1,
strlen(var) + strlen(paths[i].path) + 1);
strcat(strcat(out, var), paths[i].path);
return out;
}
}
return NULL;
}