From: Daniel Carl Date: Thu, 23 May 2019 23:36:30 +0000 (+0200) Subject: Add file storage to allow to collect ephemeral data #562. X-Git-Url: https://git.owens.tech/editable-focus.html/editable-focus.html/git?a=commitdiff_plain;h=a602753067a74b429edbf252062033406bda4dbd;p=vimb.git Add file storage to allow to collect ephemeral data #562. --- diff --git a/src/file-storage.c b/src/file-storage.c new file mode 100644 index 0000000..925f669 --- /dev/null +++ b/src/file-storage.c @@ -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 +#include +#include +#include + +#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 index 0000000..3beecce --- /dev/null +++ b/src/file-storage.h @@ -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 + +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 */ diff --git a/tests/.gitignore b/tests/.gitignore index c802245..362dcd3 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1 +1,2 @@ /test-* +!/test-*.c diff --git a/tests/Makefile b/tests/Makefile index 6ebc85d..c3ce1ca 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -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 index 0000000..28859b2 --- /dev/null +++ b/tests/test-file-storage.c @@ -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 +#include +#include + +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; +}