Add file storage to allow to collect ephemeral data #562.
authorDaniel Carl <danielcarl@gmx.de>
Thu, 23 May 2019 23:36:30 +0000 (01:36 +0200)
committerDaniel Carl <danielcarl@gmx.de>
Mon, 3 Jun 2019 21:16:00 +0000 (23:16 +0200)
src/file-storage.c [new file with mode: 0644]
src/file-storage.h [new file with mode: 0644]
tests/.gitignore
tests/Makefile
tests/test-file-storage.c [new file with mode: 0644]

diff --git a/src/file-storage.c b/src/file-storage.c
new file mode 100644 (file)
index 0000000..925f669
--- /dev/null
@@ -0,0 +1,159 @@
+/**
+ * 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"
+#include "util.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, int mode)
+{
+    char *fullpath;
+    FileStorage *storage;
+
+    storage           = g_slice_new(FileStorage);
+    storage->readonly = (mode == 0);
+
+    /* Built the full path out of dir and given file name. */
+    fullpath = g_build_filename(dir, filename, NULL);
+    if (g_file_test(fullpath, G_FILE_TEST_IS_REGULAR)) {
+        storage->file_path = fullpath;
+    } else if (mode) {
+        /* If create option was given - create the file. */
+        fclose(fopen(fullpath, "a"));
+
+        storage->file_path = fullpath;
+        g_chmod(fullpath, mode);
+    } else {
+        storage->file_path = NULL;
+        g_free(fullpath);
+    }
+
+    /* Use gstring as storage in case when the file is used read only. */
+    if (storage->readonly) {
+        storage->str = g_string_new(NULL);
+    }
+
+    return storage;
+}
+
+/**
+ * Free memory for given file storage.
+ */
+void file_storage_free(FileStorage *storage)
+{
+    if (storage) {
+        if (storage->file_path) {
+            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 (storage->file_path && (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;
+
+    if (storage->file_path) {
+        content = util_get_file_contents(storage->file_path, 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;
+}
diff --git a/src/file-storage.h b/src/file-storage.h
new file mode 100644 (file)
index 0000000..3beecce
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * 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);
+
+#endif /* end of include guard: _FILE_STORAGE_H */
index c802245..362dcd3 100644 (file)
@@ -1 +1,2 @@
 /test-*
+!/test-*.c
index 6ebc85d..c3ce1ca 100644 (file)
@@ -4,7 +4,8 @@ include ../config.mk
 
 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 --git a/tests/test-file-storage.c b/tests/test-file-storage.c
new file mode 100644 (file)
index 0000000..28859b2
--- /dev/null
@@ -0,0 +1,132 @@
+/**
+ * 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, 0);
+    g_assert_nonnull(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, 0640);
+    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);
+    result = g_file_set_contents(file_path, "one\n", -1, NULL);
+    g_assert_true(result);
+
+    s = file_storage_new(pwd, existing_file, 0);
+    g_assert_nonnull(s);
+
+    /* empty file storage but file with two lines */
+    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;
+}