Go Back

0 /*

1 * ISC License

2 * Copyright (c) 2023 RMF <rawmonk@firemail.cc>

3 */

4 #include <stdlib.h>

5 #include <stdio.h>

6 #include <stdint.h>

7 #include <string.h>

8 #include <wchar.h>

9 #include <pthread.h>

10 #include "macro.h"

11 #include "strlcpy.h"

12 #include "error.h"

13 #include "config.h"

14 #include "utf8.h"

15 #include "url.h"

16 #include "storage.h"

17 #define HISTORY_INTERNAL

18 #include "history.h"

19

20 #define HISTORY "history.txt"

21 pthread_mutex_t history_mutex = PTHREAD_MUTEX_INITIALIZER;

22

23 struct history_entry *history = NULL;

24

25 int history_load(const char *path) {

26

27 struct history_entry entry = {0};

28 struct history_entry *last = NULL;

29 FILE *f;

30 unsigned int i, part, count;

31

32 if (!config.maximumHistorySize) return 0;

33

34 if (!(f = storage_fopen(path, "r"))) return ERROR_STORAGE_ACCESS;

35

36 count = i = part = 0;

37 for (;;) {

38 uint32_t ch;

39 char *ptr;

40 if (utf8_fgetc(f, &ch)) break;

41 if (ch == '\n') {

42 struct history_entry *new;

43 i = 0;

44 part = 0;

45 new = malloc(sizeof(*new));

46 if (!new) {

47 fclose(f);

48 return ERROR_MEMORY_FAILURE;

49 }

50 *new = entry;

51 if (last) {

52 last->next = new;

53 } else {

54 new->next = NULL;

55 history = new;

56 }

57 last = new;

58 memset(&entry, 0, sizeof(entry));

59 count++;

60 if (count >= (unsigned)config.maximumHistorySize)

61 break;

62 continue;

63 }

64 if (!part && ch <= ' ') {

65 part = 1;

66 i = 0;

67 continue;

68 }

69 if (part == 1) {

70 if (ch <= ' ') continue;

71 part = 2;

72 }

73 if (i >= (part ? sizeof(entry.title) : sizeof(entry.url))) {

74 continue;

75 }

76 ptr = part ? entry.title : entry.url;

77 i += utf8_unicode_to_char(&ptr[i], ch);

78 }

79 fclose(f);

80 return 0;

81 }

82

83 void history_init() {

84 if (!config.enableHistory) return;

85 history_load(HISTORY);

86 }

87

88 int history_write(const char *path) {

89

90 FILE *f;

91 struct history_entry *entry;

92

93 f = storage_fopen(path, "w");

94 if (!f) return -1;

95 for (entry = history; entry; entry = entry->next) {

96 fprintf(f, "%s %s\n", entry->url, entry->title);

97 }

98 fclose(f);

99

100 return 0;

101 }

102

103 int history_clear() {

104 history_free();

105 history = NULL;

106 return history_save();

107 }

108

109 int history_save() {

110 return history_write(HISTORY);

111 }

112

113 int history_add(const char *url, const char *title) {

114

115 struct history_entry *entry;

116

117 if (!config.enableHistory) return 0;

118 entry = malloc(sizeof(*entry));

119 if (!entry) return ERROR_MEMORY_FAILURE;

120 url_hide_query(title, V(entry->title));

121 if (strstr(url, "gemini://") == url) {

122 url_hide_query(url, V(entry->url));

123 } else {

124 UTF8CPY(entry->title, title);

125 }

126 pthread_mutex_lock(&history_mutex);

127 entry->next = history;

128 history = entry;

129 pthread_mutex_unlock(&history_mutex);

130 return 0;

131 }

132

133 void history_free() {

134 struct history_entry *next;

135 while (history) {

136 next = history->next;

137 free(history);

138 history = next;

139 }

140 }

141