diff --git a/bookmarks.c b/bookmarks.c index 40e5a1f..ec73223 100644 --- a/bookmarks.c +++ b/bookmarks.c @@ -2,6 +2,7 @@ #include #include "bookmarks.h" +#include "database.h" zathura_bookmark_t* zathura_bookmark_add(zathura_t* zathura, const gchar* id, unsigned int page) @@ -10,8 +11,7 @@ zathura_bookmark_add(zathura_t* zathura, const gchar* id, unsigned int page) g_return_val_if_fail(id, NULL); GIRARA_LIST_FOREACH(zathura->bookmarks.bookmarks, zathura_bookmark_t*, iter, bookmark) - if (strcmp(bookmark->id, id) == 0) - { + if (strcmp(bookmark->id, id) == 0) { girara_list_iterator_free(iter); return NULL; } @@ -31,8 +31,7 @@ void zathura_bookmark_remove(zathura_t* zathura, const gchar* id) g_return_if_fail(id); zathura_bookmark_t* bookmark = zathura_bookmark_get(zathura, id); - if (bookmark) - { + if (bookmark) { girara_list_remove(zathura->bookmarks.bookmarks, bookmark); } } @@ -43,8 +42,7 @@ zathura_bookmark_t* zathura_bookmark_get(zathura_t* zathura, const gchar* id) g_return_val_if_fail(id, NULL); GIRARA_LIST_FOREACH(zathura->bookmarks.bookmarks, zathura_bookmark_t*, iter, bookmark) - if (strcmp(bookmark->id, id) == 0) - { + if (strcmp(bookmark->id, id) == 0) { girara_list_iterator_free(iter); return bookmark; } @@ -55,8 +53,7 @@ zathura_bookmark_t* zathura_bookmark_get(zathura_t* zathura, const gchar* id) void zathura_bookmark_free(zathura_bookmark_t* bookmark) { - if (!bookmark) - { + if (!bookmark) { return; } @@ -64,3 +61,19 @@ void zathura_bookmark_free(zathura_bookmark_t* bookmark) g_free(bookmark); } +bool +zathura_bookmarks_load(zathura_t* zathura, const gchar* file) { + g_return_val_if_fail(zathura && zathura->database, false); + g_return_val_if_fail(file, false); + + girara_list_t* bookmarks = zathura_db_load_bookmarks(zathura->database, file); + if (!bookmarks) { + return false; + } + + girara_list_free(zathura->bookmarks.bookmarks); + zathura->bookmarks.bookmarks = bookmarks; + return true; +} + + diff --git a/config.mk b/config.mk index 6c08b29..e2e163c 100644 --- a/config.mk +++ b/config.mk @@ -14,8 +14,11 @@ GTK_LIB ?= $(shell pkg-config --libs gtk+-2.0 gthread-2.0) GIRARA_INC ?= $(shell pkg-config --cflags girara-gtk2) GIRARA_LIB ?= $(shell pkg-config --libs girara-gtk2) -INCS = ${GIRARA_INC} ${GTK_INC} -LIBS = -lc ${GIRARA_LIB} ${GTK_LIB} -lpthread -lm -ldl +SQLITE_INC ?= $(shell pkg-config --cflags sqlite3) +SQLITE_LIB ?= $(shell pkg-config --libs sqlite3) + +INCS = ${GIRARA_INC} ${GTK_INC} $(SQLITE_INC) +LIBS = -lc ${GIRARA_LIB} ${GTK_LIB} $(SQLITE_LIB) -lpthread -lm -ldl # flags CFLAGS += -std=c99 -pedantic -Wall -Wno-format-zero-length $(INCS) diff --git a/database-sqlite.c b/database-sqlite.c new file mode 100644 index 0000000..33b41e1 --- /dev/null +++ b/database-sqlite.c @@ -0,0 +1,173 @@ +/* See LICENSE file for license and copyright information */ + +#include +#include +#include + +#include "database.h" + +struct zathura_database_s { + sqlite3* session; +}; + +zathura_database_t* +zathura_db_init(const char* path) { + zathura_database_t* db = g_malloc0(sizeof(zathura_database_t)); + + /* create bookmarks database */ + static const char SQL_BOOKMARK_INIT[] = + "CREATE TABLE IF NOT EXISTS bookmarks (" + "file TEXT PRIMARY KEY," + "id TEXT," + "page INTEGER," + "UNIQUE(file, id));"; + + static const char SQL_FILEINFO_INIT[] = + "CREATE TABLE IF NOT EXISTS fileinfo (" + "file TEXT PRIMARY KEY," + "page INTEGER," + "offset INTEGER," + "scale INTEGER);"; + + if (sqlite3_open(path, &(db->session)) != SQLITE_OK) { + girara_error("Could not open database: %s\n", path); + zathura_db_free(db); + return NULL; + } + + if (sqlite3_exec(db->session, SQL_BOOKMARK_INIT, NULL, 0, NULL) != SQLITE_OK) { + girara_error("Failed to initialize database: %s\n", path); + zathura_db_free(db); + return NULL; + } + + if (sqlite3_exec(db->session, SQL_FILEINFO_INIT, NULL, 0, NULL) != SQLITE_OK) { + girara_error("Failed to initialize database: %s\n", path); + zathura_db_free(db); + return NULL; + } + + return db; +} + +void +zathura_db_free(zathura_database_t* db) { + if (db == NULL) { + return; + } + + sqlite3_close(db->session); + g_free(db); +} + +static sqlite3_stmt* +prepare_statement(sqlite3* session, const char* statement) +{ + if (session == NULL || statement == NULL) { + return NULL; + } + + const char* pz_tail = NULL; + sqlite3_stmt* pp_stmt = NULL; + + if (sqlite3_prepare(session, statement, -1, &pp_stmt, &pz_tail) != SQLITE_OK) { + girara_error("Failed to prepare query: %s", statement); + sqlite3_finalize(pp_stmt); + return NULL; + } else if (pz_tail && *pz_tail != '\0') { + girara_error("Unused portion of statement: %s", pz_tail); + sqlite3_finalize(pp_stmt); + return NULL; + } + + return pp_stmt; +} + +bool +zathura_db_add_bookmark(zathura_database_t* db, const char* file, zathura_bookmark_t* bookmark) +{ + g_return_val_if_fail(db && file && bookmark, false); + + static const char SQL_BOOKMARK_ADD[] = + "REPLACE INTO bookmarks (file, id, page) VALUES (?, ?, ?);"; + + sqlite3_stmt* stmt = prepare_statement(db->session, SQL_BOOKMARK_ADD); + if (stmt == NULL) { + return false; + } + + if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK || + sqlite3_bind_text(stmt, 2, bookmark->id, -1, NULL) != SQLITE_OK || + sqlite3_bind_int(stmt, 3, bookmark->page) != SQLITE_OK) { + sqlite3_finalize(stmt); + girara_error("Failed to bind arguments."); + return false; + } + + int res = sqlite3_step(stmt); + sqlite3_finalize(stmt); + return res == SQLITE_OK; +} + +bool +zathura_db_remove_bookmark(zathura_database_t* db, const char* file, const char* id) +{ + g_return_val_if_fail(db && file && id, false); + + static const char SQL_BOOKMARK_ADD[] = + "DELETE FROM bookmarks WHERE file = ? && id = ?;"; + + sqlite3_stmt* stmt = prepare_statement(db->session, SQL_BOOKMARK_ADD); + if (stmt == NULL) { + return false; + } + + if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK || + sqlite3_bind_text(stmt, 2, id, -1, NULL) != SQLITE_OK) { + sqlite3_finalize(stmt); + girara_error("Failed to bind arguments."); + return false; + } + + int res = sqlite3_step(stmt); + sqlite3_finalize(stmt); + return res == SQLITE_OK; +} + +girara_list_t* +zathura_db_load_bookmarks(zathura_database_t* db, const char* file) +{ + g_return_val_if_fail(db && file, NULL); + + static const char SQL_BOOKMARK_SELECT[] = + "SELECT id, page FROM bookmarks WHERE file = ?;"; + + sqlite3_stmt* stmt = prepare_statement(db->session, SQL_BOOKMARK_SELECT); + if (stmt == NULL) { + return NULL; + } + + if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK) { + sqlite3_finalize(stmt); + girara_error("Failed to bind arguments."); + return NULL; + } + + girara_list_t* result = girara_list_new(); + if (result == NULL) { + sqlite3_finalize(stmt); + return NULL; + } + girara_list_set_free_function(result, (girara_free_function_t) zathura_bookmark_free); + + while (sqlite3_step(stmt) == SQLITE_ROW) { + zathura_bookmark_t* bookmark = g_malloc0(sizeof(zathura_bookmark_t)); + bookmark->id = g_strdup((const char*) sqlite3_column_text(stmt, 0)); + bookmark->page = sqlite3_column_int(stmt, 1); + + girara_list_append(result, bookmark); + } + sqlite3_finalize(stmt); + return result; +} + diff --git a/database.h b/database.h new file mode 100644 index 0000000..3430589 --- /dev/null +++ b/database.h @@ -0,0 +1,22 @@ +/* See LICENSE file for license and copyright information */ + +#ifndef DATABASE_H +#define DATABASE_H + +#include +#include + +#include "zathura.h" +#include "bookmarks.h" + +zathura_database_t* zathura_db_init(const char* path); + +void zathura_db_free(zathura_database_t* db); + +bool zathura_db_add_bookmark(zathura_database_t* db, const char* file, zathura_bookmark_t* bookmark); + +bool zathura_db_remove_bookmark(zathura_database_t* db, const char* file, const char* id); + +girara_list_t* zathura_db_load_bookmarks(zathura_database_t* db, const char* file); + +#endif // DATABASE_H diff --git a/zathura.c b/zathura.c index dabbdff..43fd50a 100644 --- a/zathura.c +++ b/zathura.c @@ -7,6 +7,7 @@ #include "bookmarks.h" #include "callbacks.h" #include "config.h" +#include "database.h" #include "document.h" #include "shortcuts.h" #include "zathura.h" @@ -190,6 +191,14 @@ zathura_init(int argc, char* argv[]) free(string_value); } + /* database */ + char* database_path = g_build_filename(zathura->config.data_dir, "bookmarks.sqlite", NULL); + zathura->database = zathura_db_init(database_path); + if (zathura->database == NULL) { + girara_error("Unable to inizialize database. Bookmarks won't be available."); + } + g_free(database_path); + /* bookmarks */ zathura->bookmarks.bookmarks = girara_list_new(); girara_list_set_free_function(zathura->bookmarks.bookmarks, (girara_free_function_t) zathura_bookmark_free); @@ -215,6 +224,8 @@ error_free: } girara_session_destroy(zathura->ui.session); + girara_list_free(zathura->bookmarks.bookmarks); + zathura_db_free(zathura->database); error_out: @@ -239,6 +250,9 @@ zathura_free(zathura_t* zathura) /* bookmarks */ girara_list_free(zathura->bookmarks.bookmarks); + /* database */ + zathura_db_free(zathura->database); + /* free print settings */ g_object_unref(zathura->print.settings); g_object_unref(zathura->print.page_setup); diff --git a/zathura.h b/zathura.h index f654fb0..d81bfc1 100644 --- a/zathura.h +++ b/zathura.h @@ -20,6 +20,10 @@ struct zathura_page_s; typedef struct zathura_document_s zathura_document_t; typedef struct zathura_page_s zathura_page_t; +/* forward declaration for types form database.h */ +struct zathura_database_s; +typedef struct zathura_database_s zathura_database_t; + /* forward declaration for types from render.h */ struct render_thread_s; typedef struct render_thread_s render_thread_t; @@ -91,6 +95,7 @@ typedef struct zathura_s } bookmarks; zathura_document_t* document; /**> The current document */ + zathura_database_t* database; /**> The database */ } zathura_t; /**