/* See LICENSE file for license and copyright information */ #include #include #include #include "database.h" #define DATABASE "bookmarks.sqlite" struct zathura_database_s { sqlite3* session; }; zathura_database_t* zathura_db_init(const char* dir) { if (dir == NULL) { goto error_ret; } char* path = g_build_filename(dir, DATABASE, NULL); if (path == NULL) { goto error_ret; } zathura_database_t* db = g_malloc0(sizeof(zathura_database_t)); if (db == NULL) { goto error_free; } /* create bookmarks database */ static const char SQL_BOOKMARK_INIT[] = "CREATE TABLE IF NOT EXISTS bookmarks (" "file TEXT," "id TEXT," "page INTEGER," "PRIMARY KEY(file, id));"; static const char SQL_FILEINFO_INIT[] = "CREATE TABLE IF NOT EXISTS fileinfo (" "file TEXT PRIMARY KEY," "page INTEGER," "offset INTEGER," "scale FLOAT);"; if (sqlite3_open(path, &(db->session)) != SQLITE_OK) { girara_error("Could not open database: %s\n", path); goto error_free; } if (sqlite3_exec(db->session, SQL_BOOKMARK_INIT, NULL, 0, NULL) != SQLITE_OK) { girara_error("Failed to initialize database: %s\n", path); goto error_free; } if (sqlite3_exec(db->session, SQL_FILEINFO_INIT, NULL, 0, NULL) != SQLITE_OK) { girara_error("Failed to initialize database: %s\n", path); goto error_free; } return db; error_free: zathura_db_free(db); error_ret: g_free(path); return NULL; } void zathura_db_free(zathura_database_t* db) { if (db == NULL) { return; } if (db->session != NULL) { 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)); if (bookmark == NULL) { continue; } 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; } bool zathura_db_set_fileinfo(zathura_database_t* db, const char* file, unsigned int page, int offset, float scale) { g_return_val_if_fail(db && file, false); static const char SQL_FILEINFO_SET[] = "REPLACE INTO fileinfo (file, page, offset, scale) VALUES (?, ?, ?, ?);"; sqlite3_stmt* stmt = prepare_statement(db->session, SQL_FILEINFO_SET); if (stmt == NULL) { return false; } if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK || sqlite3_bind_int(stmt, 2, page) != SQLITE_OK || sqlite3_bind_int(stmt, 3, offset) != SQLITE_OK || sqlite3_bind_double(stmt, 4, scale) != 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_get_fileinfo(zathura_database_t* db, const char* file, unsigned int* page, int* offset, float* scale) { g_return_val_if_fail(db && file && page && offset && scale, false); static const char SQL_FILEINFO_GET[] = "SELECT page, offset, scale FROM fileinfo WHERE file = ?;"; sqlite3_stmt* stmt = prepare_statement(db->session, SQL_FILEINFO_GET); if (stmt == NULL) { return false; } if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK) { sqlite3_finalize(stmt); girara_error("Failed to bind arguments."); return false; } if (sqlite3_step(stmt) != SQLITE_ROW) { sqlite3_finalize(stmt); girara_info("No info for file %s available.", file); return false; } *page = sqlite3_column_int(stmt, 0); *offset = sqlite3_column_int(stmt, 1); *scale = sqlite3_column_double(stmt, 2); sqlite3_finalize(stmt); return true; }