database-plain.c 9.02 KB
Newer Older
Moritz Lipp's avatar
Moritz Lipp committed
1 2 3
/* See LICENSE file for license and copyright information */

#include <girara.h>
4 5 6
#include <glib.h>
#include <stdlib.h>
#include <string.h>
7 8
#include <sys/stat.h>
#include <fcntl.h>
9
#include <unistd.h>
Moritz Lipp's avatar
Moritz Lipp committed
10 11 12

#include "database.h"

Moritz Lipp's avatar
Moritz Lipp committed
13 14 15
#define BOOKMARKS "bookmarks"
#define HISTORY "history"

16 17 18
#define KEY_PAGE "page"
#define KEY_OFFSET "offset"
#define KEY_SCALE "scale"
19

20 21 22 23 24 25
#define file_lock_set(fd, cmd) \
  { \
  struct flock lock = { .l_type = cmd, .l_start = 0, .l_whence = SEEK_SET, .l_len = 0}; \
  fcntl(fd, F_SETLK, lock); \
  }

26 27
/* forward declaration */
static bool zathura_db_check_file(const char* path);
28
static GKeyFile* zathura_db_read_key_file_from_file(const char* path);
29
static void zathura_db_write_key_file_to_file(const char* file, GKeyFile* key_file);
30 31
static void cb_zathura_db_watch_file(GFileMonitor* monitor, GFile* file, GFile*
    other_file, GFileMonitorEvent event, zathura_database_t* database);
32

Moritz Lipp's avatar
Moritz Lipp committed
33 34
struct zathura_database_s
{
35
  char* bookmark_path;
36
  GKeyFile* bookmarks;
37 38 39
  GFileMonitor* bookmark_monitor;

  char* history_path;
40
  GKeyFile* history;
41
  GFileMonitor* history_monitor;
Moritz Lipp's avatar
Moritz Lipp committed
42 43 44 45 46
};

zathura_database_t*
zathura_db_init(const char* dir)
{
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
  if (dir == NULL) {
    goto error_ret;
  }

  zathura_database_t* db = calloc(1, sizeof(zathura_database_t));
  if (db == NULL) {
    goto error_ret;
  }

  /* bookmarks */
  db->bookmark_path = g_build_filename(dir, BOOKMARKS, NULL);
  if (db->bookmark_path == NULL ||
      zathura_db_check_file(db->bookmark_path) == false) {
    goto error_free;
  }

  GFile* bookmark_file = g_file_new_for_path(db->bookmark_path);
  if (bookmark_file != NULL) {
    db->bookmark_monitor = g_file_monitor(bookmark_file, G_FILE_MONITOR_NONE, NULL, NULL);
  } else {
    goto error_free;
  }
  g_object_unref(bookmark_file);

71 72 73 74 75 76 77
  g_signal_connect(
      G_OBJECT(db->bookmark_monitor),
      "changed",
      G_CALLBACK(cb_zathura_db_watch_file),
      db
  );

78
  db->bookmarks = zathura_db_read_key_file_from_file(db->bookmark_path);
79 80 81 82
  if (db->bookmarks == NULL) {
    goto error_free;
  }

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
  /* history */
  db->history_path = g_build_filename(dir, HISTORY, NULL);
  if (db->history_path == NULL ||
      zathura_db_check_file(db->history_path) == false) {
    goto error_free;
  }

  GFile* history_file = g_file_new_for_path(db->history_path);
  if (history_file != NULL) {
    db->history_monitor = g_file_monitor(history_file, G_FILE_MONITOR_NONE, NULL, NULL);
  } else {
    goto error_free;
  }
  g_object_unref(history_file);

98 99 100 101 102 103 104
  g_signal_connect(
      G_OBJECT(db->history_monitor),
      "changed",
      G_CALLBACK(cb_zathura_db_watch_file),
      db
  );

105
  db->history = zathura_db_read_key_file_from_file(db->history_path);
106 107 108 109 110 111 112 113 114 115 116 117
  if (db->history == NULL) {
    goto error_free;
  }

  return db;

error_free:

  zathura_db_free(db);

error_ret:

Moritz Lipp's avatar
Moritz Lipp committed
118 119 120 121 122 123
  return NULL;
}

void
zathura_db_free(zathura_database_t* db)
{
124 125 126 127 128 129 130 131 132 133 134
  if (db == NULL) {
    return;
  }

  /* bookmarks */
  g_free(db->bookmark_path);

  if (db->bookmark_monitor != NULL) {
    g_object_unref(db->bookmark_monitor);
  }

135 136 137
  if (db->bookmarks != NULL) {
    g_key_file_free(db->bookmarks);
  }
138 139 140 141 142 143 144 145 146 147

  /* history */
  g_free(db->history_path);

  if (db->history_monitor != NULL) {
    g_object_unref(db->history_monitor);
  }

  /* database */
  free(db);
Moritz Lipp's avatar
Moritz Lipp committed
148 149 150 151 152 153
}

bool
zathura_db_add_bookmark(zathura_database_t* db, const char* file,
    zathura_bookmark_t* bookmark)
{
154 155
  if (db == NULL || db->bookmarks == NULL || db->bookmark_path == NULL || file
      == NULL || bookmark == NULL || bookmark->id == NULL) {
156 157 158
    return false;
  }

159 160
  g_key_file_set_integer(db->bookmarks, file, bookmark->id, bookmark->page);

161
  zathura_db_write_key_file_to_file(db->bookmark_path, db->bookmarks);
162 163

  return true;
Moritz Lipp's avatar
Moritz Lipp committed
164 165 166 167 168 169
}

bool
zathura_db_remove_bookmark(zathura_database_t* db, const char* file, const char*
    id)
{
170 171
  if (db == NULL || db->bookmarks == NULL || db->bookmark_path == NULL || file
      == NULL || id == NULL) {
172 173 174
    return false;
  }

175 176 177
  if (g_key_file_has_group(db->bookmarks, file) == TRUE) {
    g_key_file_remove_group(db->bookmarks, file, NULL);

178
    zathura_db_write_key_file_to_file(db->bookmark_path, db->bookmarks);
179 180 181 182

    return true;
  }

Moritz Lipp's avatar
Moritz Lipp committed
183 184 185 186 187 188
  return false;
}

girara_list_t*
zathura_db_load_bookmarks(zathura_database_t* db, const char* file)
{
189
  if (db == NULL || db->bookmarks == NULL || file == NULL) {
190 191 192
    return NULL;
  }

193 194 195 196
  if (g_key_file_has_group(db->bookmarks, file) == FALSE) {
    return NULL;
  }

197 198
  girara_list_t* result = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
      (girara_free_function_t) zathura_bookmark_free);
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
  if (result == NULL) {
    return NULL;
  }

  girara_list_set_free_function(result, (girara_free_function_t) zathura_bookmark_free);

  gsize length;
  char** keys = g_key_file_get_keys(db->bookmarks, file, &length, NULL);
  if (keys == NULL) {
    girara_list_free(result);
    return NULL;
  }

  for (gsize i = 0; i < length; i++) {
    zathura_bookmark_t* bookmark = g_malloc0(sizeof(zathura_bookmark_t));

    bookmark->id   = g_strdup(keys[i]);
    bookmark->page = g_key_file_get_integer(db->bookmarks, file, keys[i], NULL);

    girara_list_append(result, bookmark);
  }

  return result;
Moritz Lipp's avatar
Moritz Lipp committed
222 223 224 225 226 227
}

bool
zathura_db_set_fileinfo(zathura_database_t* db, const char* file, unsigned int
    page, int offset, float scale)
{
Moritz Lipp's avatar
Moritz Lipp committed
228 229 230 231
  if (db == NULL || db->history == NULL || file == NULL) {
    return false;
  }

232 233
  char* tmp = g_strdup_printf("%f", scale);
  if (tmp == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
234 235 236
    return false;
  }

237 238 239
  g_key_file_set_integer(db->history, file, KEY_PAGE,   page);
  g_key_file_set_integer(db->history, file, KEY_OFFSET, offset);
  g_key_file_set_string (db->history, file, KEY_SCALE,  tmp);
Moritz Lipp's avatar
Moritz Lipp committed
240

241
  g_free(tmp);
Moritz Lipp's avatar
Moritz Lipp committed
242

243
  zathura_db_write_key_file_to_file(db->history_path, db->history);
Moritz Lipp's avatar
Moritz Lipp committed
244

Moritz Lipp's avatar
Moritz Lipp committed
245
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
246 247 248 249 250 251
}

bool
zathura_db_get_fileinfo(zathura_database_t* db, const char* file, unsigned int*
    page, int* offset, float* scale)
{
Moritz Lipp's avatar
Moritz Lipp committed
252 253
  if (db == NULL || db->history == NULL || file == NULL || page == NULL ||
      offset == NULL || scale == NULL) {
254 255 256
    return false;
  }

257 258
  if (g_key_file_has_group(db->history, file) == FALSE) {
    return false;
Moritz Lipp's avatar
Moritz Lipp committed
259 260
  }

261 262 263 264 265
  *page   = g_key_file_get_integer(db->history, file, KEY_PAGE, NULL);
  *offset = g_key_file_get_integer(db->history, file, KEY_OFFSET, NULL);
  *scale  = strtof(g_key_file_get_string(db->history, file, KEY_SCALE, NULL), NULL);

  return true;
Moritz Lipp's avatar
Moritz Lipp committed
266
}
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288

static bool
zathura_db_check_file(const char* path)
{
  if (path == NULL) {
    return false;
  }

  if (g_file_test(path, G_FILE_TEST_EXISTS) == false) {
    FILE* file = fopen(path, "w");
    if (file != NULL) {
      fclose(file);
    } else {
      return false;
    }
  } else if (g_file_test(path, G_FILE_TEST_IS_REGULAR) == false) {
    return false;
  }

  return true;
}

289
static GKeyFile*
290
zathura_db_read_key_file_from_file(const char* path)
291 292 293 294 295
{
  if (path == NULL) {
    return NULL;
  }

296 297 298 299 300 301
  /* open file */
  int fd = open(path, O_RDWR);
  if (fd == -1) {
    return NULL;
  }

302 303
  GKeyFile* key_file = g_key_file_new();
  if (key_file == NULL) {
304 305 306 307 308 309 310 311 312 313
    close(fd);
    return NULL;
  }

  /* read config file */
  file_lock_set(fd, F_WRLCK);
  char* content = girara_file_read_from_fd(fd);
  if (content == NULL) {
    file_lock_set(fd, F_UNLCK);
    close(fd);
Moritz Lipp's avatar
Moritz Lipp committed
314 315
    return NULL;
  }
316
  file_lock_set(fd, F_UNLCK);
Moritz Lipp's avatar
Moritz Lipp committed
317

318 319 320
  close(fd);

  /* parse config file */
321 322 323 324 325 326 327
  size_t contentlen = strlen(content);
  if (contentlen == 0) {
    static const char dummy_content[] = "# nothing";
    static const size_t dummy_len = sizeof(dummy_content) - 1;

    free(content);
    content = malloc(sizeof(char) * (dummy_len + 1));
328
    content = memcpy(content, dummy_content, dummy_len + 1);
329 330 331
    contentlen = dummy_len;
  }

332
  GError* error = NULL;
333
  if (g_key_file_load_from_data(key_file, content, contentlen,
334 335 336
        G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error) ==
      FALSE) {
    if (error->code != 1) /* ignore empty file */ {
337
      free(content);
338 339
      g_key_file_free(key_file);
      g_error_free(error);
340
      return NULL;
341 342 343 344 345
    }

    g_error_free(error);
  }

346 347
  free(content);

348 349 350 351
  return key_file;
}

static void
352
zathura_db_write_key_file_to_file(const char* file, GKeyFile* key_file)
353
{
354
  if (file == NULL || key_file == NULL) {
355 356 357
    return;
  }

358
  gchar* content = g_key_file_to_data(key_file, NULL, NULL);
359 360 361 362
  if (content == NULL) {
    return;
  }

363 364 365
  /* open file */
  int fd = open(file, O_RDWR);
  if (fd == -1) {
366 367 368 369
    g_free(content);
    return;
  }

370 371 372 373 374 375
  file_lock_set(fd, F_WRLCK);
  write(fd, content, strlen(content));
  file_lock_set(fd, F_UNLCK);

  close(fd);

376
  g_free(content);
377 378
}

379 380 381 382 383 384 385 386 387 388 389 390 391 392
static void
cb_zathura_db_watch_file(GFileMonitor* UNUSED(monitor), GFile* file, GFile* UNUSED(other_file),
    GFileMonitorEvent event, zathura_database_t* database)
{
  if (event != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT || database == NULL) {
    return;
  }

  char* path = g_file_get_path(file);
  if (path == NULL) {
    return;
  }

  if (database->bookmark_path && strcmp(database->bookmark_path, path) == 0) {
393
    database->bookmarks = zathura_db_read_key_file_from_file(database->bookmark_path);
394
  } else if (database->history_path && strcmp(database->history_path, path) == 0) {
395
    database->history = zathura_db_read_key_file_from_file(database->history_path);
396 397
  }
}