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

3
#define _POSIX_SOURCE
4
#define _XOPEN_SOURCE 500
5

6
7
8
#include <glib.h>
#include <stdlib.h>
#include <string.h>
9
10
#include <sys/stat.h>
#include <fcntl.h>
11
#include <unistd.h>
12
13
#include <girara/utils.h>
#include <girara/datastructures.h>
14
#include <girara/input-history.h>
Moritz Lipp's avatar
Moritz Lipp committed
15

16
#include "database-plain.h"
Moritz Lipp's avatar
Moritz Lipp committed
17

18
19
20
21
22
23
24
25
26
27
28
29
30
#define BOOKMARKS                 "bookmarks"
#define HISTORY                   "history"
#define INPUT_HISTORY             "input-history"

#define KEY_PAGE                  "page"
#define KEY_OFFSET                "offset"
#define KEY_SCALE                 "scale"
#define KEY_ROTATE                "rotate"
#define KEY_PAGES_PER_ROW         "pages-per-row"
#define KEY_FIRST_PAGE_COLUMN     "first-page-column"
#define KEY_POSITION_X            "position-x"
#define KEY_POSITION_Y            "position-y"
#define KEY_JUMPLIST              "jumplist"
31

32
33
34
35
#ifdef __GNU__
#include <sys/file.h>
#define file_lock_set(fd, cmd) flock(fd, cmd)
#else
36
37
38
#define file_lock_set(fd, cmd) \
  { \
  struct flock lock = { .l_type = cmd, .l_start = 0, .l_whence = SEEK_SET, .l_len = 0}; \
Sebastian Ramacher's avatar
Sebastian Ramacher committed
39
  fcntl(fd, F_SETLK, lock); \
40
  }
41
#endif
42

43
static void zathura_database_interface_init(ZathuraDatabaseInterface* iface);
44
static void io_interface_init(GiraraInputHistoryIOInterface* iface);
45
46

G_DEFINE_TYPE_WITH_CODE(ZathuraPlainDatabase, zathura_plaindatabase, G_TYPE_OBJECT,
47
48
                        G_IMPLEMENT_INTERFACE(ZATHURA_TYPE_DATABASE, zathura_database_interface_init)
                        G_IMPLEMENT_INTERFACE(GIRARA_TYPE_INPUT_HISTORY_IO, io_interface_init))
49

50
51
52
53
54
55
56
57
58
59
static void           plain_finalize(GObject* object);
static bool           plain_add_bookmark(zathura_database_t* db, const char* file, zathura_bookmark_t* bookmark);
static bool           plain_remove_bookmark(zathura_database_t* db, const char* file, const char* id);
static girara_list_t* plain_load_bookmarks(zathura_database_t* db, const char* file);
static girara_list_t* plain_load_jumplist(zathura_database_t* db, const char* file);
static bool           plain_save_jumplist(zathura_database_t* db, const char* file, girara_list_t* jumplist);
static bool           plain_set_fileinfo(zathura_database_t* db, const char* file, zathura_fileinfo_t* file_info);
static bool           plain_get_fileinfo(zathura_database_t* db, const char* file, zathura_fileinfo_t* file_info);
static void           plain_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec);
static void           plain_io_append(GiraraInputHistoryIO* db, const char*);
60
static girara_list_t* plain_io_read(GiraraInputHistoryIO* db);
61

62
/* forward declaration */
63
64
65
66
static bool           zathura_db_check_file(const char* path);
static GKeyFile*      zathura_db_read_key_file_from_file(const char* path);
static void           zathura_db_write_key_file_to_file(const char* file, GKeyFile* key_file);
static void           cb_zathura_db_watch_file(GFileMonitor* monitor, GFile* file, GFile* other_file, GFileMonitorEvent event, zathura_database_t* database);
67

68
typedef struct zathura_plaindatabase_private_s {
69
  char* bookmark_path;
70
  GKeyFile* bookmarks;
71
72
73
  GFileMonitor* bookmark_monitor;

  char* history_path;
74
  GKeyFile* history;
75
  GFileMonitor* history_monitor;
76
77

  char* input_history_path;
78
79
80
81
82
} zathura_plaindatabase_private_t;

#define ZATHURA_PLAINDATABASE_GET_PRIVATE(obj) \
  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ZATHURA_TYPE_PLAINDATABASE, zathura_plaindatabase_private_t))

Moritz Lipp's avatar
Moritz Lipp committed
83
enum {
84
85
  PROP_0,
  PROP_PATH
Moritz Lipp's avatar
Moritz Lipp committed
86
87
};

88
89
90
91
92
93
94
95
96
97
98
99
100
101
static char*
prepare_filename(const char* file)
{
  if (file == NULL) {
    return NULL;
  }

  if (strchr(file, '[') == NULL && strchr(file, ']') == NULL) {
    return g_strdup(file);
  }

  return g_base64_encode((const guchar*) file, strlen(file));
}

102
103
104
105
static void
zathura_database_interface_init(ZathuraDatabaseInterface* iface)
{
  /* initialize interface */
Moritz Lipp's avatar
Moritz Lipp committed
106
  iface->add_bookmark    = plain_add_bookmark;
107
  iface->remove_bookmark = plain_remove_bookmark;
Moritz Lipp's avatar
Moritz Lipp committed
108
  iface->load_bookmarks  = plain_load_bookmarks;
109
110
  iface->load_jumplist   = plain_load_jumplist;
  iface->save_jumplist   = plain_save_jumplist;
Moritz Lipp's avatar
Moritz Lipp committed
111
112
  iface->set_fileinfo    = plain_set_fileinfo;
  iface->get_fileinfo    = plain_get_fileinfo;
113
114
}

115
116
117
118
119
120
121
122
static void
io_interface_init(GiraraInputHistoryIOInterface* iface)
{
  /* initialize interface */
  iface->append = plain_io_append;
  iface->read = plain_io_read;
}

123
124
125
126
127
128
129
130
static void
zathura_plaindatabase_class_init(ZathuraPlainDatabaseClass* class)
{
  /* add private members */
  g_type_class_add_private(class, sizeof(zathura_plaindatabase_private_t));

  /* override methods */
  GObjectClass* object_class = G_OBJECT_CLASS(class);
Moritz Lipp's avatar
Moritz Lipp committed
131
  object_class->finalize     = plain_finalize;
132
133
134
  object_class->set_property = plain_set_property;

  g_object_class_install_property(object_class, PROP_PATH,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
135
136
    g_param_spec_string("path", "path", "path to directory where the bookmarks and history are locates",
      NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
137
138
139
140
141
142
}

static void
zathura_plaindatabase_init(ZathuraPlainDatabase* db)
{
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);
Moritz Lipp's avatar
Moritz Lipp committed
143

144
145
146
147
148
149
150
  priv->bookmark_path         = NULL;
  priv->bookmark_monitor      = NULL;
  priv->bookmarks             = NULL;
  priv->history_path          = NULL;
  priv->history_monitor       = NULL;
  priv->history               = NULL;
  priv->input_history_path    = NULL;
151
152
}

Moritz Lipp's avatar
Moritz Lipp committed
153
zathura_database_t*
154
zathura_plaindatabase_new(const char* path)
Moritz Lipp's avatar
Moritz Lipp committed
155
{
156
  g_return_val_if_fail(path != NULL && strlen(path) != 0, NULL);
157

158
159
160
161
162
  zathura_database_t* db = g_object_new(ZATHURA_TYPE_PLAINDATABASE, "path", path, NULL);
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);
  if (priv->bookmark_path == NULL) {
    g_object_unref(db);
    return NULL;
163
  }
Moritz Lipp's avatar
Moritz Lipp committed
164

165
166
167
168
169
170
171
  return db;
}

static void
plain_db_init(ZathuraPlainDatabase* db, const char* dir)
{
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);
172
173

  /* bookmarks */
174
175
  priv->bookmark_path = g_build_filename(dir, BOOKMARKS, NULL);
  if (zathura_db_check_file(priv->bookmark_path) == false) {
176
177
178
    goto error_free;
  }

179
  GFile* bookmark_file = g_file_new_for_path(priv->bookmark_path);
180
  if (bookmark_file != NULL) {
181
    priv->bookmark_monitor = g_file_monitor(bookmark_file, G_FILE_MONITOR_NONE, NULL, NULL);
182
183
184
  } else {
    goto error_free;
  }
Moritz Lipp's avatar
Moritz Lipp committed
185

186
187
  g_object_unref(bookmark_file);

188
  g_signal_connect(
Moritz Lipp's avatar
Moritz Lipp committed
189
190
191
192
    G_OBJECT(priv->bookmark_monitor),
    "changed",
    G_CALLBACK(cb_zathura_db_watch_file),
    db
193
194
  );

195
196
  priv->bookmarks = zathura_db_read_key_file_from_file(priv->bookmark_path);
  if (priv->bookmarks == NULL) {
197
198
199
    goto error_free;
  }

200
  /* history */
201
202
  priv->history_path = g_build_filename(dir, HISTORY, NULL);
  if (zathura_db_check_file(priv->history_path) == false) {
203
204
205
    goto error_free;
  }

206
  GFile* history_file = g_file_new_for_path(priv->history_path);
207
  if (history_file != NULL) {
208
    priv->history_monitor = g_file_monitor(history_file, G_FILE_MONITOR_NONE, NULL, NULL);
209
210
211
  } else {
    goto error_free;
  }
Moritz Lipp's avatar
Moritz Lipp committed
212

213
214
  g_object_unref(history_file);

215
  g_signal_connect(
Moritz Lipp's avatar
Moritz Lipp committed
216
217
218
219
    G_OBJECT(priv->history_monitor),
    "changed",
    G_CALLBACK(cb_zathura_db_watch_file),
    db
220
221
  );

222
223
  priv->history = zathura_db_read_key_file_from_file(priv->history_path);
  if (priv->history == NULL) {
224
225
    goto error_free;
  }
Moritz Lipp's avatar
Moritz Lipp committed
226

227
228
229
230
231
232
  /* input history */
  priv->input_history_path = g_build_filename(dir, INPUT_HISTORY, NULL);
  if (zathura_db_check_file(priv->input_history_path) == false) {
    goto error_free;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
233
  return;
234
235
236

error_free:

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
  /* bookmarks */
  g_free(priv->bookmark_path);
  priv->bookmark_path = NULL;

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

  if (priv->bookmarks != NULL) {
    g_key_file_free(priv->bookmarks);
    priv->bookmarks = NULL;
  }

  /* history */
  g_free(priv->history_path);
  priv->history_path = NULL;
254

255
256
257
258
  if (priv->history_monitor != NULL) {
    g_object_unref(priv->history_monitor);
    priv->history_monitor = NULL;
  }
259

260
261
262
263
  if (priv->history != NULL) {
    g_key_file_free(priv->history);
    priv->history = NULL;
  }
264
265
266
267

  /* input history */
  g_free(priv->input_history_path);
  priv->input_history_path = NULL;
Moritz Lipp's avatar
Moritz Lipp committed
268
269
}

270
271
static void
plain_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec)
Moritz Lipp's avatar
Moritz Lipp committed
272
{
273
274
275
276
277
278
279
280
  ZathuraPlainDatabase* db = ZATHURA_PLAINDATABASE(object);

  switch (prop_id) {
    case PROP_PATH:
      plain_db_init(db, g_value_get_string(value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
281
  }
282
}
283

284
285
286
287
288
static void
plain_finalize(GObject* object)
{
  ZathuraPlainDatabase* db = ZATHURA_PLAINDATABASE(object);
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);
Moritz Lipp's avatar
Moritz Lipp committed
289

290
  /* bookmarks */
291
  g_free(priv->bookmark_path);
292

293
294
  if (priv->bookmark_monitor != NULL) {
    g_object_unref(priv->bookmark_monitor);
295
296
  }

297
298
  if (priv->bookmarks != NULL) {
    g_key_file_free(priv->bookmarks);
299
  }
300
301

  /* history */
302
  g_free(priv->history_path);
303

304
305
  if (priv->history_monitor != NULL) {
    g_object_unref(priv->history_monitor);
306
307
  }

308
309
310
311
  if (priv->history != NULL) {
    g_key_file_free(priv->history);
  }

312
313
314
  /* input history */
  g_free(priv->input_history_path);

315
  G_OBJECT_CLASS(zathura_plaindatabase_parent_class)->finalize(object);
Moritz Lipp's avatar
Moritz Lipp committed
316
317
}

318
319
static bool
plain_add_bookmark(zathura_database_t* db, const char* file,
Moritz Lipp's avatar
Moritz Lipp committed
320
                   zathura_bookmark_t* bookmark)
Moritz Lipp's avatar
Moritz Lipp committed
321
{
322
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);
Moritz Lipp's avatar
Moritz Lipp committed
323
  if (priv->bookmarks == NULL || priv->bookmark_path == NULL ||
324
      bookmark->id == NULL) {
325
326
327
    return false;
  }

328
  char* name = prepare_filename(file);
329
330
331
332
333
334
335
336
337
338
339
340
  char* val_list[] = { g_strdup_printf("%d", bookmark->page),
                       g_ascii_dtostr(g_malloc(G_ASCII_DTOSTR_BUF_SIZE), G_ASCII_DTOSTR_BUF_SIZE, bookmark->x),
                       g_ascii_dtostr(g_malloc(G_ASCII_DTOSTR_BUF_SIZE), G_ASCII_DTOSTR_BUF_SIZE, bookmark->y) };

  gsize num_vals = sizeof(val_list)/sizeof(char *);

  g_key_file_set_string_list(priv->bookmarks, name, bookmark->id, (const char**)val_list, num_vals);

  for (unsigned int i = 0; i < num_vals; ++i) {
    g_free(val_list[i]);
  }

341
  g_free(name);
342

343
  zathura_db_write_key_file_to_file(priv->bookmark_path, priv->bookmarks);
344
345

  return true;
Moritz Lipp's avatar
Moritz Lipp committed
346
347
}

348
static bool
349
plain_remove_bookmark(zathura_database_t* db, const char* file, const char* id)
Moritz Lipp's avatar
Moritz Lipp committed
350
{
351
352
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);
  if (priv->bookmarks == NULL || priv->bookmark_path == NULL) {
353
354
355
    return false;
  }

356
357
  char* name = prepare_filename(file);
  if (g_key_file_has_group(priv->bookmarks, name) == TRUE) {
358
    if (g_key_file_remove_key(priv->bookmarks, name, id, NULL) == TRUE) {
359

360
361
      zathura_db_write_key_file_to_file(priv->bookmark_path, priv->bookmarks);
      g_free(name);
362

363
364
      return true;
    }
365
  }
366
  g_free(name);
367

Moritz Lipp's avatar
Moritz Lipp committed
368
369
370
  return false;
}

371
372
static girara_list_t*
plain_load_bookmarks(zathura_database_t* db, const char* file)
Moritz Lipp's avatar
Moritz Lipp committed
373
{
374
375
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);
  if (priv->bookmarks == NULL) {
376
377
378
    return NULL;
  }

379
380
381
  char* name = prepare_filename(file);
  if (g_key_file_has_group(priv->bookmarks, name) == FALSE) {
    g_free(name);
382
383
384
    return NULL;
  }

Moritz Lipp's avatar
Moritz Lipp committed
385
  girara_list_t* result = girara_sorted_list_new2((girara_compare_function_t)
Moritz Lipp's avatar
Moritz Lipp committed
386
387
                          zathura_bookmarks_compare, (girara_free_function_t)
                          zathura_bookmark_free);
388

389
390
  gsize num_keys;
  char** keys = g_key_file_get_keys(priv->bookmarks, name, &num_keys, NULL);
391
392
  if (keys == NULL) {
    girara_list_free(result);
393
    g_free(name);
394
395
396
    return NULL;
  }

397
398
399
400
  char **val_list = NULL;
  gsize num_vals = 0;

  for (gsize i = 0; i < num_keys; i++) {
401
402
403
    zathura_bookmark_t* bookmark = g_malloc0(sizeof(zathura_bookmark_t));

    bookmark->id   = g_strdup(keys[i]);
404
405
406
407
408
409
410
411
412
413
414
415
416
    val_list = g_key_file_get_string_list(priv->bookmarks, name, keys[i], &num_vals, NULL);

    bookmark->page = atoi(val_list[0]);

    if (num_vals == 3) {
      bookmark->x = g_ascii_strtod(val_list[1], NULL);
      bookmark->y = g_ascii_strtod(val_list[2], NULL);
    } else if (num_vals == 1) {
       bookmark->x = DBL_MIN;
       bookmark->y = DBL_MIN;
    } else {
      girara_debug("This must be a BUG");
    }
417
418

    girara_list_append(result, bookmark);
419
    g_strfreev(val_list);
420
421
  }

422
  g_free(name);
Moritz Lipp's avatar
Moritz Lipp committed
423
424
  g_strfreev(keys);

425
  return result;
Moritz Lipp's avatar
Moritz Lipp committed
426
427
}

428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
static girara_list_t*
get_jumplist_from_str(const char* str)
{
  g_return_val_if_fail(str != NULL, NULL);

  if (*str == 0) {
    return girara_list_new2(g_free);
  }

  girara_list_t* result = girara_list_new2(g_free);
  char* copy = g_strdup(str);
  char* token = strtok(copy, " ");

  while (token != NULL) {
    zathura_jump_t* jump = g_malloc0(sizeof(zathura_jump_t));

    jump->page = strtoul(token, NULL, 0);
    token = strtok(NULL, " ");
    jump->x = strtod(token, NULL);
    token = strtok(NULL, " ");
    jump->y = strtod(token, NULL);
    girara_list_append(result, jump);
    token = strtok(NULL, " ");
  }

  g_free(copy);

  return result;
}

static girara_list_t*
plain_load_jumplist(zathura_database_t* db, const char* file)
{
  g_return_val_if_fail(db != NULL && file != NULL, NULL);

  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);
  char* str_value = g_key_file_get_string(priv->history, file, KEY_JUMPLIST, NULL);

  if (str_value == NULL) {
    return girara_list_new2(g_free);
  }

  return get_jumplist_from_str(str_value);
}

static bool
plain_save_jumplist(zathura_database_t* db, const char* file, girara_list_t* jumplist)
{
  g_return_val_if_fail(db != NULL && file != NULL && jumplist != NULL, false);

  GString* str_val = g_string_new(NULL);

  GIRARA_LIST_FOREACH(jumplist, zathura_jump_t*, iter, jump)
  g_string_append(str_val, g_strdup_printf("%d ", jump->page));
  g_string_append(str_val, g_strdup_printf("%.20f ", jump->x));
  g_string_append(str_val, g_strdup_printf("%.20f ", jump->y));
  GIRARA_LIST_FOREACH_END(jumplist, zathura_jump_t*, iter, jump);

  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);

  g_key_file_set_string(priv->history, file, KEY_JUMPLIST, str_val->str);
  zathura_db_write_key_file_to_file(priv->history_path, priv->history);
  g_string_free(str_val, TRUE);

  return true;
}

495
static bool
496
plain_set_fileinfo(zathura_database_t* db, const char* file, zathura_fileinfo_t*
Moritz Lipp's avatar
Moritz Lipp committed
497
                   file_info)
Moritz Lipp's avatar
Moritz Lipp committed
498
{
499
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);
500
  if (priv->history == NULL || file_info == NULL || file == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
501
502
503
    return false;
  }

504
  char* name = prepare_filename(file);
Moritz Lipp's avatar
Moritz Lipp committed
505

506
507
  g_key_file_set_integer(priv->history, name, KEY_PAGE,   file_info->current_page);
  g_key_file_set_integer(priv->history, name, KEY_OFFSET, file_info->page_offset);
Moritz Lipp's avatar
Moritz Lipp committed
508

509
  char* tmp = g_strdup_printf("%.20f", file_info->scale);
510
  g_key_file_set_string (priv->history, name, KEY_SCALE, tmp);
Moritz Lipp's avatar
Moritz Lipp committed
511
  g_free(tmp);
512

513
514
515
  g_key_file_set_integer(priv->history, name, KEY_ROTATE,            file_info->rotation);
  g_key_file_set_integer(priv->history, name, KEY_PAGES_PER_ROW,     file_info->pages_per_row);
  g_key_file_set_integer(priv->history, name, KEY_FIRST_PAGE_COLUMN, file_info->first_page_column);
516

517
  tmp = g_strdup_printf("%.20f", file_info->position_x);
518
  g_key_file_set_string(priv->history,  name, KEY_POSITION_X, tmp);
Moritz Lipp's avatar
Moritz Lipp committed
519
  g_free(tmp);
520

521
  tmp = g_strdup_printf("%.20f", file_info->position_y);
522
  g_key_file_set_string(priv->history,  name, KEY_POSITION_Y, tmp);
523
  g_free(tmp);
Moritz Lipp's avatar
Moritz Lipp committed
524

525
526
  g_free(name);

527
  zathura_db_write_key_file_to_file(priv->history_path, priv->history);
Moritz Lipp's avatar
Moritz Lipp committed
528

Moritz Lipp's avatar
Moritz Lipp committed
529
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
530
531
}

532
static bool
533
plain_get_fileinfo(zathura_database_t* db, const char* file, zathura_fileinfo_t*
Moritz Lipp's avatar
Moritz Lipp committed
534
                   file_info)
Moritz Lipp's avatar
Moritz Lipp committed
535
{
536
537
538
539
  if (db == NULL || file == NULL || file_info == NULL) {
    return false;
  }

540
541
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);
  if (priv->history == NULL) {
542
543
544
    return false;
  }

545
546
  char* name = prepare_filename(file);
  if (g_key_file_has_group(priv->history, name) == FALSE) {
Moritz Lipp's avatar
Moritz Lipp committed
547
    g_free(name);
548
    return false;
Moritz Lipp's avatar
Moritz Lipp committed
549
550
  }

551
552
553
554
555
  file_info->current_page      = g_key_file_get_integer(priv->history, name, KEY_PAGE, NULL);
  file_info->page_offset       = g_key_file_get_integer(priv->history, name, KEY_OFFSET, NULL);
  file_info->rotation          = g_key_file_get_integer(priv->history, name, KEY_ROTATE, NULL);
  file_info->pages_per_row     = g_key_file_get_integer(priv->history, name, KEY_PAGES_PER_ROW, NULL);
  file_info->first_page_column = g_key_file_get_integer(priv->history, name, KEY_FIRST_PAGE_COLUMN, NULL);
556

557
  char* scale_string = g_key_file_get_string(priv->history, name, KEY_SCALE, NULL);
558
559
560
561
  if (scale_string != NULL) {
    file_info->scale  = strtod(scale_string, NULL);
    g_free(scale_string);
  }
562
563

  char* position_x_string = g_key_file_get_string(priv->history, name, KEY_POSITION_X, NULL);
564
565
566
567
  if (position_x_string != NULL) {
    file_info->position_x = strtod(position_x_string, NULL);
    g_free(position_x_string);
  }
568
569

  char* position_y_string = g_key_file_get_string(priv->history, name, KEY_POSITION_Y, NULL);
570
571
572
573
  if (position_y_string != NULL) {
    file_info->position_y = strtod(position_y_string, NULL);
    g_free(position_y_string);
  }
574

575
  g_free(name);
576

577
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
578
}
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600

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;
}

601
static GKeyFile*
602
zathura_db_read_key_file_from_file(const char* path)
603
604
605
606
607
{
  if (path == NULL) {
    return NULL;
  }

608
  /* open file */
609
  FILE* file = fopen(path, "rw");
610
  if (file == NULL) {
611
612
613
    return NULL;
  }

614
615
  GKeyFile* key_file = g_key_file_new();
  if (key_file == NULL) {
616
    fclose(file);
617
618
619
620
    return NULL;
  }

  /* read config file */
621
622
623
624
  file_lock_set(fileno(file), F_WRLCK);
  char* content = girara_file_read2(file);
  file_lock_set(fileno(file), F_UNLCK);
  fclose(file);
625
  if (content == NULL) {
626
    g_key_file_free(key_file);
Moritz Lipp's avatar
Moritz Lipp committed
627
628
    return NULL;
  }
629
630

  /* parse config file */
631
632
633
634
635
636
637
  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));
638
    content = memcpy(content, dummy_content, dummy_len + 1);
639
640
641
    contentlen = dummy_len;
  }

642
  GError* error = NULL;
643
  if (g_key_file_load_from_data(key_file, content, contentlen,
Moritz Lipp's avatar
Moritz Lipp committed
644
                                G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error) ==
645
      FALSE) {
Moritz Lipp's avatar
Moritz Lipp committed
646
    if (error->code != 1) { /* ignore empty file */
647
      free(content);
648
649
      g_key_file_free(key_file);
      g_error_free(error);
650
      return NULL;
651
652
653
654
655
    }

    g_error_free(error);
  }

656
657
  free(content);

658
659
660
661
  return key_file;
}

static void
662
zathura_db_write_key_file_to_file(const char* file, GKeyFile* key_file)
663
{
664
  if (file == NULL || key_file == NULL) {
665
666
667
    return;
  }

668
  gchar* content = g_key_file_to_data(key_file, NULL, NULL);
669
670
671
672
  if (content == NULL) {
    return;
  }

673
  /* open file */
674
  int fd = open(file, O_RDWR | O_TRUNC);
675
  if (fd == -1) {
676
677
678
679
    g_free(content);
    return;
  }

680
  file_lock_set(fd, F_WRLCK);
681
682
683
  if (write(fd, content, strlen(content)) == 0) {
    girara_error("Failed to write to %s", file);
  }
684
685
686
687
  file_lock_set(fd, F_UNLCK);

  close(fd);

688
  g_free(content);
689
690
}

691
692
static void
cb_zathura_db_watch_file(GFileMonitor* UNUSED(monitor), GFile* file, GFile* UNUSED(other_file),
Moritz Lipp's avatar
Moritz Lipp committed
693
                         GFileMonitorEvent event, zathura_database_t* database)
694
695
696
697
698
699
700
701
702
703
{
  if (event != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT || database == NULL) {
    return;
  }

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

704
705
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(database);
  if (priv->bookmark_path && strcmp(priv->bookmark_path, path) == 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
706
707
708
    if (priv->bookmarks != NULL) {
      g_key_file_free(priv->bookmarks);
    }
Moritz Lipp's avatar
Moritz Lipp committed
709

710
711
    priv->bookmarks = zathura_db_read_key_file_from_file(priv->bookmark_path);
  } else if (priv->history_path && strcmp(priv->history_path, path) == 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
712
713
714
    if (priv->history != NULL) {
      g_key_file_free(priv->history);
    }
Moritz Lipp's avatar
Moritz Lipp committed
715

716
    priv->history = zathura_db_read_key_file_from_file(priv->history_path);
717
  }
718
719

  g_free(path);
720
}
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741

static girara_list_t*
plain_io_read(GiraraInputHistoryIO* db)
{
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);

  /* open file */
  FILE* file = fopen(priv->input_history_path, "r");
  if (file == NULL) {
    return NULL;
  }

  /* read input history file */
  file_lock_set(fileno(file), F_RDLCK);
  char* content = girara_file_read2(file);
  file_lock_set(fileno(file), F_UNLCK);
  fclose(file);

  girara_list_t* res = girara_list_new2(g_free);
  char** tmp = g_strsplit(content, "\n", 0);
  for (size_t i = 0; tmp[i] != NULL; ++i) {
742
    if (strlen(tmp[i]) == 0 || strchr(":/?", tmp[i][0]) == NULL) {
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
      continue;
    }
    girara_list_append(res, g_strdup(tmp[i]));
  }
  g_strfreev(tmp);
  free(content);

  return res;
}

#include <errno.h>

static void
plain_io_append(GiraraInputHistoryIO* db, const char* input)
{
  zathura_plaindatabase_private_t* priv = ZATHURA_PLAINDATABASE_GET_PRIVATE(db);

  /* open file */
  FILE* file = fopen(priv->input_history_path, "r+");
  if (file == NULL) {
    return;
  }

  /* read input history file */
  file_lock_set(fileno(file), F_WRLCK);
  char* content = girara_file_read2(file);

  rewind(file);
  if (ftruncate(fileno(file), 0) != 0) {
    free(content);
    file_lock_set(fileno(file), F_UNLCK);
    fclose(file);
    return;
  }

  char** tmp = g_strsplit(content, "\n", 0);
  free(content);

  /* write input history file */
  for (size_t i = 0; tmp[i] != NULL; ++i) {
783
    if (strlen(tmp[i]) == 0 || strchr(":/?", tmp[i][0]) == NULL || strcmp(tmp[i], input) == 0) {
784
785
786
787
788
789
790
791
792
793
      continue;
    }
    fprintf(file, "%s\n", tmp[i]);
  }
  g_strfreev(tmp);
  fprintf(file, "%s\n", input);

  file_lock_set(fileno(file), F_UNLCK);
  fclose(file);
}