diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-23 22:11:24 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-23 22:11:24 +0300 |
commit | 27ebfc904b9b3eac3ef5479ed0d0ba166fac1543 (patch) | |
tree | 0a7a74bf586701645107c27a04564b41368260db /src/bookmarks.c | |
parent | 48c3553660f4c20fc3bb4fb5df95c058adf9dd87 (diff) |
Bookmarks: TOML syntax; field for manual order
The bookmarks file is now saved in a TOML subset and named "bookmarks.ini". This makes it more robust and forwards compatible.
Added a field for sort order, since that will be used for manual ordering.
Diffstat (limited to 'src/bookmarks.c')
-rw-r--r-- | src/bookmarks.c | 141 |
1 files changed, 128 insertions, 13 deletions
diff --git a/src/bookmarks.c b/src/bookmarks.c index c27efbfe..94f4be4e 100644 --- a/src/bookmarks.c +++ b/src/bookmarks.c | |||
@@ -31,13 +31,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
31 | #include <the_Foundation/path.h> | 31 | #include <the_Foundation/path.h> |
32 | #include <the_Foundation/regexp.h> | 32 | #include <the_Foundation/regexp.h> |
33 | #include <the_Foundation/stringset.h> | 33 | #include <the_Foundation/stringset.h> |
34 | #include <the_Foundation/toml.h> | ||
34 | 35 | ||
35 | void init_Bookmark(iBookmark *d) { | 36 | void init_Bookmark(iBookmark *d) { |
36 | init_String(&d->url); | 37 | init_String(&d->url); |
37 | init_String(&d->title); | 38 | init_String(&d->title); |
38 | init_String(&d->tags); | 39 | init_String(&d->tags); |
39 | iZap(d->when); | 40 | iZap(d->when); |
40 | d->sourceId = 0; | 41 | d->parentId = 0; |
42 | d->order = 0; | ||
41 | } | 43 | } |
42 | 44 | ||
43 | void deinit_Bookmark(iBookmark *d) { | 45 | void deinit_Bookmark(iBookmark *d) { |
@@ -83,7 +85,8 @@ static int cmpTitleAscending_Bookmark_(const iBookmark **a, const iBookmark **b) | |||
83 | 85 | ||
84 | /*----------------------------------------------------------------------------------------------*/ | 86 | /*----------------------------------------------------------------------------------------------*/ |
85 | 87 | ||
86 | static const char *fileName_Bookmarks_ = "bookmarks.txt"; | 88 | static const char *oldFileName_Bookmarks_ = "bookmarks.txt"; |
89 | static const char *fileName_Bookmarks_ = "bookmarks.ini"; /* since v1.7 (TOML subset) */ | ||
87 | 90 | ||
88 | struct Impl_Bookmarks { | 91 | struct Impl_Bookmarks { |
89 | iMutex * mtx; | 92 | iMutex * mtx; |
@@ -123,16 +126,19 @@ void clear_Bookmarks(iBookmarks *d) { | |||
123 | unlock_Mutex(d->mtx); | 126 | unlock_Mutex(d->mtx); |
124 | } | 127 | } |
125 | 128 | ||
129 | static void insertId_Bookmarks_(iBookmarks *d, iBookmark *bookmark, int id) { | ||
130 | bookmark->node.key = id; | ||
131 | insert_Hash(&d->bookmarks, &bookmark->node); | ||
132 | } | ||
133 | |||
126 | static void insert_Bookmarks_(iBookmarks *d, iBookmark *bookmark) { | 134 | static void insert_Bookmarks_(iBookmarks *d, iBookmark *bookmark) { |
127 | lock_Mutex(d->mtx); | 135 | lock_Mutex(d->mtx); |
128 | bookmark->node.key = ++d->idEnum; | 136 | insertId_Bookmarks_(d, bookmark, ++d->idEnum); |
129 | insert_Hash(&d->bookmarks, &bookmark->node); | ||
130 | unlock_Mutex(d->mtx); | 137 | unlock_Mutex(d->mtx); |
131 | } | 138 | } |
132 | 139 | ||
133 | void load_Bookmarks(iBookmarks *d, const char *dirPath) { | 140 | static void loadOldFormat_Bookmarks(iBookmarks *d, const char *dirPath) { |
134 | clear_Bookmarks(d); | 141 | iFile *f = newCStr_File(concatPath_CStr(dirPath, oldFileName_Bookmarks_)); |
135 | iFile *f = newCStr_File(concatPath_CStr(dirPath, fileName_Bookmarks_)); | ||
136 | if (open_File(f, readOnly_FileMode | text_FileMode)) { | 142 | if (open_File(f, readOnly_FileMode | text_FileMode)) { |
137 | const iRangecc src = range_Block(collect_Block(readAll_File(f))); | 143 | const iRangecc src = range_Block(collect_Block(readAll_File(f))); |
138 | iRangecc line = iNullRange; | 144 | iRangecc line = iNullRange; |
@@ -170,6 +176,101 @@ void load_Bookmarks(iBookmarks *d, const char *dirPath) { | |||
170 | iRelease(f); | 176 | iRelease(f); |
171 | } | 177 | } |
172 | 178 | ||
179 | /*----------------------------------------------------------------------------------------------*/ | ||
180 | |||
181 | iDeclareType(BookmarkLoader) | ||
182 | |||
183 | struct Impl_BookmarkLoader { | ||
184 | iTomlParser *toml; | ||
185 | iBookmarks * bookmarks; | ||
186 | iBookmark * bm; | ||
187 | }; | ||
188 | |||
189 | static void handleTable_BookmarkLoader_(void *context, const iString *table, iBool isStart) { | ||
190 | iBookmarkLoader *d = context; | ||
191 | if (isStart) { | ||
192 | iAssert(!d->bm); | ||
193 | d->bm = new_Bookmark(); | ||
194 | const int id = toInt_String(table); | ||
195 | d->bookmarks->idEnum = iMax(d->bookmarks->idEnum, id); | ||
196 | insertId_Bookmarks_(d->bookmarks, d->bm, id); | ||
197 | } | ||
198 | else { | ||
199 | d->bm = NULL; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | static void handleKeyValue_BookmarkLoader_(void *context, const iString *table, const iString *key, | ||
204 | const iTomlValue *tv) { | ||
205 | iBookmarkLoader *d = context; | ||
206 | iBookmark *bm = d->bm; | ||
207 | if (bm) { | ||
208 | iUnused(table); /* it's the current one */ | ||
209 | if (!cmp_String(key, "url") && tv->type == string_TomlType) { | ||
210 | set_String(&bm->url, tv->value.string); | ||
211 | } | ||
212 | else if (!cmp_String(key, "title") && tv->type == string_TomlType) { | ||
213 | set_String(&bm->title, tv->value.string); | ||
214 | } | ||
215 | else if (!cmp_String(key, "tags") && tv->type == string_TomlType) { | ||
216 | set_String(&bm->tags, tv->value.string); | ||
217 | } | ||
218 | else if (!cmp_String(key, "icon") && tv->type == int64_TomlType) { | ||
219 | bm->icon = (iChar) tv->value.int64; | ||
220 | } | ||
221 | else if (!cmp_String(key, "created") && tv->type == int64_TomlType) { | ||
222 | initSeconds_Time(&bm->when, tv->value.int64); | ||
223 | } | ||
224 | else if (!cmp_String(key, "parent") && tv->type == int64_TomlType) { | ||
225 | bm->parentId = tv->value.int64; | ||
226 | } | ||
227 | else if (!cmp_String(key, "order") && tv->type == int64_TomlType) { | ||
228 | bm->order = tv->value.int64; | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | |||
233 | static void init_BookmarkLoader(iBookmarkLoader *d, iBookmarks *bookmarks) { | ||
234 | d->toml = new_TomlParser(); | ||
235 | setHandlers_TomlParser(d->toml, handleTable_BookmarkLoader_, handleKeyValue_BookmarkLoader_, d); | ||
236 | d->bookmarks = bookmarks; | ||
237 | d->bm = NULL; | ||
238 | } | ||
239 | |||
240 | static void deinit_BookmarkLoader(iBookmarkLoader *d) { | ||
241 | delete_TomlParser(d->toml); | ||
242 | } | ||
243 | |||
244 | static void load_BookmarkLoader(iBookmarkLoader *d, iFile *file) { | ||
245 | if (!parse_TomlParser(d->toml, collect_String(readString_File(file)))) { | ||
246 | fprintf(stderr, "[Bookmarks] syntax error(s) in %s\n", cstr_String(path_File(file))); | ||
247 | } | ||
248 | } | ||
249 | |||
250 | iDefineTypeConstructionArgs(BookmarkLoader, (iBookmarks *b), b) | ||
251 | |||
252 | /*----------------------------------------------------------------------------------------------*/ | ||
253 | |||
254 | void load_Bookmarks(iBookmarks *d, const char *dirPath) { | ||
255 | clear_Bookmarks(d); | ||
256 | /* Load new .ini bookmarks, if present. */ | ||
257 | iFile *f = iClob(newCStr_File(concatPath_CStr(dirPath, fileName_Bookmarks_))); | ||
258 | if (!open_File(f, readOnly_FileMode | text_FileMode)) { | ||
259 | /* As a fallback, try loading the v1.6 bookmarks file. */ | ||
260 | loadOldFormat_Bookmarks(d, dirPath); | ||
261 | /* Set ordering based on titles. */ | ||
262 | iConstForEach(PtrArray, i, list_Bookmarks(d, cmpTitleAscending_Bookmark_, NULL, NULL)) { | ||
263 | iBookmark *bm = i.ptr; | ||
264 | bm->order = index_PtrArrayConstIterator(&i) + 1; | ||
265 | } | ||
266 | return; | ||
267 | } | ||
268 | iBookmarkLoader loader; | ||
269 | init_BookmarkLoader(&loader, d); | ||
270 | load_BookmarkLoader(&loader, f); | ||
271 | deinit_BookmarkLoader(&loader); | ||
272 | } | ||
273 | |||
173 | void save_Bookmarks(const iBookmarks *d, const char *dirPath) { | 274 | void save_Bookmarks(const iBookmarks *d, const char *dirPath) { |
174 | lock_Mutex(d->mtx); | 275 | lock_Mutex(d->mtx); |
175 | iRegExp *remotePattern = iClob(new_RegExp("\\bremote\\b", caseSensitive_RegExpOption)); | 276 | iRegExp *remotePattern = iClob(new_RegExp("\\bremote\\b", caseSensitive_RegExpOption)); |
@@ -185,12 +286,26 @@ void save_Bookmarks(const iBookmarks *d, const char *dirPath) { | |||
185 | continue; | 286 | continue; |
186 | } | 287 | } |
187 | format_String(str, | 288 | format_String(str, |
188 | "%08x %.0lf %s\n%s\n%s\n", | 289 | "[%d]\n" |
290 | "url = \"%s\"\n" | ||
291 | "title = \"%s\"\n" | ||
292 | "tags = \"%s\"\n" | ||
293 | "icon = 0x%x\n" | ||
294 | "created = %.0f # %s\n", | ||
295 | id_Bookmark(bm), | ||
296 | cstrCollect_String(quote_String(&bm->url, iFalse)), | ||
297 | cstrCollect_String(quote_String(&bm->title, iFalse)), | ||
298 | cstrCollect_String(quote_String(&bm->tags, iFalse)), | ||
189 | bm->icon, | 299 | bm->icon, |
190 | seconds_Time(&bm->when), | 300 | seconds_Time(&bm->when), |
191 | cstr_String(&bm->url), | 301 | cstrCollect_String(format_Time(&bm->when, "%Y-%m-%d"))); |
192 | cstr_String(&bm->title), | 302 | if (bm->parentId) { |
193 | cstr_String(&bm->tags)); | 303 | appendFormat_String(str, "parent = %d\n", bm->parentId); |
304 | } | ||
305 | if (bm->order) { | ||
306 | appendFormat_String(str, "order = %d\n", bm->order); | ||
307 | } | ||
308 | appendCStr_String(str, "\n"); | ||
194 | writeData_File(f, cstr_String(str), size_String(str)); | 309 | writeData_File(f, cstr_String(str), size_String(str)); |
195 | } | 310 | } |
196 | } | 311 | } |
@@ -223,7 +338,7 @@ iBool remove_Bookmarks(iBookmarks *d, uint32_t id) { | |||
223 | if (hasTag_Bookmark(bm, remoteSource_BookmarkTag)) { | 338 | if (hasTag_Bookmark(bm, remoteSource_BookmarkTag)) { |
224 | iForEach(Hash, i, &d->bookmarks) { | 339 | iForEach(Hash, i, &d->bookmarks) { |
225 | iBookmark *j = (iBookmark *) i.value; | 340 | iBookmark *j = (iBookmark *) i.value; |
226 | if (j->sourceId == id_Bookmark(bm)) { | 341 | if (j->parentId == id_Bookmark(bm)) { |
227 | remove_HashIterator(&i); | 342 | remove_HashIterator(&i); |
228 | delete_Bookmark(j); | 343 | delete_Bookmark(j); |
229 | } | 344 | } |
@@ -452,7 +567,7 @@ void requestFinished_Bookmarks(iBookmarks *d, iGmRequest *req) { | |||
452 | } | 567 | } |
453 | const uint32_t bmId = add_Bookmarks(d, absUrl, titleStr, remoteTag, 0x2913); | 568 | const uint32_t bmId = add_Bookmarks(d, absUrl, titleStr, remoteTag, 0x2913); |
454 | iBookmark *bm = get_Bookmarks(d, bmId); | 569 | iBookmark *bm = get_Bookmarks(d, bmId); |
455 | bm->sourceId = *(uint32_t *) userData_Object(req); | 570 | bm->parentId = *(uint32_t *) userData_Object(req); |
456 | delete_String(titleStr); | 571 | delete_String(titleStr); |
457 | } | 572 | } |
458 | delete_String(urlStr); | 573 | delete_String(urlStr); |