Hello community,

here is the log from the commit of package vimb for openSUSE:Factory checked in 
at 2019-07-30 12:39:16
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/vimb (Old)
 and      /work/SRC/openSUSE:Factory/.vimb.new.4126 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "vimb"

Tue Jul 30 12:39:16 2019 rev:3 rq:719740 version:3.5.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/vimb/vimb.changes        2019-03-27 
16:22:04.635521340 +0100
+++ /work/SRC/openSUSE:Factory/.vimb.new.4126/vimb.changes      2019-07-30 
12:39:21.378934067 +0200
@@ -1,0 +2,19 @@
+Tue Jul 30 04:41:40 UTC 2019 - [email protected]
+
+- Update to 3.5.0:
+  Added:
+  * Add external download command #543 #348.
+  * Added ephemeral mode by new option --incognito#562.
+  Changed:
+  * Hinting shows the current focused elements URI in the statusbar.
+  * Show error if printing with :hardcopy fails #564.
+  Fixed:
+  * Fixed compilation if source is not in a git repo (Thanks to Patrick 
Steinhardt).
+  * Fixed partial hidden hint labels on top of screen.
+  * Fix segfault on open in new tabe from context menu #556.
+  * Fix "... (null)" shown in title during url sanitization.
+  Removed:
+  * Setting private-browsing was removed in favor of --incognito option.
+- Remove vim-3.4.0-fixbuild.patch: included upstream
+
+-------------------------------------------------------------------

Old:
----
  3.4.0.tar.gz
  vim-3.4.0-fixbuild.patch

New:
----
  3.5.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ vimb.spec ++++++
--- /var/tmp/diff_new_pack.LcUHzd/_old  2019-07-30 12:39:22.254933921 +0200
+++ /var/tmp/diff_new_pack.LcUHzd/_new  2019-07-30 12:39:22.254933921 +0200
@@ -17,15 +17,13 @@
 
 
 Name:           vimb
-Version:        3.4.0
+Version:        3.5.0
 Release:        0
 Summary:        The vim-like browser
 License:        GPL-3.0-or-later
 Group:          Productivity/Networking/Web/Browsers
 URL:            https://fanglingsu.github.io/vimb/
 Source:         https://github.com/fanglingsu/vimb/archive/%{version}.tar.gz
-# FIX-UPSTREAM - [email protected] - https://github.com/fanglingsu/vimb/pull/552
-Patch0:         vim-3.4.0-fixbuild.patch
 BuildRequires:  gtk3-devel
 BuildRequires:  webkit2gtk3-devel >= 2.20
 
@@ -35,7 +33,6 @@
 
 %prep
 %setup -q
-%patch0 -p1
 
 %build
 make %{?_smp_mflags}

++++++ 3.4.0.tar.gz -> 3.5.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/CHANGELOG.md new/vimb-3.5.0/CHANGELOG.md
--- old/vimb-3.4.0/CHANGELOG.md 2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/CHANGELOG.md 2019-07-29 22:03:20.000000000 +0200
@@ -11,6 +11,21 @@
 ### Fixed
 ### Removed
 
+## [3.5.0] - 2019-07-29
+### Added
+* Add external download command #543 #348.
+* Added ephemeral mode by new option `--incognito` #562.
+### Changed
+* Hinting shows the current focused elements URI in the statusbar.
+* Show error if printing with `:hardcopy` fails #564.
+### Fixed
+* Fixed compilation if source is not in a git repo (Thanks to Patrick 
Steinhardt).
+* Fixed partial hidden hint labels on top of screen.
+* Fix segfault on open in new tabe from context menu #556.
+* Fix "... (null)" shown in title during url sanitization.
+### Removed
+* Setting `private-browsing` was removed in favor of `--incognito` option.
+
 ## [3.4.0] - 2019-03-26
 ### Added
 * Allow to show video in fullscreen, without statusbar and inputbox, if 
requested.
@@ -241,7 +256,8 @@
   cookie file
 * Fixed none POSIX `echo -n` call
 
-[Unreleased]: https://github.com/fanglingsu/vimb/compare/3.3.0...master
+[Unreleased]: https://github.com/fanglingsu/vimb/compare/3.5.0...master
+[3.5.0]: https://github.com/fanglingsu/vimb/compare/3.4.0...3.5.0
 [3.4.0]: https://github.com/fanglingsu/vimb/compare/3.3.0...3.4.0
 [3.3.0]: https://github.com/fanglingsu/vimb/compare/3.2.0...3.3.0
 [3.2.0]: https://github.com/fanglingsu/vimb/compare/3.1.0...3.2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/Makefile new/vimb-3.5.0/Makefile
--- old/vimb-3.4.0/Makefile     2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/Makefile     2019-07-29 22:03:20.000000000 +0200
@@ -1,9 +1,9 @@
-version = 3.4.0
+version = 3.5.0
 include config.mk
 
 all: version.h src.subdir-all
 
-version.h: Makefile .git/index
+version.h: Makefile $(wildcard .git/index)
        @echo "create $@"
        $(Q)v="$$(git describe --tags 2>/dev/null)"; \
        echo "#define VERSION \"$${v:-$(version)}\"" > $@
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/doc/vimb.1 new/vimb-3.5.0/doc/vimb.1
--- old/vimb-3.4.0/doc/vimb.1   2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/doc/vimb.1   2019-07-29 22:03:20.000000000 +0200
@@ -37,6 +37,9 @@
 .I WINID
 of an XEmbed-aware application, that Vimb will use as its parent.
 .TP
+.B "\-i, \-\-incognito"
+Start an instance with user data read-only (see \fIFILES\fP section).
+.TP
 .B "\-h, \-\-help"
 Show help options.
 .TP
@@ -836,7 +839,7 @@
 .RE
 .TP
 .BI ":sh[ellcmd]! " cmd
-Like :sh[ellcmd], but asyncron.
+Like :sh[ellcmd] but asynchronous.
 .sp
 Example:
 .EX
@@ -1044,6 +1047,29 @@
 .B dns-prefetching (bool)
 Indicates if Vimb prefetches domain names.
 .TP
+.B download-command (string)
+A command with placeholder '%s' that will be invoked to download a URI in
+case 'download-use-external' is enabled.
+.RS
+.TP
+The following additional environment variable are available:
+.PD 0
+.TP
+.B $VIMB_URI
+The URI of the current opened page, normally the page where the download was
+started from, also known as referer.
+.TP
+.B $VIMB_DOWNLOAD_PATH
+Setting value of 'download-path' which would be used normally for downloads.
+.PD
+.PP
+Example:
+.PD 0
+.IP ":set download-command=/bin/sh -c ""cd '$VIMB_DOWNLOAD_PATH' \
+&& curl -sLJOC - -e '$VIMB_URI' %s"""
+.PD
+.RE
+.TP
 .B download-path (string)
 Path to the default download directory.
 If no download directory is set, download will be written into current
@@ -1051,6 +1077,11 @@
 The following pattern will be expanded if the download is
 started '~/', '~user', '$VAR' and '${VAR}'.
 .TP
+.B download-use-external (bool)
+Indicates if the external download tool set as 'download-command' should be
+used to handle downloads.
+If this is disabled Vimb will handle the download.
+.TP
 .B editor-command (string)
 Command with placeholder '%s' called if form field is opened with $EDITOR to
 spawn the editor-like `x-terminal-emulator -e vim %s'.
@@ -1149,6 +1180,7 @@
 .B history-max-items (int)
 Maximum number of unique items stored in search-, command or URI history.
 If history-max-items is set to 0, the history file will not be changed.
+This setting has no effect if option \-\-incognito is set.
 .TP
 .B home-page (string)
 Homepage that vimb opens if started without a URI.
@@ -1225,12 +1257,6 @@
 .B print-backgrounds (bool)
 Whether background images should be drawn during printing.
 .TP
-.B private-browsing (bool)
-Whether to enable private browsing mode.
-This suppresses  printing of messages into JavaScript Console.
-At the time this is the only way to force WebKit to
-not allow a page to store data in the windows sessionStorage.
-.TP
 .B plugins (bool)
 Determines whether or not plugins on the page are enabled.
 .TP
@@ -1361,22 +1387,30 @@
 .TP
 .I cookies.db
 Sqlite cookie storage.
+This file will not be touched if option \-\-incognito is set.
 .TP
 .I closed
 Holds the URIs of last closed browser windows.
+This file will not be touched if option \-\-incognito is set.
 .TP
 .I history
 This file holds the history of unique opened URIs.
+This file will not be touched if option \-\-incognito is set.
+.TP
+.I bookmark
+This file holds the list of bookmarked URIs with tags.
 .TP
 .I command
 This file holds the history of commands and search queries performed via input
 box.
+This file will not be touched if option \-\-incognito is set.
 .TP
 .I queue
 Holds the read it later queue filled by `qpush'.
 .TP
 .I search
 This file holds the history of search queries.
+This file will not be touched if option \-\-incognito is set.
 .TP
 .I scripts.js
 This file can be used to run user scripts, that are injected into every page
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/bookmark.c 
new/vimb-3.5.0/src/bookmark.c
--- old/vimb-3.4.0/src/bookmark.c       2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/src/bookmark.c       2019-07-29 22:03:20.000000000 +0200
@@ -236,7 +236,12 @@
 
 static GList *load(const char *file)
 {
-    return util_file_to_unique_list(file, (Util_Content_Func)line_to_bookmark, 
0);
+    char **lines;
+    GList *list;
+    lines = util_get_lines(file);
+    list  = util_strv_to_unique_list(lines, 
(Util_Content_Func)line_to_bookmark, 0);
+    g_strfreev(lines);
+    return list;
 }
 
 /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/ex.c new/vimb-3.5.0/src/ex.c
--- old/vimb-3.4.0/src/ex.c     2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/src/ex.c     2019-07-29 22:03:20.000000000 +0200
@@ -140,6 +140,7 @@
 static void on_eval_script_finished(GDBusProxy *proxy, GAsyncResult *result, 
Client *c);
 static VbCmdResult ex_clearcache(Client *c, const ExArg *arg);
 static VbCmdResult ex_hardcopy(Client *c, const ExArg *arg);
+static void print_failed_cb(WebKitPrintOperation* op, GError *err, Client *c);
 static VbCmdResult ex_map(Client *c, const ExArg *arg);
 static VbCmdResult ex_unmap(Client *c, const ExArg *arg);
 static VbCmdResult ex_normal(Client *c, const ExArg *arg);
@@ -858,12 +859,17 @@
     return CMD_SUCCESS;
 }
 
+/**
+ * Opens the gtk print dialog.
+ */
 static VbCmdResult ex_hardcopy(Client *c, const ExArg *arg)
 {
     WebKitPrintOperation *op   = webkit_print_operation_new(c->webview);
     GtkPrintSettings *settings = gtk_print_settings_new();
-
     gtk_print_settings_set(settings, GTK_PRINT_SETTINGS_OUTPUT_BASENAME, 
c->state.title);
+
+    g_signal_connect(op, "failed", G_CALLBACK(print_failed_cb), c);
+
     webkit_print_operation_set_print_settings(op, settings);
     webkit_print_operation_run_dialog(op, NULL);
     g_object_unref(op);
@@ -872,6 +878,14 @@
     return CMD_SUCCESS;
 }
 
+/**
+ * Callback called when printing failed.
+ */
+static void print_failed_cb(WebKitPrintOperation* op, GError *err, Client *c)
+{
+    vb_echo(c, MSG_ERROR, FALSE, "print failed: %s", err->message);
+}
+
 static VbCmdResult ex_map(Client *c, const ExArg *arg)
 {
     if (!arg->lhs->len || !arg->rhs->len) {
@@ -956,7 +970,7 @@
 
     return command_queue(c, &a)
         ? CMD_SUCCESS | CMD_KEEPINPUT
-        : CMD_ERROR | CMD_KEEPINPUT;
+        : CMD_ERROR;
 }
 #endif
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/file-storage.c 
new/vimb-3.5.0/src/file-storage.c
--- old/vimb-3.4.0/src/file-storage.c   1970-01-01 01:00:00.000000000 +0100
+++ new/vimb-3.5.0/src/file-storage.c   2019-07-29 22:03:20.000000000 +0200
@@ -0,0 +1,151 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2019 Daniel Carl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#include <glib.h>
+#include <stdio.h>
+#include <sys/file.h>
+#include <glib/gstdio.h>
+
+#include "file-storage.h"
+
+struct filestorage {
+    char        *file_path;
+    gboolean    readonly;
+    GString     *str;
+};
+
+/**
+ * Create new file storage instance for given directory and filename. If the
+ * file does not exists in the directory and give mode is not 0 the file is
+ * created with the given mode.
+ *
+ * The returned FileStorage must be freed by file_storage_free().
+ *
+ * @dir:        Directory in which the file is searched.
+ * @filename:   Filename to built the absolute path with.
+ * @mode:       Mode (file permission as chmod(2)) used for the file when
+ *              creating it. If 0 the file is not created and the storage is
+ *              used in read only mode - no data written to the file.
+ */
+FileStorage *file_storage_new(const char *dir, const char *filename, gboolean 
readonly)
+{
+    FileStorage *storage;
+
+    storage            = g_slice_new(FileStorage);
+    storage->readonly  = readonly;
+    storage->file_path = g_build_filename(dir, filename, NULL);
+
+    /* Use gstring as storage in case when the file is used read only. */
+    if (storage->readonly) {
+        storage->str = g_string_new(NULL);
+    } else {
+        storage->str = NULL;
+    }
+
+    return storage;
+}
+
+/**
+ * Free memory for given file storage.
+ */
+void file_storage_free(FileStorage *storage)
+{
+    if (storage) {
+        g_free(storage->file_path);
+        if (storage->str) {
+            g_string_free(storage->str, TRUE);
+        }
+        g_slice_free(FileStorage, storage);
+    }
+}
+
+/**
+ * Append new data to file.
+ *
+ * @fileStorage: FileStorage to append the data to
+ * @format: Format string used to process va_list
+ */
+gboolean file_storage_append(FileStorage *storage, const char *format, ...)
+{
+    FILE *f;
+    va_list args;
+
+    g_assert(storage);
+
+    /* Write data to in memory list in case the file storage is read only. */
+    if (storage->readonly) {
+        va_start(args, format);
+        g_string_append_vprintf(storage->str, format, args);
+        va_end(args);
+        return TRUE;
+    }
+    if ((f = fopen(storage->file_path, "a+"))) {
+        flock(fileno(f), LOCK_EX);
+        va_start(args, format);
+        vfprintf(f, format, args);
+        va_end(args);
+        flock(fileno(f), LOCK_UN);
+        fclose(f);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/**
+ * Retrieves all the lines from file storage.
+ *
+ * The result have to be freed by g_strfreev().
+ */
+char **file_storage_get_lines(FileStorage *storage)
+{
+    char *fullcontent = NULL;
+    char *content     = NULL;
+    char **lines      = NULL;
+
+    g_file_get_contents(storage->file_path, &content, NULL, NULL);
+
+    if (storage->str && storage->str->len) {
+        if (content) {
+            fullcontent = g_strconcat(content, storage->str->str, NULL);
+            lines       = g_strsplit(fullcontent, "\n", -1);
+            g_free(fullcontent);
+        } else {
+            lines = g_strsplit(storage->str->str, "\n", -1);
+        }
+    } else {
+        lines = g_strsplit(content ? content : "", "\n", -1);
+    }
+
+    if (content) {
+        g_free(content);
+    }
+
+    return lines;
+}
+
+const char *file_storage_get_path(FileStorage *storage)
+{
+    return storage->file_path;
+}
+
+gboolean file_storage_is_readonly(FileStorage *storage)
+{
+    return storage->readonly;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/file-storage.h 
new/vimb-3.5.0/src/file-storage.h
--- old/vimb-3.4.0/src/file-storage.h   1970-01-01 01:00:00.000000000 +0100
+++ new/vimb-3.5.0/src/file-storage.h   2019-07-29 22:03:20.000000000 +0200
@@ -0,0 +1,33 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2019 Daniel Carl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#ifndef _FILE_STORAGE_H
+#define _FILE_STORAGE_H
+
+#include <glib.h>
+
+typedef struct filestorage FileStorage;
+FileStorage *file_storage_new(const char *dir, const char *filename, int mode);
+void file_storage_free(FileStorage *storage);
+gboolean file_storage_append(FileStorage *storage, const char *format, ...);
+char **file_storage_get_lines(FileStorage *storage);
+const char *file_storage_get_path(FileStorage *storage);
+gboolean file_storage_is_readonly(FileStorage *storage);
+
+#endif /* end of include guard: _FILE_STORAGE_H */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/hints.c new/vimb-3.5.0/src/hints.c
--- old/vimb-3.4.0/src/hints.c  2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/src/hints.c  2019-07-29 22:03:20.000000000 +0200
@@ -308,16 +308,28 @@
     char *value = NULL;
 
     if (!return_value) {
-        return FALSE;
+        goto error;
     }
 
     g_variant_get(return_value, "(bs)", &success, &value);
     if (!success || !strncmp(value, "ERROR:", 6)) {
-        return FALSE;
+        goto error;
     }
-
-    /* following return values mark fired hints */
-    if (!strncmp(value, "DONE:", 5)) {
+    if (!strncmp(value, "OVER:", 5)) {
+        /* If focused elements src is given fire mouse-target-changed signal
+         * to show its uri in the statusbar. */
+        if (*(value + 7)) {
+            /* We get OVER:{I,A}:element-url so we use byte 6 to check for the
+             * hinted element type image I or link A. */
+            if (*(value + 5) == 'I') {
+                vb_statusbar_show_hover_url(c, LINK_TYPE_IMAGE, value + 7);
+            } else {
+                vb_statusbar_show_hover_url(c, LINK_TYPE_LINK, value + 7);
+            }
+        } else {
+            goto error;
+        }
+    } else if (!strncmp(value, "DONE:", 5)) {
         fire_timeout(c, FALSE);
         /* Change to normal mode only if we are currently in command mode and
          * we are not in g-mode hinting. This is required to not switch to
@@ -397,6 +409,10 @@
     }
 
     return TRUE;
+
+error:
+    vb_statusbar_show_hover_url(c, LINK_TYPE_NONE, NULL);
+    return FALSE;
 }
 
 static void fire_timeout(Client *c, gboolean on)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/history.c new/vimb-3.5.0/src/history.c
--- old/vimb-3.4.0/src/history.c        2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/src/history.c        2019-07-29 22:03:20.000000000 +0200
@@ -27,8 +27,9 @@
 #include "history.h"
 #include "main.h"
 #include "util.h"
+#include "file-storage.h"
 
-#define HIST_FILE(t) (vb.files[file_map[t]])
+#define HIST_STORAGE(t) (vb.storage[storage_map[t]])
 typedef struct {
     char *first;
     char *second;
@@ -37,14 +38,14 @@
 static gboolean history_item_contains_all_tags(History *item, char **query, 
guint qlen);
 static void free_history(History *item);
 static History *line_to_history(const char *uri, const char *title);
-static GList *load(const char *file);
+static GList *load(FileStorage *s);
 static void write_to_file(GList *list, const char *file);
 
 /* map history types to files */
-static const int file_map[HISTORY_LAST] = {
-    FILES_COMMAND,
-    FILES_SEARCH,
-    FILES_HISTORY
+static const int storage_map[HISTORY_LAST] = {
+    STORAGE_COMMAND,
+    STORAGE_SEARCH,
+    STORAGE_HISTORY
 };
 extern struct Vimb vb;
 
@@ -53,18 +54,18 @@
  */
 void history_add(Client *c, HistoryType type, const char *value, const char 
*additional)
 {
-    const char *file;
+    FileStorage *s;
 
     /* Don't write a history entry if the history max size is set to 0. */
     if (!vb.config.history_max) {
         return;
     }
 
-    file = HIST_FILE(type);
+    s = HIST_STORAGE(type);
     if (additional) {
-        util_file_append(file, "%s\t%s\n", value, additional);
+        file_storage_append(s, "%s\t%s\n", value, additional);
     } else {
-        util_file_append(file, "%s\n", value);
+        file_storage_append(s, "%s\n", value);
     }
 }
 
@@ -74,7 +75,7 @@
  */
 void history_cleanup(void)
 {
-    const char *file;
+    FileStorage *s;
     GList *list;
 
     /* don't cleanup the history file if history max size is 0 */
@@ -83,10 +84,12 @@
     }
 
     for (HistoryType i = HISTORY_FIRST; i < HISTORY_LAST; i++) {
-        file = HIST_FILE(i);
-        list = load(file);
-        write_to_file(list, file);
-        g_list_free_full(list, (GDestroyNotify)free_history);
+        s = HIST_STORAGE(i);
+        if (!file_storage_is_readonly(s)) {
+            list = load(s);
+            write_to_file(list, file_storage_get_path(s));
+            g_list_free_full(list, (GDestroyNotify)free_history);
+        }
     }
 }
 
@@ -99,7 +102,7 @@
     GtkTreeIter iter;
     History *item;
 
-    src = load(HIST_FILE(type));
+    src = load(HIST_STORAGE(type));
     src = g_list_reverse(src);
     if (!input || !*input) {
         /* without any tags return all items */
@@ -168,12 +171,12 @@
 
     switch (type) {
         case INPUT_COMMAND:
-            src = load(HIST_FILE(HISTORY_COMMAND));
+            src = load(HIST_STORAGE(HISTORY_COMMAND));
             break;
 
         case INPUT_SEARCH_FORWARD:
         case INPUT_SEARCH_BACKWARD:
-            src = load(HIST_FILE(HISTORY_SEARCH));
+            src = load(HIST_STORAGE(HISTORY_SEARCH));
             break;
 
         default:
@@ -241,10 +244,12 @@
  *
  * Returned list must be freed with (GDestroyNotify)free_history.
  */
-static GList *load(const char *file)
+static GList *load(FileStorage *s)
 {
-    return util_file_to_unique_list(
-        file, (Util_Content_Func)line_to_history, vb.config.history_max
+    return util_strv_to_unique_list(
+        file_storage_get_lines(s),
+        (Util_Content_Func)line_to_history,
+        vb.config.history_max
     );
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/main.c new/vimb-3.5.0/src/main.c
--- old/vimb-3.4.0/src/main.c   2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/src/main.c   2019-07-29 22:03:20.000000000 +0200
@@ -46,6 +46,7 @@
 #include "shortcut.h"
 #include "util.h"
 #include "autocmd.h"
+#include "file-storage.h"
 
 static void client_destroy(Client *c);
 static Client *client_new(WebKitWebView *webview);
@@ -63,6 +64,9 @@
 static void on_webctx_init_web_extension(WebKitWebContext *webctx, gpointer 
data);
 static gboolean on_webdownload_decide_destination(WebKitDownload *download,
         gchar *suggested_filename, Client *c);
+static void on_webdownload_response_received(WebKitDownload *download,
+        GParamSpec *ps, Client *c);
+static void spawn_download_command(Client *c, WebKitURIResponse *response);
 static void on_webdownload_failed(WebKitDownload *download,
         GError *error, Client *c);
 static void on_webdownload_finished(WebKitDownload *download, Client *c);
@@ -639,6 +643,39 @@
 }
 
 /**
+ * Show the given url on the left of statusbar.
+ */
+void vb_statusbar_show_hover_url(Client *c, VbLinkType type, const char *uri)
+{
+    char *sanitized_uri,
+         *msg;
+    const char *type_label;
+
+    /* No uri given - show the current URI. */
+    if (!uri || !*uri) {
+        update_urlbar(c);
+        return;
+    }
+
+    switch (type) {
+        case LINK_TYPE_LINK:
+            type_label = "Link: ";
+            break;
+        case LINK_TYPE_IMAGE:
+            type_label = "Image: ";
+            break;
+        default:
+            return;
+    }
+
+    sanitized_uri = util_sanitize_uri(uri);
+    msg           = g_strconcat(type_label, uri, NULL);
+    gtk_label_set_text(GTK_LABEL(c->statusbar.left), msg);
+    g_free(msg);
+    g_free(sanitized_uri);
+}
+
+/**
  * Destroys given client and removed it from client queue. If no client is
  * there in queue, quit the gtk main loop.
  */
@@ -990,6 +1027,7 @@
 #ifndef FEATURE_NO_XEMBED
         + (vb.embed ? 2 : 0)
 #endif
+        + (vb.incognito ? 1 : 0)
         + (vb.profile ? 2 : 0)
         + (vb.no_maximize ? 1 : 0),
         sizeof(char *)
@@ -1009,6 +1047,9 @@
         cmd[i++] = xid;
     }
 #endif
+    if (vb.incognito) {
+        cmd[i++] = "-i";
+    }
     if (vb.profile) {
         cmd[i++] = "-p";
         cmd[i++] = vb.profile;
@@ -1037,15 +1078,19 @@
     autocmd_run(c, AU_DOWNLOAD_STARTED, uri, NULL);
 #endif
 
-    g_signal_connect(download, "decide-destination", 
G_CALLBACK(on_webdownload_decide_destination), c);
-    g_signal_connect(download, "failed", G_CALLBACK(on_webdownload_failed), c);
-    g_signal_connect(download, "finished", 
G_CALLBACK(on_webdownload_finished), c);
-    g_signal_connect(download, "received-data", 
G_CALLBACK(on_webdownload_received_data), c);
+    if (GET_BOOL(c, "download-use-external")) {
+        g_signal_connect(download, "notify::response", 
G_CALLBACK(on_webdownload_response_received), c);
+    } else {
+        g_signal_connect(download, "decide-destination", 
G_CALLBACK(on_webdownload_decide_destination), c);
+        g_signal_connect(download, "failed", 
G_CALLBACK(on_webdownload_failed), c);
+        g_signal_connect(download, "finished", 
G_CALLBACK(on_webdownload_finished), c);
+        g_signal_connect(download, "received-data", 
G_CALLBACK(on_webdownload_received_data), c);
 
-    c->state.downloads = g_list_append(c->state.downloads, download);
+        c->state.downloads = g_list_append(c->state.downloads, download);
 
-    /* to reflect the correct download count */
-    vb_statusbar_update(c);
+        /* to reflect the correct download count */
+        vb_statusbar_update(c);
+    }
 }
 
 /**
@@ -1087,6 +1132,48 @@
     return vb_download_set_destination(c, download, suggested_filename, NULL);
 }
 
+static void on_webdownload_response_received(WebKitDownload *download,
+        GParamSpec *ps, Client *c)
+{
+    spawn_download_command(c, webkit_download_get_response(download));
+    webkit_download_cancel(download);
+}
+
+static void spawn_download_command(Client *c, WebKitURIResponse *response)
+{
+    char *cmd;
+    char **argv, **envp;
+    int argc;
+    GError *error = NULL;
+
+    cmd = g_strdup_printf(GET_CHAR(c, "download-command"),
+            webkit_uri_response_get_uri(response));
+
+    if (!g_shell_parse_argv(cmd, &argc, &argv, &error)) {
+        g_warning("Could not parse download-command '%s': %s",
+                cmd,
+                error->message);
+        g_error_free(error);
+        g_free(cmd);
+        return;
+    }
+
+    envp = g_get_environ();
+    envp = g_environ_setenv(envp, "VIMB_DOWNLOAD_PATH",
+            GET_CHAR(c, "download-path"), TRUE);
+
+    if (g_spawn_async(NULL, argv, envp, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, 
&error)) {
+        vb_echo(c, MSG_NORMAL, FALSE, "Download started");
+    } else {
+        vb_echo(c, MSG_ERROR, TRUE, "Could not start download");
+        g_warning("%s", error->message);
+        g_clear_error(&error);
+    }
+    g_free(cmd);
+    g_strfreev(envp);
+    g_strfreev(argv);
+}
+
 /**
  * Callback for the webkit download failed signal.
  * This signal is emitted when an error occurs during the download operation.
@@ -1347,18 +1434,25 @@
         WebKitLoadEvent event, Client *c)
 {
     GTlsCertificateFlags tlsflags;
+    const char *raw_uri;
     char *uri = NULL;
 
+    raw_uri = webkit_web_view_get_uri(webview);
+    if (raw_uri) {
+        uri = util_sanitize_uri(raw_uri);
+    }
+
     switch (event) {
         case WEBKIT_LOAD_STARTED:
 #ifdef FEATURE_AUTOCMD
-            autocmd_run(c, AU_LOAD_STARTED, webkit_web_view_get_uri(webview), 
NULL);
+            autocmd_run(c, AU_LOAD_STARTED, raw_uri, NULL);
 #endif
             /* update load progress in statusbar */
             c->state.progress = 0;
             vb_statusbar_update(c);
-            uri = util_sanitize_uri(webkit_web_view_get_uri(webview));
-            set_title(c, uri);
+            if (uri) {
+                set_title(c, uri);
+            }
             /* Make sure hinting is cleared before the new page is loaded.
              * Without that vimb would still be in hinting mode after hinting
              * was started and some links was clicked my mouse. Even if there
@@ -1379,13 +1473,12 @@
              * right place to remove the flag. */
             c->mode->flags &= ~FLAG_IGNORE_FOCUS;
 #ifdef FEATURE_AUTOCMD
-            autocmd_run(c, AU_LOAD_COMMITTED, 
webkit_web_view_get_uri(webview), NULL);
+            autocmd_run(c, AU_LOAD_COMMITTED, raw_uri, NULL);
 #endif
             /* save the current URI in register % */
-            uri = util_sanitize_uri(webkit_web_view_get_uri(webview));
             vb_register_add(c, '%', uri);
             /* check if tls is on and the page is trusted */
-            if (g_str_has_prefix(uri, "https://";)) {
+            if (uri && g_str_has_prefix(uri, "https://";)) {
                 if (webkit_web_view_get_tls_info(webview, NULL, &tlsflags) && 
tlsflags) {
                     set_statusbar_style(c, STATUS_SSL_INVALID);
                 } else {
@@ -1405,12 +1498,11 @@
             break;
 
         case WEBKIT_LOAD_FINISHED:
-            uri = util_sanitize_uri(webkit_web_view_get_uri(webview));
 #ifdef FEATURE_AUTOCMD
-            autocmd_run(c, AU_LOAD_FINISHED, webkit_web_view_get_uri(webview), 
NULL);
+            autocmd_run(c, AU_LOAD_FINISHED, raw_uri, NULL);
 #endif
             c->state.progress = 100;
-            if (strncmp(uri, "about:", 6)) {
+            if (uri && strncmp(uri, "about:", 6)) {
                 history_add(c, HISTORY_URL, uri, 
webkit_web_view_get_title(webview));
             }
             break;
@@ -1429,9 +1521,6 @@
 static void on_webview_mouse_target_changed(WebKitWebView *webview,
         WebKitHitTestResult *result, guint modifiers, Client *c)
 {
-    char *msg;
-    char *uri;
-
     /* Save the hitTestResult to have this later available for events that
      * don't support this. */
     if (c->state.hit_test_result) {
@@ -1440,20 +1529,14 @@
     c->state.hit_test_result = g_object_ref(result);
 
     if (webkit_hit_test_result_context_is_link(result)) {
-        uri = util_sanitize_uri(webkit_hit_test_result_get_link_uri(result));
-        msg = g_strconcat("Link: ", uri, NULL);
-        gtk_label_set_text(GTK_LABEL(c->statusbar.left), msg);
-        g_free(msg);
-        g_free(uri);
+        vb_statusbar_show_hover_url(c, LINK_TYPE_LINK,
+                webkit_hit_test_result_get_link_uri(result));
     } else if (webkit_hit_test_result_context_is_image(result)) {
-        uri = util_sanitize_uri(webkit_hit_test_result_get_image_uri(result));
-        msg = g_strconcat("Image: ", uri, NULL);
-        gtk_label_set_text(GTK_LABEL(c->statusbar.left), msg);
-        g_free(msg);
-        g_free(uri);
+        vb_statusbar_show_hover_url(c, LINK_TYPE_LINK,
+                webkit_hit_test_result_get_image_uri(result));
     } else {
         /* No link under cursor - show the current URI. */
-        update_urlbar(c);
+        vb_statusbar_show_hover_url(c, LINK_TYPE_NONE, NULL);
     }
 }
 
@@ -1492,6 +1575,7 @@
     if (c->state.uri) {
         g_free(c->state.uri);
     }
+
     c->state.uri = util_sanitize_uri(webkit_web_view_get_uri(c->webview));
 
     update_urlbar(c);
@@ -1689,6 +1773,9 @@
     /* free memory of other components */
     util_cleanup();
 
+    for (i = 0; i < STORAGE_LAST; i++) {
+        file_storage_free(vb.storage[i]);
+    }
     for (i = 0; i < FILES_LAST; i++) {
         if (vb.files[i]) {
             g_free(vb.files[i]);
@@ -1715,25 +1802,32 @@
         vb.files[FILES_CONFIG] = g_strdup(rp);
         free(rp);
     } else {
-        vb.files[FILES_CONFIG] = util_get_filepath(path, "config", FALSE, 
0600);
+        vb.files[FILES_CONFIG] = g_build_filename(path, "config", NULL);
     }
 
     /* Setup those files that are use multiple time during runtime */
-    vb.files[FILES_CLOSED]     = util_get_filepath(path, "closed", TRUE, 0600);
-    vb.files[FILES_COOKIE]     = util_get_filepath(path, "cookies.db", TRUE, 
0600);
-    vb.files[FILES_USER_STYLE] = util_get_filepath(path, "style.css", FALSE, 
0600);
-    vb.files[FILES_SCRIPT]     = util_get_filepath(path, "scripts.js", FALSE, 
0600);
-    vb.files[FILES_HISTORY]    = util_get_filepath(path, "history", TRUE, 
0600);
-    vb.files[FILES_COMMAND]    = util_get_filepath(path, "command", TRUE, 
0600);
-    vb.files[FILES_BOOKMARK]   = util_get_filepath(path, "bookmark", TRUE, 
0600);
-    vb.files[FILES_QUEUE]      = util_get_filepath(path, "queue", TRUE, 0600);
-    vb.files[FILES_SEARCH]     = util_get_filepath(path, "search", TRUE, 0600);
+    if (!vb.incognito) {
+        vb.files[FILES_CLOSED] = g_build_filename(path, "closed", NULL);
+        vb.files[FILES_COOKIE] = g_build_filename(path, "cookies.db", NULL);
+    }
+    vb.files[FILES_BOOKMARK]   = g_build_filename(path, "bookmark", NULL);
+    vb.files[FILES_QUEUE]      = g_build_filename(path, "queue", NULL);
+    vb.files[FILES_SCRIPT]     = g_build_filename(path, "scripts.js", NULL);
+    vb.files[FILES_USER_STYLE] = g_build_filename(path, "style.css", NULL);
+
+    vb.storage[STORAGE_HISTORY]  = file_storage_new(path, "history", 
vb.incognito);
+    vb.storage[STORAGE_COMMAND]  = file_storage_new(path, "command", 
vb.incognito);
+    vb.storage[STORAGE_SEARCH]   = file_storage_new(path, "search", 
vb.incognito);
     g_free(path);
 
     /* Use seperate rendering processed for the webview of the clients in the
      * current instance. This must be called as soon as possible according to
      * the documentation. */
-    ctx = webkit_web_context_get_default();
+    if (vb.incognito) {
+        ctx = webkit_web_context_new_ephemeral();
+    } else {
+        ctx = webkit_web_context_get_default();
+    }
     webkit_web_context_set_process_model(ctx, 
WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
     webkit_web_context_set_cache_model(ctx, WEBKIT_CACHE_MODEL_WEB_BROWSER);
 
@@ -1988,6 +2082,7 @@
     GOptionEntry opts[] = {
         {"config", 'c', 0, G_OPTION_ARG_FILENAME, &vb.configfile, "Custom 
configuration file", NULL},
         {"embed", 'e', 0, G_OPTION_ARG_STRING, &winid, "Reparents to window 
specified by xid", NULL},
+        {"incognito", 'i', 0, G_OPTION_ARG_NONE, &vb.incognito, "Run with user 
data read-only", NULL},
         {"profile", 'p', 0, G_OPTION_ARG_CALLBACK, 
(GOptionArgFunc*)profileOptionArgFunc, "Profile name", NULL},
         {"version", 'v', 0, G_OPTION_ARG_NONE, &ver, "Print version", NULL},
         {"no-maximize", 0, 0, G_OPTION_ARG_NONE, &vb.no_maximize, "Do no 
attempt to maximize window", NULL},
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/main.h new/vimb-3.5.0/src/main.h
--- old/vimb-3.4.0/src/main.h   2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/src/main.h   2019-07-29 22:03:20.000000000 +0200
@@ -26,6 +26,7 @@
 #include <webkit2/webkit2.h>
 #include "shortcut.h"
 #include "handler.h"
+#include "file-storage.h"
 
 #include "config.h"
 
@@ -105,17 +106,29 @@
 enum {
     FILES_BOOKMARK,
     FILES_CLOSED,
-    FILES_COMMAND,
     FILES_CONFIG,
     FILES_COOKIE,
-    FILES_HISTORY,
     FILES_QUEUE,
     FILES_SCRIPT,
-    FILES_SEARCH,
     FILES_USER_STYLE,
     FILES_LAST
 };
 
+enum {
+    STORAGE_CLOSED,
+    STORAGE_COMMAND,
+    STORAGE_CONFIG,
+    STORAGE_HISTORY,
+    STORAGE_SEARCH,
+    STORAGE_LAST
+};
+
+typedef enum {
+    LINK_TYPE_NONE,
+    LINK_TYPE_LINK,
+    LINK_TYPE_IMAGE,
+} VbLinkType;
+
 typedef struct Client Client;
 typedef struct State State;
 typedef struct Map Map;
@@ -262,6 +275,7 @@
     GHashTable  *modes;             /* all available browser main modes */
     char        *configfile;        /* config file given as option on startup 
*/
     char        *files[FILES_LAST];
+    FileStorage *storage[STORAGE_LAST];
     char        *profile;           /* profile name */
     struct {
         guint   history_max;
@@ -269,6 +283,7 @@
     } config;
     GtkCssProvider *style_provider;
     gboolean    no_maximize;
+    gboolean    incognito;
 };
 
 gboolean vb_download_set_destination(Client *c, WebKitDownload *download,
@@ -290,6 +305,7 @@
 void vb_register_add(Client *c, char buf, const char *value);
 const char *vb_register_get(Client *c, char buf);
 void vb_statusbar_update(Client *c);
+void vb_statusbar_show_hover_url(Client *c, VbLinkType type, const char *uri);
 void vb_gui_style_update(Client *c, const char *name, const char *value);
 
 #endif /* end of include guard: _MAIN_H */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/scripts/hints.css 
new/vimb-3.5.0/src/scripts/hints.css
--- old/vimb-3.4.0/src/scripts/hints.css        2019-03-26 23:47:03.000000000 
+0100
+++ new/vimb-3.5.0/src/scripts/hints.css        2019-07-29 22:03:20.000000000 
+0200
@@ -1,5 +1,4 @@
 span[vimbhint^='label']{
-    -webkit-transform:translate(-4px,-4px);
     background-color:#fff;
     border:1px solid #444;
     color:#000;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/scripts/hints.js 
new/vimb-3.5.0/src/scripts/hints.js
--- old/vimb-3.4.0/src/scripts/hints.js 2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/src/scripts/hints.js 2019-07-29 22:03:20.000000000 +0200
@@ -175,8 +175,8 @@
                 label = labelTmpl.cloneNode(false);
 
                 label.style.display = "none";
-                label.style.left    = rect.left + "px";
-                label.style.top     = rect.top + "px";
+                label.style.left    = Math.max(rect.left - 4, 0) + "px";
+                label.style.top     = Math.max(rect.top - 4, 0) + "px";
 
                 /* if hinted element is an image - show title or alt of the 
image in hint label */
                 /* this allows to see how to filter for the image */
@@ -348,7 +348,7 @@
                 idx = 0;
             }
         }
-        focusHint(idx);
+        return focusHint(idx);
     }
 
     function fire() {
@@ -423,8 +423,11 @@
         }
         /* get the new active hint */
         if ((activeHint = validHints[newIdx])) {
+            var e = activeHint.e;
             activeHint.focus();
-            mouseEvent(activeHint.e, "mouseover");
+            mouseEvent(e, "mouseover");
+
+            return "OVER:" + (e instanceof HTMLImageElement ? "I:" : "A:") + 
getSrc(e);
         }
     }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/setting.c new/vimb-3.5.0/src/setting.c
--- old/vimb-3.4.0/src/setting.c        2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/src/setting.c        2019-07-29 22:03:20.000000000 +0200
@@ -116,7 +116,6 @@
     setting_add(c, "plugins", TYPE_BOOLEAN, &on, webkit, 0, "enable-plugins");
     setting_add(c, "prevent-newwindow", TYPE_BOOLEAN, &off, internal, 0, 
&c->config.prevent_newwindow);
     setting_add(c, "print-backgrounds", TYPE_BOOLEAN, &on, webkit, 0, 
"print-backgrounds");
-    setting_add(c, "private-browsing", TYPE_BOOLEAN, &off, webkit, 0, 
"enable-private-browsing");
     setting_add(c, "sans-serif-font", TYPE_CHAR, &"sans-serif", webkit, 0, 
"sans-serif-font-family");
     setting_add(c, "scripts", TYPE_BOOLEAN, &on, webkit, 0, 
"enable-javascript");
     setting_add(c, "serif-font", TYPE_CHAR, &"serif", webkit, 0, 
"serif-font-family");
@@ -150,6 +149,8 @@
     i = 100;
     setting_add(c, "default-zoom", TYPE_INTEGER, &i, default_zoom, 0, NULL);
     setting_add(c, "download-path", TYPE_CHAR, &"~/", NULL, 0, NULL);
+    setting_add(c, "download-command", TYPE_CHAR, &"/bin/sh -c \"curl -sLJOC - 
-e '$VIMB_URI' %s\"", NULL, 0, NULL);
+    setting_add(c, "download-use-external", TYPE_BOOLEAN, &off, NULL, 0, NULL);
     setting_add(c, "incsearch", TYPE_BOOLEAN, &off, internal, 0, 
&c->config.incsearch);
     i = 10;
     /* TODO should be global and not overwritten by a new client */
@@ -158,7 +159,7 @@
     setting_add(c, "spell-checking", TYPE_BOOLEAN, &off, 
webkit_spell_checking, 0, NULL);
     setting_add(c, "spell-checking-languages", TYPE_CHAR, &"en_US", 
webkit_spell_checking_language, FLAG_LIST|FLAG_NODUP, NULL);
 
-    /* gui style settings vimb3 */
+    /* gui style settings vimb */
     setting_add(c, "completion-css", TYPE_CHAR, 
&"color:#fff;background-color:#656565;font:" SETTING_GUI_FONT_NORMAL, 
gui_style, 0, NULL);
     setting_add(c, "completion-hover-css", TYPE_CHAR, 
&"background-color:#777;", gui_style, 0, NULL);
     setting_add(c, "completion-selected-css", TYPE_CHAR, 
&"color:#f6f3e8;background-color:#888;", gui_style, 0, NULL);
@@ -672,7 +673,7 @@
 
     ucm = webkit_web_view_get_user_content_manager(c->webview);
 
-    if (enabled && vb.files[FILES_USER_STYLE]) {
+    if (enabled) {
         if (g_file_get_contents(vb.files[FILES_USER_STYLE], &source, NULL, 
NULL)) {
             style = webkit_user_style_sheet_new(
                 source, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
@@ -683,7 +684,7 @@
             webkit_user_style_sheet_unref(style);
             g_free(source);
         } else {
-            g_warning("Could not reed style file: %s", 
vb.files[FILES_USER_STYLE]);
+            g_message("Could not reed style file: %s", 
vb.files[FILES_USER_STYLE]);
         }
     } else {
         webkit_user_content_manager_remove_all_style_sheets(ucm);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/util.c new/vimb-3.5.0/src/util.c
--- old/vimb-3.4.0/src/util.c   2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/src/util.c   2019-07-29 22:03:20.000000000 +0200
@@ -430,41 +430,6 @@
 }
 
 /**
- * Buil the path from given directory and filename and checks if the file
- * exists. If the file does not exists and the create option is not set, this
- * function returns NULL.
- * If the file exists or the create option was given the full generated path
- * is returned as newly allocated string.
- *
- * The return value must be freed with g_free.
- *
- * @dir:        Directory in which the file is searched.
- * @filename:   Filename to built the absolute path with.
- * @create:     If TRUE, the file is created if it does not already exist.
- * @mode:       Mode (file permission as chmod(2)) used for the file when 
creating it.
- */
-char *util_get_filepath(const char *dir, const char *filename, gboolean 
create, int mode)
-{
-    char *fullpath;
-
-    /* Built the full path out of config dir and given file name. */
-    fullpath = g_build_filename(dir, filename, NULL);
-
-    if (g_file_test(fullpath, G_FILE_TEST_IS_REGULAR)) {
-        return fullpath;
-    } else if (create) {
-        /* If create option was given - create the file. */
-        fclose(fopen(fullpath, "a"));
-        g_chmod(fullpath, mode);
-        return fullpath;
-    }
-
-    g_free(fullpath);
-    return NULL;
-}
-
-
-/**
  * Retrieves the file content as lines.
  *
  * The result have to be freed by g_strfreev().
@@ -478,7 +443,7 @@
         return NULL;
     }
 
-    if ((content = util_get_file_contents(filename, NULL))) {
+    if (g_file_get_contents(filename, &content, NULL, NULL)) {
         /* split the file content into lines */
         lines = g_strsplit(content, "\n", -1);
         g_free(content);
@@ -491,20 +456,18 @@
  * based on the lines comparing all chars until the next <tab> char or end of
  * line.
  *
- * @filename:    file to read items from
  * @func:        Function to parse a single line to item.
  * @max_items:   maximum number of items that are returned, use 0 for
  *               unlimited items
  */
-GList *util_file_to_unique_list(const char *filename, Util_Content_Func func,
+GList *util_strv_to_unique_list(char **lines, Util_Content_Func func,
         guint max_items)
 {
-    char *line, **lines;
+    char *line;
     int i, len;
     GList *gl = NULL;
     GHashTable *ht;
 
-    lines = util_get_lines(filename);
     if (!lines) {
         return NULL;
     }
@@ -556,7 +519,6 @@
         }
     }
 
-    g_strfreev(lines);
     g_hash_table_destroy(ht);
 
     return gl;
@@ -821,7 +783,7 @@
 /**
  * Strips password from a uri.
  *
- * Return newly allocated string.
+ * Return newly allocated string or NULL.
  */
 char *util_sanitize_uri(const char *uri_str)
 {
@@ -829,8 +791,14 @@
     char *sanitized_uri;
     char *for_display;
 
+    if (!uri_str) {
+        return NULL;
+    }
 #if WEBKIT_CHECK_VERSION(2, 24, 0)
     for_display = webkit_uri_for_display(uri_str);
+    if (!for_display) {
+        for_display = g_strdup(uri_str);
+    }
 #else
     for_display = g_strdup(uri_str);
 #endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/src/util.h new/vimb-3.5.0/src/util.h
--- old/vimb-3.4.0/src/util.h   2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/src/util.h   2019-07-29 22:03:20.000000000 +0200
@@ -43,9 +43,8 @@
 char *util_get_config_dir(void);
 char *util_get_file_contents(const char *filename, gsize *length);
 gboolean util_file_set_content(const char *file, const char *contents);
-char *util_get_filepath(const char *dir, const char *filename, gboolean 
create, int mode);
 char **util_get_lines(const char *filename);
-GList *util_file_to_unique_list(const char *filename, Util_Content_Func func,
+GList *util_strv_to_unique_list(char **lines, Util_Content_Func func,
         guint max_items);
 gboolean util_fill_completion(GtkListStore *store, const char *input, GList 
*src);
 gboolean util_filename_fill_completion(State state, GtkListStore *store, const 
char *input);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/tests/.gitignore 
new/vimb-3.5.0/tests/.gitignore
--- old/vimb-3.4.0/tests/.gitignore     2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/tests/.gitignore     2019-07-29 22:03:20.000000000 +0200
@@ -1 +1,2 @@
 /test-*
+!/test-*.c
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/tests/Makefile 
new/vimb-3.5.0/tests/Makefile
--- old/vimb-3.4.0/tests/Makefile       2019-03-26 23:47:03.000000000 +0100
+++ new/vimb-3.5.0/tests/Makefile       2019-07-29 22:03:20.000000000 +0200
@@ -4,7 +4,8 @@
 
 TEST_PROGS = test-util \
                         test-shortcut \
-                        test-handler
+                        test-handler \
+                        test-file-storage
 
 all: $(TEST_PROGS)
        $(Q)LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):." gtester --verbose 
$(TEST_PROGS)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vimb-3.4.0/tests/test-file-storage.c 
new/vimb-3.5.0/tests/test-file-storage.c
--- old/vimb-3.4.0/tests/test-file-storage.c    1970-01-01 01:00:00.000000000 
+0100
+++ new/vimb-3.5.0/tests/test-file-storage.c    2019-07-29 22:03:20.000000000 
+0200
@@ -0,0 +1,147 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2019 Daniel Carl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#include <src/file-storage.h>
+#include <gio/gio.h>
+#include <stdio.h>
+
+static char *pwd;
+static char *none_existing_file = "_absent.txt";
+static char *created_file       = "_created.txt";
+static char *existing_file      = "_existent.txt";
+
+static void test_ephemeral_no_file(void)
+{
+    FileStorage *s;
+    char **lines;
+    char *file_path;
+
+    file_path = g_build_filename(pwd, none_existing_file, NULL);
+
+    /* make sure the file does not exist */
+    remove(file_path);
+    s = file_storage_new(pwd, none_existing_file, TRUE);
+    g_assert_nonnull(s);
+    g_assert_cmpstr(file_path, ==, file_storage_get_path(s));
+    g_assert_true(file_storage_is_readonly(s));
+
+    /* empty file storage */
+    lines = file_storage_get_lines(s);
+    g_assert_cmpint(g_strv_length(lines), ==, 0);
+    g_strfreev(lines);
+
+    file_storage_append(s, "-%s\n", "foo");
+    file_storage_append(s, "-%s\n", "bar");
+
+    /* File must not be created on appending data to storage */
+    g_assert_false(g_file_test(file_path, G_FILE_TEST_IS_REGULAR));
+
+    lines = file_storage_get_lines(s);
+    g_assert_cmpint(g_strv_length(lines), ==, 3);
+    g_assert_cmpstr(lines[0], ==, "-foo");
+    g_assert_cmpstr(lines[1], ==, "-bar");
+    g_strfreev(lines);
+    file_storage_free(s);
+    g_free(file_path);
+}
+
+static void test_file_created(void)
+{
+    FileStorage *s;
+    char *file_path;
+
+    file_path = g_build_filename(pwd, created_file, NULL);
+    remove(file_path);
+
+    g_assert_false(g_file_test(file_path, G_FILE_TEST_IS_REGULAR));
+    s = file_storage_new(pwd, created_file, FALSE);
+    g_assert_false(file_storage_is_readonly(s));
+    g_assert_cmpstr(file_path, ==, file_storage_get_path(s));
+
+    /* check that file is created only on first write */
+    g_assert_false(g_file_test(file_path, G_FILE_TEST_IS_REGULAR));
+    file_storage_append(s, "");
+    g_assert_true(g_file_test(file_path, G_FILE_TEST_IS_REGULAR));
+
+    file_storage_free(s);
+    g_free(file_path);
+}
+
+static void test_ephemeral_with_file(void)
+{
+    FileStorage *s;
+    char *file_path;
+    char **lines;
+    char *content = NULL;
+    gboolean result;
+
+    file_path = g_build_filename(pwd, existing_file, NULL);
+
+    s = file_storage_new(pwd, existing_file, TRUE);
+    g_assert_nonnull(s);
+    g_assert_true(file_storage_is_readonly(s));
+    g_assert_cmpstr(file_path, ==, file_storage_get_path(s));
+
+    /* file does not exists yet */
+    lines = file_storage_get_lines(s);
+    g_assert_cmpint(g_strv_length(lines), ==, 0);
+    g_strfreev(lines);
+
+    /* empty file storage but file with two lines */
+    result = g_file_set_contents(file_path, "one\n", -1, NULL);
+    g_assert_true(result);
+    lines = file_storage_get_lines(s);
+    g_assert_cmpint(g_strv_length(lines), ==, 2);
+    g_strfreev(lines);
+
+    file_storage_append(s, "%s\n", "two ephemeral");
+
+    lines = file_storage_get_lines(s);
+    g_assert_cmpint(g_strv_length(lines), ==, 3);
+    g_assert_cmpstr(lines[0], ==, "one");
+    g_assert_cmpstr(lines[1], ==, "two ephemeral");
+    g_strfreev(lines);
+
+    /* now make sure the file was not changed */
+    g_file_get_contents(file_path, &content, NULL, NULL);
+    g_assert_cmpstr(content, ==, "one\n");
+
+    file_storage_free(s);
+    g_free(file_path);
+}
+
+int main(int argc, char *argv[])
+{
+    int result;
+    g_test_init(&argc, &argv, NULL);
+
+    pwd = g_get_current_dir();
+
+    g_test_add_func("/test-file-storage/ephemeral-no-file", 
test_ephemeral_no_file);
+    g_test_add_func("/test-file-storage/file-created", test_file_created);
+    g_test_add_func("/test-file-storage/ephemeral-with-file", 
test_ephemeral_with_file);
+
+    result = g_test_run();
+
+    remove(existing_file);
+    remove(created_file);
+    g_free(pwd);
+
+    return result;
+}


Reply via email to