2005-09-15 09:58:31 -04:00
|
|
|
/* Internal bookmarks support */
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "elinks.h"
|
|
|
|
|
|
|
|
#include "bfu/dialog.h"
|
|
|
|
#include "bfu/hierbox.h"
|
2006-06-14 03:11:51 -04:00
|
|
|
#include "bfu/listbox.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "bookmarks/backend/common.h"
|
|
|
|
#include "bookmarks/bookmarks.h"
|
|
|
|
#include "bookmarks/dialogs.h"
|
|
|
|
#include "config/home.h"
|
|
|
|
#include "config/options.h"
|
2021-08-08 15:25:08 -04:00
|
|
|
#include "intl/libintl.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "main/module.h"
|
|
|
|
#include "main/object.h"
|
|
|
|
#include "protocol/uri.h"
|
|
|
|
#include "session/task.h"
|
|
|
|
#include "terminal/tab.h"
|
|
|
|
#include "util/conv.h"
|
|
|
|
#include "util/hash.h"
|
|
|
|
#include "util/lists.h"
|
|
|
|
#include "util/memory.h"
|
|
|
|
#include "util/secsave.h"
|
|
|
|
#include "util/string.h"
|
|
|
|
|
|
|
|
/* The list of bookmarks */
|
2007-07-26 15:39:08 -04:00
|
|
|
INIT_LIST_OF(struct bookmark, bookmarks);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
/* Set to 1, if bookmarks have changed. */
|
|
|
|
static int bookmarks_dirty = 0;
|
|
|
|
|
|
|
|
static struct hash *bookmark_cache = NULL;
|
|
|
|
|
|
|
|
static struct bookmark *bm_snapshot_last_folder;
|
|
|
|
|
|
|
|
|
|
|
|
/* Life functions */
|
|
|
|
|
bug 764: Initialize the right member of union option_value
INIT_OPTION used to initialize union option_value at compile time by
casting the default value to LIST_OF(struct option) *, which is the
type of the first member. On sparc64 and other big-endian systems
where sizeof(int) < sizeof(struct list_head *), this tended to leave
option->value.number as zero, thus messing up OPT_INT and OPT_BOOL
at least. OPT_LONG however tended to work right.
This would be easy to fix with C99 designated initializers,
but doc/hacking.txt says ELinks must be kept C89 compatible.
Another solution would be to make register_options() read the
value from option->value.tree (the first member), cast it back
to the right type, and write it to the appropriate member;
but that would still require somewhat dubious conversions
between integers, data pointers, and function pointers.
So here's a rather more invasive solution. Add struct option_init,
which is somewhat similar to struct option but has non-overlapping
members for different types of values, to ensure nothing is lost
in compile-time conversions. Move unsigned char *path from struct
option_info to struct option_init, and replace struct option_info
with a union that contains struct option_init and struct option.
Now, this union can be initialized with no portability problems,
and register_options() then moves the values from struct option_init
to their final places in struct option.
In my x86 ELinks build with plenty of options configured in, this
change bloated the text section by 340 bytes but compressed the data
section by 2784 bytes, presumably because union option_info is a
pointer smaller than struct option_info was.
(cherry picked from elinks-0.12 commit e5f6592ee20780a61f70feeb1f9e17631b9c5835)
Conflicts:
src/protocol/fsp/fsp.c: All options had been removed in 0.13.GIT.
src/protocol/smb/smb2.c: Ditto.
2009-08-15 15:39:07 -04:00
|
|
|
static union option_info bookmark_options_info[] = {
|
2022-01-19 16:49:13 -05:00
|
|
|
INIT_OPT_TREE(C_(""), N_("Bookmarks"),
|
|
|
|
C_("bookmarks"), OPT_ZERO,
|
2005-09-15 09:58:31 -04:00
|
|
|
N_("Bookmark options.")),
|
|
|
|
|
|
|
|
#ifdef CONFIG_XBEL_BOOKMARKS
|
2022-01-19 16:49:13 -05:00
|
|
|
INIT_OPT_INT(C_("bookmarks"), N_("File format"),
|
|
|
|
C_("file_format"), OPT_ZERO, 0, 1, 0,
|
Rewrap lines in option documentation.
Documentation strings of most options used to contain a "\n" at the
end of each source line. When the option manager displayed these
strings, it treated each "\n" as a hard newline. On 80x24 terminals
however, the option description window has only 60 columes available
for the text (with the default setup.h), and the hard newlines were
further apart, so the option manager wrapped the text a second time,
resulting in rather ugly output where long lones are interleaved with
short ones. This could also cause the text to take up too much
vertical space and not fit in the window.
Replace most of those hard newlines with spaces so that the option
manager (or perhaps BFU) will take care of the wrapping. At the same
time, rewrap the strings in source code so that the source lines are
at most 79 columns wide.
In some options though, there is a list of possible values and their
meanings. In those lists, if the description of one value does not
fit in one line, then continuation lines should be indented. The
option manager and BFU are not currently able to do that. So, keep
the hard newlines in those lists, but rewrap them to 60 columns so
that they are less likely to require further wrapping at runtime.
2009-03-07 13:48:38 -05:00
|
|
|
N_("File format for bookmarks (affects both reading and "
|
|
|
|
"saving):\n"
|
2005-09-15 09:58:31 -04:00
|
|
|
"0 is the default native ELinks format\n"
|
2009-01-04 06:59:04 -05:00
|
|
|
"1 is XBEL universal XML bookmarks format")),
|
2005-09-15 09:58:31 -04:00
|
|
|
#else
|
2022-01-19 16:49:13 -05:00
|
|
|
INIT_OPT_INT(C_("bookmarks"), N_("File format"),
|
|
|
|
C_("file_format"), OPT_ZERO, 0, 1, 0,
|
Rewrap lines in option documentation.
Documentation strings of most options used to contain a "\n" at the
end of each source line. When the option manager displayed these
strings, it treated each "\n" as a hard newline. On 80x24 terminals
however, the option description window has only 60 columes available
for the text (with the default setup.h), and the hard newlines were
further apart, so the option manager wrapped the text a second time,
resulting in rather ugly output where long lones are interleaved with
short ones. This could also cause the text to take up too much
vertical space and not fit in the window.
Replace most of those hard newlines with spaces so that the option
manager (or perhaps BFU) will take care of the wrapping. At the same
time, rewrap the strings in source code so that the source lines are
at most 79 columns wide.
In some options though, there is a list of possible values and their
meanings. In those lists, if the description of one value does not
fit in one line, then continuation lines should be indented. The
option manager and BFU are not currently able to do that. So, keep
the hard newlines in those lists, but rewrap them to 60 columns so
that they are less likely to require further wrapping at runtime.
2009-03-07 13:48:38 -05:00
|
|
|
N_("File format for bookmarks (affects both reading and "
|
|
|
|
"saving):\n"
|
2005-09-15 09:58:31 -04:00
|
|
|
"0 is the default native ELinks format\n"
|
2009-01-04 06:59:04 -05:00
|
|
|
"1 is XBEL universal XML bookmarks format (DISABLED)")),
|
2005-09-15 09:58:31 -04:00
|
|
|
#endif
|
|
|
|
|
2022-01-19 16:49:13 -05:00
|
|
|
INIT_OPT_BOOL(C_("bookmarks"), N_("Save folder state"),
|
|
|
|
C_("folder_state"), OPT_ZERO, 1,
|
Rewrap lines in option documentation.
Documentation strings of most options used to contain a "\n" at the
end of each source line. When the option manager displayed these
strings, it treated each "\n" as a hard newline. On 80x24 terminals
however, the option description window has only 60 columes available
for the text (with the default setup.h), and the hard newlines were
further apart, so the option manager wrapped the text a second time,
resulting in rather ugly output where long lones are interleaved with
short ones. This could also cause the text to take up too much
vertical space and not fit in the window.
Replace most of those hard newlines with spaces so that the option
manager (or perhaps BFU) will take care of the wrapping. At the same
time, rewrap the strings in source code so that the source lines are
at most 79 columns wide.
In some options though, there is a list of possible values and their
meanings. In those lists, if the description of one value does not
fit in one line, then continuation lines should be indented. The
option manager and BFU are not currently able to do that. So, keep
the hard newlines in those lists, but rewrap them to 60 columns so
that they are less likely to require further wrapping at runtime.
2009-03-07 13:48:38 -05:00
|
|
|
N_("When saving bookmarks also store whether folders are "
|
|
|
|
"expanded or not, so the look of the bookmark dialog is "
|
|
|
|
"kept across ELinks sessions. If disabled all folders will "
|
2005-09-15 09:58:31 -04:00
|
|
|
"appear unexpanded next time ELinks is run.")),
|
|
|
|
|
2022-01-19 16:49:13 -05:00
|
|
|
INIT_OPT_BOOL(C_("ui.sessions"), N_("Periodic snapshotting"),
|
|
|
|
C_("snapshot"), OPT_ZERO, 0,
|
Rewrap lines in option documentation.
Documentation strings of most options used to contain a "\n" at the
end of each source line. When the option manager displayed these
strings, it treated each "\n" as a hard newline. On 80x24 terminals
however, the option description window has only 60 columes available
for the text (with the default setup.h), and the hard newlines were
further apart, so the option manager wrapped the text a second time,
resulting in rather ugly output where long lones are interleaved with
short ones. This could also cause the text to take up too much
vertical space and not fit in the window.
Replace most of those hard newlines with spaces so that the option
manager (or perhaps BFU) will take care of the wrapping. At the same
time, rewrap the strings in source code so that the source lines are
at most 79 columns wide.
In some options though, there is a list of possible values and their
meanings. In those lists, if the description of one value does not
fit in one line, then continuation lines should be indented. The
option manager and BFU are not currently able to do that. So, keep
the hard newlines in those lists, but rewrap them to 60 columns so
that they are less likely to require further wrapping at runtime.
2009-03-07 13:48:38 -05:00
|
|
|
N_("Automatically save a snapshot of all tabs periodically. "
|
|
|
|
"This will periodically bookmark the tabs of each terminal "
|
|
|
|
"in a separate folder for recovery after a crash.\n"
|
|
|
|
"\n"
|
2005-09-15 09:58:31 -04:00
|
|
|
"This feature requires bookmark support.")),
|
|
|
|
|
|
|
|
NULL_OPTION_INFO
|
|
|
|
};
|
|
|
|
|
|
|
|
static enum evhook_status bookmark_change_hook(va_list ap, void *data);
|
|
|
|
static enum evhook_status bookmark_write_hook(va_list ap, void *data);
|
|
|
|
|
|
|
|
struct event_hook_info bookmark_hooks[] = {
|
2022-01-19 16:49:13 -05:00
|
|
|
{ C_("bookmark-delete"), 0, bookmark_change_hook, NULL },
|
|
|
|
{ C_("bookmark-move"), 0, bookmark_change_hook, NULL },
|
|
|
|
{ C_("bookmark-update"), 0, bookmark_change_hook, NULL },
|
|
|
|
{ C_("periodic-saving"), 0, bookmark_write_hook, NULL },
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
NULL_EVENT_HOOK_INFO,
|
|
|
|
};
|
|
|
|
|
|
|
|
static enum evhook_status
|
|
|
|
bookmark_change_hook(va_list ap, void *data)
|
|
|
|
{
|
|
|
|
struct bookmark *bookmark = va_arg(ap, struct bookmark *);
|
|
|
|
|
|
|
|
if (bookmark == bm_snapshot_last_folder)
|
|
|
|
bm_snapshot_last_folder = NULL;
|
|
|
|
|
|
|
|
return EVENT_HOOK_STATUS_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bookmark_snapshot();
|
|
|
|
|
|
|
|
static enum evhook_status
|
|
|
|
bookmark_write_hook(va_list ap, void *data)
|
|
|
|
{
|
2007-08-28 12:41:18 -04:00
|
|
|
if (get_opt_bool("ui.sessions.snapshot", NULL)
|
2005-09-15 09:58:31 -04:00
|
|
|
&& !get_cmd_opt_bool("anonymous"))
|
|
|
|
bookmark_snapshot();
|
|
|
|
|
|
|
|
write_bookmarks();
|
|
|
|
|
|
|
|
return EVENT_HOOK_STATUS_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
change_hook_folder_state(struct session *ses, struct option *current,
|
|
|
|
struct option *changed)
|
|
|
|
{
|
|
|
|
if (!changed->value.number) {
|
|
|
|
/* We are to collapse all folders on exit; mark bookmarks dirty
|
|
|
|
* to ensure that this will happen. */
|
|
|
|
bookmarks_set_dirty();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
init_bookmarks(struct module *module)
|
|
|
|
{
|
2007-01-27 12:00:47 -05:00
|
|
|
static const struct change_hook_info bookmarks_change_hooks[] = {
|
2022-01-19 16:49:13 -05:00
|
|
|
{ C_("bookmarks.folder_state"), change_hook_folder_state },
|
2005-09-15 09:58:31 -04:00
|
|
|
{ NULL, NULL },
|
|
|
|
};
|
|
|
|
|
|
|
|
register_change_hooks(bookmarks_change_hooks);
|
|
|
|
|
|
|
|
read_bookmarks();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clears the bookmark list */
|
|
|
|
static void
|
2007-07-26 15:39:08 -04:00
|
|
|
free_bookmarks(LIST_OF(struct bookmark) *bookmarks_list,
|
|
|
|
LIST_OF(struct listbox_item) *box_items)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct bookmark *bm;
|
|
|
|
|
|
|
|
foreach (bm, *bookmarks_list) {
|
|
|
|
if (!list_empty(bm->child))
|
|
|
|
free_bookmarks(&bm->child, &bm->box_item->child);
|
|
|
|
mem_free(bm->title);
|
|
|
|
mem_free(bm->url);
|
|
|
|
}
|
|
|
|
|
|
|
|
free_list(*box_items);
|
|
|
|
free_list(*bookmarks_list);
|
2006-05-31 13:33:36 -04:00
|
|
|
if (bookmark_cache) free_hash(&bookmark_cache);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Does final cleanup and saving of bookmarks */
|
|
|
|
static void
|
|
|
|
done_bookmarks(struct module *module)
|
|
|
|
{
|
|
|
|
/* This is a clean shutdown, so delete the last snapshot. */
|
|
|
|
if (bm_snapshot_last_folder) delete_bookmark(bm_snapshot_last_folder);
|
|
|
|
bm_snapshot_last_folder = NULL;
|
|
|
|
|
|
|
|
write_bookmarks();
|
|
|
|
free_bookmarks(&bookmarks, &bookmark_browser.root.child);
|
|
|
|
free_last_searched_bookmark();
|
|
|
|
}
|
|
|
|
|
|
|
|
struct module bookmarks_module = struct_module(
|
|
|
|
/* name: */ N_("Bookmarks"),
|
|
|
|
/* options: */ bookmark_options_info,
|
|
|
|
/* hooks: */ bookmark_hooks,
|
|
|
|
/* submodules: */ NULL,
|
|
|
|
/* data: */ NULL,
|
|
|
|
/* init: */ init_bookmarks,
|
|
|
|
/* done: */ done_bookmarks
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Read/write wrappers */
|
|
|
|
|
|
|
|
/* Loads the bookmarks from file */
|
|
|
|
void
|
|
|
|
read_bookmarks(void)
|
|
|
|
{
|
|
|
|
bookmarks_read();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
write_bookmarks(void)
|
|
|
|
{
|
|
|
|
if (get_cmd_opt_bool("anonymous")) {
|
|
|
|
bookmarks_unset_dirty();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bookmarks_write(&bookmarks);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Bookmarks manipulation */
|
|
|
|
|
|
|
|
void
|
|
|
|
bookmarks_set_dirty(void)
|
|
|
|
{
|
|
|
|
bookmarks_dirty = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bookmarks_unset_dirty(void)
|
|
|
|
{
|
|
|
|
bookmarks_dirty = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
bookmarks_are_dirty(void)
|
|
|
|
{
|
|
|
|
return (bookmarks_dirty == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define check_bookmark_cache(url) (bookmark_cache && (url) && *(url))
|
|
|
|
|
|
|
|
static void
|
|
|
|
done_bookmark(struct bookmark *bm)
|
|
|
|
{
|
|
|
|
done_listbox_item(&bookmark_browser, bm->box_item);
|
|
|
|
|
|
|
|
mem_free(bm->title);
|
|
|
|
mem_free(bm->url);
|
|
|
|
mem_free(bm);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
delete_bookmark(struct bookmark *bm)
|
|
|
|
{
|
|
|
|
static int delete_bookmark_event_id = EVENT_NONE;
|
|
|
|
|
|
|
|
while (!list_empty(bm->child)) {
|
2022-01-24 13:29:32 -05:00
|
|
|
delete_bookmark((struct bookmark *)bm->child.next);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (check_bookmark_cache(bm->url)) {
|
|
|
|
struct hash_item *item;
|
|
|
|
|
|
|
|
item = get_hash_item(bookmark_cache, bm->url, strlen(bm->url));
|
|
|
|
if (item) del_hash_item(bookmark_cache, item);
|
|
|
|
}
|
|
|
|
|
|
|
|
set_event_id(delete_bookmark_event_id, "bookmark-delete");
|
|
|
|
trigger_event(delete_bookmark_event_id, bm);
|
|
|
|
|
|
|
|
del_from_list(bm);
|
|
|
|
bookmarks_set_dirty();
|
|
|
|
|
|
|
|
done_bookmark(bm);
|
|
|
|
}
|
|
|
|
|
2008-11-16 15:32:32 -05:00
|
|
|
/** Deletes any bookmarks with no URLs (i.e., folders) and of which
|
|
|
|
* the title matches the given argument.
|
|
|
|
*
|
|
|
|
* @param foldername
|
|
|
|
* The title of the folder, in UTF-8. */
|
2005-09-15 09:58:31 -04:00
|
|
|
static void
|
2021-01-02 10:20:27 -05:00
|
|
|
delete_folder_by_name(const char *foldername)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct bookmark *bookmark, *next;
|
|
|
|
|
|
|
|
foreachsafe (bookmark, next, bookmarks) {
|
|
|
|
if ((bookmark->url && *bookmark->url)
|
|
|
|
|| strcmp(bookmark->title, foldername))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
delete_bookmark(bookmark);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-04 05:40:44 -05:00
|
|
|
/** Allocate and initialize a bookmark in the given folder. This
|
|
|
|
* however does not set bookmark.box_item; use add_bookmark() for
|
|
|
|
* that.
|
|
|
|
*
|
|
|
|
* @param root
|
|
|
|
* The folder in which to add the bookmark, or NULL to add it at
|
|
|
|
* top level.
|
|
|
|
* @param title
|
|
|
|
* Title of the bookmark. Must be in UTF-8 and not NULL.
|
|
|
|
* "-" means add a separator.
|
|
|
|
* @param url
|
|
|
|
* URL to which the bookmark will point. Must be in UTF-8.
|
|
|
|
* NULL or "" means add a bookmark folder.
|
|
|
|
*
|
|
|
|
* @return the new bookmark, or NULL on error. */
|
2005-09-15 09:58:31 -04:00
|
|
|
static struct bookmark *
|
2021-01-02 10:20:27 -05:00
|
|
|
init_bookmark(struct bookmark *root, char *title, char *url)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct bookmark *bm;
|
|
|
|
|
2022-01-16 15:08:50 -05:00
|
|
|
bm = (struct bookmark *)mem_calloc(1, sizeof(*bm));
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!bm) return NULL;
|
|
|
|
|
|
|
|
bm->title = stracpy(title);
|
|
|
|
if (!bm->title) {
|
|
|
|
mem_free(bm);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
sanitize_title(bm->title);
|
|
|
|
|
|
|
|
bm->url = stracpy(empty_string_or_(url));
|
|
|
|
if (!bm->url) {
|
|
|
|
mem_free(bm->title);
|
|
|
|
mem_free(bm);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
sanitize_url(bm->url);
|
|
|
|
|
|
|
|
bm->root = root;
|
|
|
|
init_list(bm->child);
|
|
|
|
|
|
|
|
object_nolock(bm, "bookmark");
|
|
|
|
|
|
|
|
return bm;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
add_bookmark_item_to_bookmarks(struct bookmark *bm, struct bookmark *root, int place)
|
|
|
|
{
|
|
|
|
/* Actually add it */
|
|
|
|
if (place) {
|
|
|
|
if (root)
|
|
|
|
add_to_list_end(root->child, bm);
|
|
|
|
else
|
|
|
|
add_to_list_end(bookmarks, bm);
|
|
|
|
} else {
|
|
|
|
if (root)
|
|
|
|
add_to_list(root->child, bm);
|
|
|
|
else
|
|
|
|
add_to_list(bookmarks, bm);
|
|
|
|
}
|
|
|
|
bookmarks_set_dirty();
|
|
|
|
|
|
|
|
/* Hash creation if needed. */
|
|
|
|
if (!bookmark_cache)
|
2006-05-31 13:17:01 -04:00
|
|
|
bookmark_cache = init_hash8();
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
/* Create a new entry. */
|
|
|
|
if (check_bookmark_cache(bm->url))
|
|
|
|
add_hash_item(bookmark_cache, bm->url, strlen(bm->url), bm);
|
|
|
|
}
|
|
|
|
|
2009-01-04 05:40:44 -05:00
|
|
|
/** Add a bookmark to the bookmark list.
|
|
|
|
*
|
|
|
|
* @param root
|
|
|
|
* The folder in which to add the bookmark, or NULL to add it at
|
|
|
|
* top level.
|
|
|
|
* @param place
|
|
|
|
* 0 means add to the top. 1 means add to the bottom.
|
|
|
|
* @param title
|
|
|
|
* Title of the bookmark. Must be in UTF-8 and not NULL.
|
|
|
|
* "-" means add a separator.
|
|
|
|
* @param url
|
|
|
|
* URL to which the bookmark will point. Must be in UTF-8.
|
|
|
|
* NULL or "" means add a bookmark folder.
|
|
|
|
*
|
2008-10-19 18:08:20 -04:00
|
|
|
* @return the new bookmark, or NULL on error.
|
|
|
|
*
|
|
|
|
* @see add_bookmark_cp() */
|
2005-09-15 09:58:31 -04:00
|
|
|
struct bookmark *
|
2021-01-02 10:20:27 -05:00
|
|
|
add_bookmark(struct bookmark *root, int place, char *title,
|
|
|
|
char *url)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
enum listbox_item_type type;
|
|
|
|
struct bookmark *bm;
|
|
|
|
|
|
|
|
bm = init_bookmark(root, title, url);
|
|
|
|
if (!bm) return NULL;
|
|
|
|
|
|
|
|
if (url && *url) {
|
|
|
|
type = BI_LEAF;
|
|
|
|
} else if (title && title[0] == '-' && title[1] == '\0') {
|
|
|
|
type = BI_SEPARATOR;
|
|
|
|
} else {
|
|
|
|
type = BI_FOLDER;
|
|
|
|
}
|
|
|
|
|
|
|
|
bm->box_item = add_listbox_item(&bookmark_browser,
|
|
|
|
root ? root->box_item : NULL,
|
|
|
|
type,
|
|
|
|
(void *) bm,
|
|
|
|
place ? -1 : 1);
|
|
|
|
|
|
|
|
if (!bm->box_item) {
|
|
|
|
mem_free(bm->url);
|
|
|
|
mem_free(bm->title);
|
|
|
|
mem_free(bm);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_bookmark_item_to_bookmarks(bm, root, place);
|
|
|
|
|
|
|
|
return bm;
|
|
|
|
}
|
|
|
|
|
2008-10-19 18:08:20 -04:00
|
|
|
/** Add a bookmark to the bookmark list.
|
|
|
|
*
|
|
|
|
* @param root
|
|
|
|
* The folder in which to add the bookmark, or NULL to add it at
|
|
|
|
* top level.
|
|
|
|
* @param place
|
|
|
|
* 0 means add to the top. 1 means add to the bottom.
|
|
|
|
* @param codepage
|
|
|
|
* Codepage of @a title and @a url.
|
|
|
|
* @param title
|
|
|
|
* Title of the bookmark. Must not be NULL.
|
|
|
|
* "-" means add a separator.
|
|
|
|
* @param url
|
|
|
|
* URL to which the bookmark will point.
|
|
|
|
* NULL or "" means add a bookmark folder.
|
|
|
|
*
|
|
|
|
* @return the new bookmark.
|
|
|
|
*
|
|
|
|
* @see add_bookmark() */
|
|
|
|
struct bookmark *
|
|
|
|
add_bookmark_cp(struct bookmark *root, int place, int codepage,
|
2021-01-02 10:20:27 -05:00
|
|
|
char *title, char *url)
|
2008-10-19 18:08:20 -04:00
|
|
|
{
|
|
|
|
const int utf8_cp = get_cp_index("UTF-8");
|
|
|
|
struct conv_table *table;
|
2021-01-02 10:20:27 -05:00
|
|
|
char *utf8_title = NULL;
|
|
|
|
char *utf8_url = NULL;
|
2008-10-19 18:08:20 -04:00
|
|
|
struct bookmark *bookmark = NULL;
|
|
|
|
|
|
|
|
if (!url)
|
|
|
|
url = "";
|
|
|
|
|
|
|
|
table = get_translation_table(codepage, utf8_cp);
|
|
|
|
if (!table)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
utf8_title = convert_string(table, title, strlen(title),
|
|
|
|
utf8_cp, CSM_NONE,
|
|
|
|
NULL, NULL, NULL);
|
|
|
|
utf8_url = convert_string(table, url, strlen(url),
|
|
|
|
utf8_cp, CSM_NONE,
|
|
|
|
NULL, NULL, NULL);
|
|
|
|
if (utf8_title && utf8_url)
|
|
|
|
bookmark = add_bookmark(root, place,
|
|
|
|
utf8_title, utf8_url);
|
|
|
|
mem_free_if(utf8_title);
|
|
|
|
mem_free_if(utf8_url);
|
|
|
|
return bookmark;
|
|
|
|
}
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
/* Updates an existing bookmark.
|
|
|
|
*
|
|
|
|
* If there's any problem, return 0. Otherwise, return 1.
|
|
|
|
*
|
|
|
|
* If any of the fields are NULL, the value is left unchanged. */
|
|
|
|
int
|
2008-11-16 17:56:18 -05:00
|
|
|
update_bookmark(struct bookmark *bm, int codepage,
|
2021-01-02 10:20:27 -05:00
|
|
|
char *title, char *url)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
static int update_bookmark_event_id = EVENT_NONE;
|
2008-11-16 17:56:18 -05:00
|
|
|
const int utf8_cp = get_cp_index("UTF-8");
|
|
|
|
struct conv_table *table;
|
2021-01-02 10:20:27 -05:00
|
|
|
char *title2 = NULL;
|
|
|
|
char *url2 = NULL;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2008-11-16 17:56:18 -05:00
|
|
|
table = get_translation_table(codepage, utf8_cp);
|
|
|
|
if (!table)
|
|
|
|
return 0;
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
if (url) {
|
2008-11-16 17:56:18 -05:00
|
|
|
url2 = convert_string(table, url, strlen(url),
|
|
|
|
utf8_cp, CSM_NONE,
|
|
|
|
NULL, NULL, NULL);
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!url2) return 0;
|
|
|
|
sanitize_url(url2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (title) {
|
2008-11-16 17:56:18 -05:00
|
|
|
title2 = convert_string(table, title, strlen(title),
|
|
|
|
utf8_cp, CSM_NONE,
|
|
|
|
NULL, NULL, NULL);
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!title2) {
|
|
|
|
mem_free_if(url2);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
sanitize_title(title2);
|
|
|
|
}
|
|
|
|
|
|
|
|
set_event_id(update_bookmark_event_id, "bookmark-update");
|
|
|
|
trigger_event(update_bookmark_event_id, bm, title2, url2);
|
|
|
|
|
|
|
|
if (title2) {
|
2006-06-05 23:06:47 -04:00
|
|
|
mem_free_set(&bm->title, title2);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (url2) {
|
|
|
|
if (check_bookmark_cache(bm->url)) {
|
|
|
|
struct hash_item *item;
|
|
|
|
int len = strlen(bm->url);
|
|
|
|
|
|
|
|
item = get_hash_item(bookmark_cache, bm->url, len);
|
|
|
|
if (item) del_hash_item(bookmark_cache, item);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (check_bookmark_cache(url2)) {
|
|
|
|
add_hash_item(bookmark_cache, url2, strlen(url2), bm);
|
|
|
|
}
|
|
|
|
|
2006-06-05 23:06:47 -04:00
|
|
|
mem_free_set(&bm->url, url2);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bookmarks_set_dirty();
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-10-19 18:09:45 -04:00
|
|
|
/** Search for a bookmark with the given title. The search does not
|
|
|
|
* recurse into subfolders.
|
|
|
|
*
|
|
|
|
* @param folder
|
|
|
|
* Search in this folder. NULL means search in the root.
|
|
|
|
*
|
|
|
|
* @param title
|
|
|
|
* Search for this title. Must be in UTF-8 and not NULL.
|
|
|
|
*
|
|
|
|
* @return The bookmark, or NULL if not found. */
|
2005-12-29 02:05:31 -05:00
|
|
|
struct bookmark *
|
2021-01-02 10:20:27 -05:00
|
|
|
get_bookmark_by_name(struct bookmark *folder, char *title)
|
2005-12-29 02:05:31 -05:00
|
|
|
{
|
|
|
|
struct bookmark *bookmark;
|
2007-07-26 15:39:08 -04:00
|
|
|
LIST_OF(struct bookmark) *lh;
|
2005-12-29 02:05:31 -05:00
|
|
|
|
|
|
|
lh = folder ? &folder->child : &bookmarks;
|
|
|
|
|
|
|
|
foreach (bookmark, *lh)
|
|
|
|
if (!strcmp(bookmark->title, title)) return bookmark;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
/* Search bookmark cache for item matching url. */
|
|
|
|
struct bookmark *
|
2021-01-02 10:20:27 -05:00
|
|
|
get_bookmark(char *url)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct hash_item *item;
|
|
|
|
|
2008-10-19 18:06:36 -04:00
|
|
|
/** @todo Bug 1066: URLs in bookmark_cache should be UTF-8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!check_bookmark_cache(url))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Search for cached entry. */
|
|
|
|
|
|
|
|
item = get_hash_item(bookmark_cache, url, strlen(url));
|
|
|
|
|
2022-01-24 13:29:32 -05:00
|
|
|
return (struct bookmark *)(item ? item->value : NULL);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bookmark_terminal(struct terminal *term, struct bookmark *folder)
|
|
|
|
{
|
2021-01-02 10:20:27 -05:00
|
|
|
char title[MAX_STR_LEN], url[MAX_STR_LEN];
|
2005-09-15 09:58:31 -04:00
|
|
|
struct window *tab;
|
2008-11-16 15:32:32 -05:00
|
|
|
int term_cp = get_terminal_codepage(term);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
foreachback_tab (tab, term->windows) {
|
2022-01-24 13:29:32 -05:00
|
|
|
struct session *ses = (struct session *)tab->data;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (!get_current_url(ses, url, MAX_STR_LEN))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!get_current_title(ses, title, MAX_STR_LEN))
|
|
|
|
continue;
|
|
|
|
|
2008-11-16 15:32:32 -05:00
|
|
|
add_bookmark_cp(folder, 1, term_cp, title, url);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-16 15:32:32 -05:00
|
|
|
/** Create a bookmark for each document on the specified terminal,
|
|
|
|
* and a folder to contain those bookmarks.
|
|
|
|
*
|
|
|
|
* @param term
|
|
|
|
* The terminal whose open documents should be bookmarked.
|
|
|
|
*
|
|
|
|
* @param foldername
|
|
|
|
* The name of the new bookmark folder, in UTF-8. */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
2021-01-02 10:20:27 -05:00
|
|
|
bookmark_terminal_tabs(struct terminal *term, char *foldername)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct bookmark *folder = add_bookmark(NULL, 1, foldername, NULL);
|
|
|
|
|
|
|
|
if (!folder) return;
|
|
|
|
|
|
|
|
bookmark_terminal(term, folder);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bookmark_all_terminals(struct bookmark *folder)
|
|
|
|
{
|
|
|
|
unsigned int n = 0;
|
|
|
|
struct terminal *term;
|
|
|
|
|
|
|
|
if (get_cmd_opt_bool("anonymous"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (list_is_singleton(terminals)) {
|
2022-01-24 13:29:32 -05:00
|
|
|
bookmark_terminal((struct terminal *)terminals.next, folder);
|
2005-09-15 09:58:31 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (term, terminals) {
|
2021-11-22 13:04:00 -05:00
|
|
|
char subfoldername[5];
|
2005-09-15 09:58:31 -04:00
|
|
|
struct bookmark *subfolder;
|
|
|
|
|
2021-11-22 13:04:00 -05:00
|
|
|
if (ulongcat(subfoldername, NULL, n, sizeof(subfoldername)-1, 0)
|
|
|
|
>= sizeof(subfoldername)-1)
|
2005-09-15 09:58:31 -04:00
|
|
|
return;
|
|
|
|
|
|
|
|
++n;
|
|
|
|
|
2009-01-04 05:40:44 -05:00
|
|
|
/* Because subfoldername[] contains only digits,
|
|
|
|
* it is OK as UTF-8. */
|
2005-09-15 09:58:31 -04:00
|
|
|
subfolder = add_bookmark(folder, 1, subfoldername, NULL);
|
|
|
|
if (!subfolder) return;
|
|
|
|
|
|
|
|
bookmark_terminal(term, subfolder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-02 10:20:27 -05:00
|
|
|
char *
|
2008-11-16 15:32:32 -05:00
|
|
|
get_auto_save_bookmark_foldername_utf8(void)
|
|
|
|
{
|
2021-01-02 10:20:27 -05:00
|
|
|
char *foldername;
|
2008-11-16 15:32:32 -05:00
|
|
|
int from_cp, to_cp;
|
|
|
|
struct conv_table *convert_table;
|
|
|
|
|
2009-02-08 15:02:57 -05:00
|
|
|
foldername = get_opt_str("ui.sessions.auto_save_foldername", NULL);
|
2008-11-16 15:32:32 -05:00
|
|
|
if (!*foldername) return NULL;
|
|
|
|
|
|
|
|
/* The charset of the string returned by get_opt_str()
|
|
|
|
* seems to be documented nowhere. Let's assume it is
|
|
|
|
* the system charset. */
|
|
|
|
from_cp = get_cp_index("System");
|
|
|
|
to_cp = get_cp_index("UTF-8");
|
|
|
|
convert_table = get_translation_table(from_cp, to_cp);
|
|
|
|
if (!convert_table) return NULL;
|
|
|
|
|
|
|
|
return convert_string(convert_table,
|
|
|
|
foldername, strlen(foldername),
|
|
|
|
to_cp, CSM_NONE,
|
|
|
|
NULL, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
|
|
|
bookmark_auto_save_tabs(struct terminal *term)
|
|
|
|
{
|
2021-01-02 10:20:27 -05:00
|
|
|
char *foldername; /* UTF-8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (get_cmd_opt_bool("anonymous")
|
2007-08-28 12:41:18 -04:00
|
|
|
|| !get_opt_bool("ui.sessions.auto_save", NULL))
|
2005-09-15 09:58:31 -04:00
|
|
|
return;
|
|
|
|
|
2008-11-16 15:32:32 -05:00
|
|
|
foldername = get_auto_save_bookmark_foldername_utf8();
|
|
|
|
if (!foldername) return;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
/* Ensure uniqueness of the auto save folder, so it is possible to
|
|
|
|
* restore the (correct) session when starting up. */
|
|
|
|
delete_folder_by_name(foldername);
|
|
|
|
|
|
|
|
bookmark_terminal_tabs(term, foldername);
|
2008-11-16 15:32:32 -05:00
|
|
|
mem_free(foldername);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-25 12:51:21 -05:00
|
|
|
bookmark_snapshot(void)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
2019-04-21 06:27:40 -04:00
|
|
|
struct string folderstring;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct bookmark *folder;
|
|
|
|
|
|
|
|
if (!init_string(&folderstring)) return;
|
|
|
|
|
|
|
|
add_to_string(&folderstring, "Session snapshot");
|
|
|
|
|
|
|
|
#ifdef HAVE_STRFTIME
|
|
|
|
add_to_string(&folderstring, " - ");
|
2007-08-28 12:41:18 -04:00
|
|
|
add_date_to_string(&folderstring, get_opt_str("ui.date_format", NULL),
|
|
|
|
NULL);
|
2005-09-15 09:58:31 -04:00
|
|
|
#endif
|
|
|
|
|
2008-11-16 15:32:32 -05:00
|
|
|
/* folderstring must be in the system codepage because
|
|
|
|
* add_date_to_string() uses strftime(). */
|
|
|
|
folder = add_bookmark_cp(NULL, 1, get_cp_index("System"),
|
|
|
|
folderstring.source, NULL);
|
2005-09-15 09:58:31 -04:00
|
|
|
done_string(&folderstring);
|
|
|
|
if (!folder) return;
|
|
|
|
|
|
|
|
bookmark_all_terminals(folder);
|
|
|
|
|
|
|
|
if (bm_snapshot_last_folder) delete_bookmark(bm_snapshot_last_folder);
|
|
|
|
bm_snapshot_last_folder = folder;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-16 15:32:32 -05:00
|
|
|
/** Open all bookmarks from the named folder.
|
|
|
|
*
|
|
|
|
* @param ses
|
|
|
|
* The session in which to open the first bookmark. The other
|
|
|
|
* bookmarks of the folder open in new tabs on the same terminal.
|
|
|
|
*
|
|
|
|
* @param foldername
|
|
|
|
* The name of the bookmark folder, in UTF-8. */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
2021-01-02 10:20:27 -05:00
|
|
|
open_bookmark_folder(struct session *ses, char *foldername)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct bookmark *bookmark;
|
|
|
|
struct bookmark *folder = NULL;
|
|
|
|
struct bookmark *current = NULL;
|
|
|
|
|
|
|
|
assert(foldername && ses);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
foreach (bookmark, bookmarks) {
|
|
|
|
if (bookmark->box_item->type != BI_FOLDER)
|
|
|
|
continue;
|
|
|
|
if (strcmp(bookmark->title, foldername))
|
|
|
|
continue;
|
|
|
|
folder = bookmark;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!folder) return;
|
|
|
|
|
|
|
|
foreach (bookmark, folder->child) {
|
|
|
|
struct uri *uri;
|
|
|
|
|
|
|
|
if (bookmark->box_item->type == BI_FOLDER
|
|
|
|
|| bookmark->box_item->type == BI_SEPARATOR
|
|
|
|
|| !*bookmark->url)
|
|
|
|
continue;
|
|
|
|
|
2008-10-19 18:06:36 -04:00
|
|
|
/** @todo Bug 1066: Tell the URI layer that
|
|
|
|
* bookmark->url is UTF-8. */
|
2005-11-24 09:38:47 -05:00
|
|
|
uri = get_translated_uri(bookmark->url, NULL);
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!uri) continue;
|
|
|
|
|
|
|
|
/* Save the first bookmark for the current tab */
|
|
|
|
if (!current) {
|
|
|
|
current = bookmark;
|
|
|
|
goto_uri(ses, uri);
|
|
|
|
} else {
|
|
|
|
open_uri_in_new_tab(ses, uri, 1, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
done_uri(uri);
|
|
|
|
}
|
|
|
|
}
|