Mercurial > geeqie.yaz
annotate src/filedata.c @ 737:8a8873e7a552
Make shell command and its option rc file options instead of hardcoded strings.
This allows users to modify the shell command that execute "editors".
Two new options appear in rc file:
- shell.path (default to "/bin/sh")
- shell.options (default to "-c")
These options can only be changed from the rc file, not at runtime.
Tests are made to check that shell.path is not empty and lead to
an executable file.
author | zas_ |
---|---|
date | Thu, 22 May 2008 20:22:13 +0000 |
parents | eda074e91ddd |
children | f73df252aa05 |
rev | line source |
---|---|
586 | 1 /* |
2 * Geeqie | |
3 * (C) 2006 John Ellis | |
4 * Copyright (C) 2008 The Geeqie Team | |
5 * | |
6 * Author: John Ellis | |
7 * | |
8 * This software is released under the GNU General Public License (GNU GPL). | |
9 * Please read the included file COPYING for more information. | |
10 * This software comes with no warranty of any kind, use at your own risk! | |
11 */ | |
12 | |
13 | |
14 #include "main.h" | |
15 #include "filedata.h" | |
16 | |
17 #include "filefilter.h" | |
18 #include "cache.h" | |
19 #include "rcfile.h" | |
20 #include "secure_save.h" | |
21 #include "thumb_standard.h" | |
22 #include "ui_fileops.h" | |
23 | |
24 | |
25 static gint sidecar_file_priority(const gchar *path); | |
26 | |
27 | |
28 /* | |
29 *----------------------------------------------------------------------------- | |
30 * text conversion utils | |
31 *----------------------------------------------------------------------------- | |
32 */ | |
33 | |
34 gchar *text_from_size(gint64 size) | |
35 { | |
36 gchar *a, *b; | |
37 gchar *s, *d; | |
38 gint l, n, i; | |
39 | |
40 /* what I would like to use is printf("%'d", size) | |
41 * BUT: not supported on every libc :( | |
42 */ | |
43 if (size > G_MAXUINT) | |
44 { | |
45 /* the %lld conversion is not valid in all libcs, so use a simple work-around */ | |
46 a = g_strdup_printf("%d%09d", (guint)(size / 1000000000), (guint)(size % 1000000000)); | |
47 } | |
48 else | |
49 { | |
50 a = g_strdup_printf("%d", (guint)size); | |
51 } | |
52 l = strlen(a); | |
53 n = (l - 1)/ 3; | |
54 if (n < 1) return a; | |
55 | |
56 b = g_new(gchar, l + n + 1); | |
57 | |
58 s = a; | |
59 d = b; | |
60 i = l - n * 3; | |
61 while (*s != '\0') | |
62 { | |
63 if (i < 1) | |
64 { | |
65 i = 3; | |
66 *d = ','; | |
67 d++; | |
68 } | |
69 | |
70 *d = *s; | |
71 s++; | |
72 d++; | |
73 i--; | |
74 } | |
75 *d = '\0'; | |
76 | |
77 g_free(a); | |
78 return b; | |
79 } | |
80 | |
81 gchar *text_from_size_abrev(gint64 size) | |
82 { | |
83 if (size < (gint64)1024) | |
84 { | |
85 return g_strdup_printf(_("%d bytes"), (gint)size); | |
86 } | |
87 if (size < (gint64)1048576) | |
88 { | |
89 return g_strdup_printf(_("%.1f K"), (double)size / 1024.0); | |
90 } | |
91 if (size < (gint64)1073741824) | |
92 { | |
93 return g_strdup_printf(_("%.1f MB"), (double)size / 1048576.0); | |
94 } | |
95 | |
96 /* to avoid overflowing the double, do division in two steps */ | |
97 size /= 1048576; | |
98 return g_strdup_printf(_("%.1f GB"), (double)size / 1024.0); | |
99 } | |
100 | |
101 /* note: returned string is valid until next call to text_from_time() */ | |
102 const gchar *text_from_time(time_t t) | |
103 { | |
104 static gchar *ret = NULL; | |
105 gchar buf[128]; | |
106 gint buflen; | |
107 struct tm *btime; | |
108 GError *error = NULL; | |
109 | |
110 btime = localtime(&t); | |
111 | |
112 /* the %x warning about 2 digit years is not an error */ | |
113 buflen = strftime(buf, sizeof(buf), "%x %H:%M", btime); | |
114 if (buflen < 1) return ""; | |
115 | |
116 g_free(ret); | |
117 ret = g_locale_to_utf8(buf, buflen, NULL, NULL, &error); | |
118 if (error) | |
119 { | |
673
fbebf5cf4a55
Do not use printf() directly but use new wrapper function log_printf() instead.
zas_
parents:
671
diff
changeset
|
120 log_printf("Error converting locale strftime to UTF-8: %s\n", error->message); |
586 | 121 g_error_free(error); |
122 return ""; | |
123 } | |
124 | |
125 return ret; | |
126 } | |
127 | |
128 /* | |
129 *----------------------------------------------------------------------------- | |
130 * file info struct | |
131 *----------------------------------------------------------------------------- | |
132 */ | |
133 | |
134 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source); | |
135 static void file_data_check_sidecars(FileData *fd); | |
136 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd); | |
137 | |
138 | |
139 static void file_data_set_path(FileData *fd, const gchar *path) | |
140 { | |
141 | |
725 | 142 if (strcmp(path, G_DIR_SEPARATOR_S) == 0) |
586 | 143 { |
144 fd->path = g_strdup(path); | |
145 fd->name = fd->path; | |
146 fd->extension = fd->name + 1; | |
147 return; | |
148 } | |
149 | |
150 fd->path = g_strdup(path); | |
151 fd->name = filename_from_path(fd->path); | |
152 | |
153 if (strcmp(fd->name, "..") == 0) | |
154 { | |
155 gchar *dir = remove_level_from_path(path); | |
156 g_free(fd->path); | |
157 fd->path = remove_level_from_path(dir); | |
158 g_free(dir); | |
159 fd->name = ".."; | |
160 fd->extension = fd->name + 2; | |
161 return; | |
162 } | |
163 else if (strcmp(fd->name, ".") == 0) | |
164 { | |
165 g_free(fd->path); | |
166 fd->path = remove_level_from_path(path); | |
167 fd->name = "."; | |
168 fd->extension = fd->name + 1; | |
169 return; | |
170 } | |
171 | |
172 fd->extension = extension_from_path(fd->path); | |
173 if (fd->extension == NULL) | |
174 fd->extension = fd->name + strlen(fd->name); | |
175 } | |
176 | |
177 static void file_data_check_changed_files(FileData *fd, struct stat *st) | |
178 { | |
179 GList *work; | |
180 if (fd->size != st->st_size || | |
181 fd->date != st->st_mtime) | |
182 { | |
183 fd->size = st->st_size; | |
184 fd->date = st->st_mtime; | |
185 if (fd->pixbuf) g_object_unref(fd->pixbuf); | |
186 fd->pixbuf = NULL; | |
187 } | |
188 | |
189 work = fd->sidecar_files; | |
190 while (work) | |
191 { | |
192 FileData *sfd = work->data; | |
193 struct stat st; | |
194 | |
195 if (!stat_utf8(sfd->path, &st)) | |
196 { | |
197 file_data_disconnect_sidecar_file(fd, sfd); | |
198 } | |
199 | |
200 file_data_check_changed_files(sfd, &st); | |
201 work = work->next; | |
202 } | |
203 } | |
204 | |
205 static GHashTable *file_data_pool = NULL; | |
206 | |
207 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars) | |
208 { | |
209 FileData *fd; | |
210 | |
211 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars); | |
212 | |
213 if (!file_data_pool) | |
214 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal); | |
215 | |
216 fd = g_hash_table_lookup(file_data_pool, path_utf8); | |
217 if (fd) | |
218 { | |
219 file_data_check_changed_files(fd, st); | |
220 DEBUG_2("file_data_pool hit: '%s'", fd->path); | |
221 return file_data_ref(fd); | |
222 } | |
223 | |
224 fd = g_new0(FileData, 1); | |
225 | |
226 file_data_set_path(fd, path_utf8); | |
227 | |
228 fd->original_path = g_strdup(path_utf8); | |
229 fd->size = st->st_size; | |
230 fd->date = st->st_mtime; | |
231 fd->pixbuf = NULL; | |
232 fd->sidecar_files = NULL; | |
233 fd->ref = 1; | |
234 fd->magick = 0x12345678; | |
235 | |
236 g_hash_table_insert(file_data_pool, fd->original_path, fd); | |
237 | |
238 if (check_sidecars && sidecar_file_priority(fd->extension)) | |
239 file_data_check_sidecars(fd); | |
240 return fd; | |
241 } | |
242 | |
243 static void file_data_check_sidecars(FileData *fd) | |
244 { | |
245 int base_len = fd->extension - fd->path; | |
246 GString *fname = g_string_new_len(fd->path, base_len); | |
247 FileData *parent_fd = NULL; | |
248 GList *work = sidecar_ext_get_list(); | |
249 while (work) | |
250 { | |
251 /* check for possible sidecar files; | |
252 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent, | |
253 they have fd->ref set to 0 and file_data unref must chack and free them all together | |
254 (using fd->ref would cause loops and leaks) | |
255 */ | |
256 | |
257 FileData *new_fd; | |
258 | |
259 gchar *ext = work->data; | |
260 work = work->next; | |
261 | |
262 if (strcmp(ext, fd->extension) == 0) | |
263 { | |
264 new_fd = fd; /* processing the original file */ | |
265 } | |
266 else | |
267 { | |
268 struct stat nst; | |
269 g_string_truncate(fname, base_len); | |
270 g_string_append(fname, ext); | |
271 | |
272 if (!stat_utf8(fname->str, &nst)) | |
273 continue; | |
274 | |
275 new_fd = file_data_new(fname->str, &nst, FALSE); | |
276 new_fd->ref--; /* do not use ref here */ | |
277 } | |
278 | |
279 if (!parent_fd) | |
280 parent_fd = new_fd; /* parent is the one with the highest prio, found first */ | |
281 else | |
282 file_data_merge_sidecar_files(parent_fd, new_fd); | |
283 } | |
284 g_string_free(fname, TRUE); | |
285 } | |
286 | |
287 | |
288 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars) | |
289 { | |
290 gchar *path_utf8 = path_to_utf8(path); | |
291 FileData *ret = file_data_new(path_utf8, st, check_sidecars); | |
292 g_free(path_utf8); | |
293 return ret; | |
294 } | |
295 | |
296 FileData *file_data_new_simple(const gchar *path_utf8) | |
297 { | |
298 struct stat st; | |
299 | |
300 if (!stat_utf8(path_utf8, &st)) | |
301 { | |
302 st.st_size = 0; | |
303 st.st_mtime = 0; | |
304 } | |
305 | |
306 return file_data_new(path_utf8, &st, TRUE); | |
307 } | |
308 | |
309 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd) | |
310 { | |
311 sfd->parent = target; | |
312 if(!g_list_find(target->sidecar_files, sfd)) | |
313 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd); | |
314 return target; | |
315 } | |
316 | |
317 | |
318 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source) | |
319 { | |
320 GList *work; | |
321 file_data_add_sidecar_file(target, source); | |
322 | |
323 work = source->sidecar_files; | |
324 while (work) | |
325 { | |
326 FileData *sfd = work->data; | |
327 file_data_add_sidecar_file(target, sfd); | |
328 work = work->next; | |
329 } | |
330 | |
331 g_list_free(source->sidecar_files); | |
332 source->sidecar_files = NULL; | |
333 | |
334 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE); | |
335 return target; | |
336 } | |
337 | |
338 | |
339 | |
340 FileData *file_data_ref(FileData *fd) | |
341 { | |
342 if (fd == NULL) return NULL; | |
343 | |
344 // return g_memdup(fd, sizeof(FileData)); | |
345 g_assert(fd->magick == 0x12345678); | |
346 fd->ref++; | |
347 return fd; | |
348 } | |
349 | |
350 static void file_data_free(FileData *fd) | |
351 { | |
352 g_assert(fd->magick == 0x12345678); | |
353 g_assert(fd->ref == 0); | |
354 | |
355 g_hash_table_remove(file_data_pool, fd->original_path); | |
356 | |
357 g_free(fd->path); | |
358 g_free(fd->original_path); | |
359 if (fd->pixbuf) g_object_unref(fd->pixbuf); | |
360 | |
361 | |
362 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */ | |
363 | |
364 file_data_change_info_free(NULL, fd); | |
365 g_free(fd); | |
366 } | |
367 | |
368 void file_data_unref(FileData *fd) | |
369 { | |
370 if (fd == NULL) return; | |
371 g_assert(fd->magick == 0x12345678); | |
372 | |
373 fd->ref--; | |
374 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path); | |
375 | |
376 if (fd->ref == 0) | |
377 { | |
378 FileData *parent = fd->parent ? fd->parent : fd; | |
379 | |
380 GList *work; | |
381 | |
382 if (parent->ref > 0) | |
383 return; | |
384 | |
385 work = parent->sidecar_files; | |
386 while (work) | |
387 { | |
388 FileData *sfd = work->data; | |
389 if (sfd->ref > 0) | |
390 return; | |
391 work = work->next; | |
392 } | |
393 | |
394 /* none of parent/children is referenced, we can free everything */ | |
395 | |
396 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, parent->path); | |
397 | |
398 work = parent->sidecar_files; | |
399 while (work) | |
400 { | |
401 FileData *sfd = work->data; | |
402 file_data_free(sfd); | |
403 work = work->next; | |
404 } | |
405 | |
406 g_list_free(parent->sidecar_files); | |
407 parent->sidecar_files = NULL; | |
408 | |
409 file_data_free(parent); | |
410 | |
411 } | |
412 } | |
413 | |
414 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd) | |
415 { | |
416 sfd->parent = target; | |
417 g_assert(g_list_find(target->sidecar_files, sfd)); | |
418 | |
419 target->sidecar_files = g_list_remove(target->sidecar_files, sfd); | |
420 sfd->parent = NULL; | |
421 | |
422 if (sfd->ref == 0) { | |
423 file_data_free(sfd); | |
424 return NULL; | |
425 } | |
426 | |
427 return sfd; | |
428 } | |
429 | |
430 /* compare name without extension */ | |
431 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2) | |
432 { | |
433 size_t len1 = fd1->extension - fd1->name; | |
434 size_t len2 = fd2->extension - fd2->name; | |
435 | |
436 if (len1 < len2) return -1; | |
437 if (len1 > len2) return 1; | |
438 | |
439 return strncmp(fd1->name, fd2->name, len1); | |
440 } | |
441 | |
442 gboolean file_data_add_change_info(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest) | |
443 { | |
444 | |
445 FileDataChangeInfo *fdci; | |
446 | |
447 if (fd->change) return FALSE; | |
448 | |
449 fdci = g_new0(FileDataChangeInfo, 1); | |
450 | |
451 fdci->type = type; | |
452 | |
453 if (src) | |
454 fdci->source = g_strdup(src); | |
455 else | |
456 fdci->source = g_strdup(fd->path); | |
457 | |
458 if (dest) | |
459 fdci->dest = g_strdup(dest); | |
460 | |
461 fd->change = fdci; | |
462 return TRUE; | |
463 } | |
464 | |
465 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd) | |
466 { | |
467 if (!fdci && fd) | |
468 fdci = fd->change; | |
469 | |
470 if (!fdci) | |
471 return; | |
472 | |
473 g_free(fdci->source); | |
474 g_free(fdci->dest); | |
475 | |
476 g_free(fdci); | |
477 | |
478 if (fd) | |
479 fd->change = NULL; | |
480 } | |
481 | |
482 | |
483 | |
484 | |
485 /* | |
486 *----------------------------------------------------------------------------- | |
487 * sidecar file info struct | |
488 *----------------------------------------------------------------------------- | |
489 */ | |
490 | |
491 | |
492 | |
493 static gint sidecar_file_priority(const gchar *path) | |
494 { | |
495 const char *extension = extension_from_path(path); | |
496 int i = 1; | |
497 GList *work; | |
498 if (extension == NULL) | |
499 return 0; | |
500 | |
501 work = sidecar_ext_get_list(); | |
502 | |
503 while (work) { | |
504 gchar *ext = work->data; | |
505 work = work->next; | |
506 if (strcmp(extension, ext) == 0) return i; | |
507 i++; | |
508 } | |
509 return 0; | |
510 } | |
511 | |
512 | |
513 /* | |
514 *----------------------------------------------------------------------------- | |
515 * load file list | |
516 *----------------------------------------------------------------------------- | |
517 */ | |
518 | |
519 static SortType filelist_sort_method = SORT_NONE; | |
520 static gint filelist_sort_ascend = TRUE; | |
521 | |
522 | |
523 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb) | |
524 { | |
525 if (!filelist_sort_ascend) | |
526 { | |
527 FileData *tmp = fa; | |
528 fa = fb; | |
529 fb = tmp; | |
530 } | |
531 | |
532 switch (filelist_sort_method) | |
533 { | |
534 case SORT_SIZE: | |
535 if (fa->size < fb->size) return -1; | |
536 if (fa->size > fb->size) return 1; | |
537 return CASE_SORT(fa->name, fb->name); /* fall back to name */ | |
538 break; | |
539 case SORT_TIME: | |
540 if (fa->date < fb->date) return -1; | |
541 if (fa->date > fb->date) return 1; | |
542 return CASE_SORT(fa->name, fb->name); /* fall back to name */ | |
543 break; | |
544 #ifdef HAVE_STRVERSCMP | |
545 case SORT_NUMBER: | |
546 return strverscmp(fa->name, fb->name); | |
547 break; | |
548 #endif | |
549 case SORT_NAME: | |
550 default: | |
551 return CASE_SORT(fa->name, fb->name); | |
552 break; | |
553 } | |
554 } | |
555 | |
556 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gint ascend) | |
557 { | |
558 filelist_sort_method = method; | |
559 filelist_sort_ascend = ascend; | |
560 return filelist_sort_compare_filedata(fa, fb); | |
561 } | |
562 | |
563 static gint filelist_sort_file_cb(void *a, void *b) | |
564 { | |
565 return filelist_sort_compare_filedata(a, b); | |
566 } | |
567 | |
568 GList *filelist_sort_full(GList *list, SortType method, gint ascend, GCompareFunc cb) | |
569 { | |
570 filelist_sort_method = method; | |
571 filelist_sort_ascend = ascend; | |
572 return g_list_sort(list, cb); | |
573 } | |
574 | |
575 GList *filelist_insert_sort_full(GList *list, void *data, SortType method, gint ascend, GCompareFunc cb) | |
576 { | |
577 filelist_sort_method = method; | |
578 filelist_sort_ascend = ascend; | |
579 return g_list_insert_sorted(list, data, cb); | |
580 } | |
581 | |
582 GList *filelist_sort(GList *list, SortType method, gint ascend) | |
583 { | |
584 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb); | |
585 } | |
586 | |
587 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gint ascend) | |
588 { | |
589 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb); | |
590 } | |
591 | |
592 | |
593 static GList *filelist_filter_out_sidecars(GList *flist) | |
594 { | |
595 GList *work = flist; | |
596 GList *flist_filtered = NULL; | |
597 | |
598 while (work) | |
599 { | |
600 FileData *fd = work->data; | |
601 work = work->next; | |
602 if (fd->parent) /* remove fd's that are children */ | |
603 file_data_unref(fd); | |
604 else | |
605 flist_filtered = g_list_prepend(flist_filtered, fd); | |
606 } | |
607 g_list_free(flist); | |
608 return flist_filtered; | |
609 } | |
610 | |
611 static gint filelist_read_real(const gchar *path, GList **files, GList **dirs, gint follow_symlinks) | |
612 { | |
613 DIR *dp; | |
614 struct dirent *dir; | |
615 struct stat ent_sbuf; | |
616 gchar *pathl; | |
617 GList *dlist; | |
618 GList *flist; | |
619 | |
620 dlist = NULL; | |
621 flist = NULL; | |
622 | |
623 pathl = path_from_utf8(path); | |
624 if (!pathl || (dp = opendir(pathl)) == NULL) | |
625 { | |
626 g_free(pathl); | |
627 if (files) *files = NULL; | |
628 if (dirs) *dirs = NULL; | |
629 return FALSE; | |
630 } | |
631 | |
632 while ((dir = readdir(dp)) != NULL) | |
633 { | |
634 gchar *name = dir->d_name; | |
635 if (options->file_filter.show_hidden_files || !ishidden(name)) | |
636 { | |
712 | 637 gchar *filepath = g_build_filename(pathl, name, NULL); |
586 | 638 if ((follow_symlinks ? |
639 stat(filepath, &ent_sbuf) : | |
640 lstat(filepath, &ent_sbuf)) >= 0) | |
641 { | |
642 if (S_ISDIR(ent_sbuf.st_mode)) | |
643 { | |
644 /* we ignore the .thumbnails dir for cleanliness */ | |
645 if ((dirs) && | |
646 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) && | |
647 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 && | |
648 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 && | |
649 strcmp(name, THUMB_FOLDER_LOCAL) != 0) | |
650 { | |
651 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE)); | |
652 } | |
653 } | |
654 else | |
655 { | |
656 if ((files) && filter_name_exists(name)) | |
657 { | |
658 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE)); | |
659 } | |
660 } | |
661 } | |
662 g_free(filepath); | |
663 } | |
664 } | |
665 | |
666 closedir(dp); | |
667 | |
668 g_free(pathl); | |
669 | |
670 flist = filelist_filter_out_sidecars(flist); | |
671 | |
672 if (dirs) *dirs = dlist; | |
673 if (files) *files = flist; | |
674 | |
675 return TRUE; | |
676 } | |
677 | |
678 gint filelist_read(const gchar *path, GList **files, GList **dirs) | |
679 { | |
680 return filelist_read_real(path, files, dirs, TRUE); | |
681 } | |
682 | |
683 gint filelist_read_lstat(const gchar *path, GList **files, GList **dirs) | |
684 { | |
685 return filelist_read_real(path, files, dirs, FALSE); | |
686 } | |
687 | |
688 void filelist_free(GList *list) | |
689 { | |
690 GList *work; | |
691 | |
692 work = list; | |
693 while (work) | |
694 { | |
695 file_data_unref((FileData *)work->data); | |
696 work = work->next; | |
697 } | |
698 | |
699 g_list_free(list); | |
700 } | |
701 | |
702 | |
703 GList *filelist_copy(GList *list) | |
704 { | |
705 GList *new_list = NULL; | |
706 GList *work; | |
707 | |
708 work = list; | |
709 while (work) | |
710 { | |
711 FileData *fd; | |
712 | |
713 fd = work->data; | |
714 work = work->next; | |
715 | |
716 new_list = g_list_prepend(new_list, file_data_ref(fd)); | |
717 } | |
718 | |
719 return g_list_reverse(new_list); | |
720 } | |
721 | |
722 GList *filelist_from_path_list(GList *list) | |
723 { | |
724 GList *new_list = NULL; | |
725 GList *work; | |
726 | |
727 work = list; | |
728 while (work) | |
729 { | |
730 gchar *path; | |
731 | |
732 path = work->data; | |
733 work = work->next; | |
734 | |
735 new_list = g_list_prepend(new_list, file_data_new_simple(path)); | |
736 } | |
737 | |
738 return g_list_reverse(new_list); | |
739 } | |
740 | |
741 GList *filelist_to_path_list(GList *list) | |
742 { | |
743 GList *new_list = NULL; | |
744 GList *work; | |
745 | |
746 work = list; | |
747 while (work) | |
748 { | |
749 FileData *fd; | |
750 | |
751 fd = work->data; | |
752 work = work->next; | |
753 | |
754 new_list = g_list_prepend(new_list, g_strdup(fd->path)); | |
755 } | |
756 | |
757 return g_list_reverse(new_list); | |
758 } | |
759 | |
760 GList *filelist_filter(GList *list, gint is_dir_list) | |
761 { | |
762 GList *work; | |
763 | |
764 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list; | |
765 | |
766 work = list; | |
767 while (work) | |
768 { | |
769 FileData *fd = (FileData *)(work->data); | |
770 const gchar *name = fd->name; | |
771 | |
772 if ((!options->file_filter.show_hidden_files && ishidden(name)) || | |
773 (!is_dir_list && !filter_name_exists(name)) || | |
774 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 || | |
775 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) ) | |
776 { | |
777 GList *link = work; | |
778 work = work->next; | |
779 list = g_list_remove_link(list, link); | |
780 file_data_unref(fd); | |
781 g_list_free(link); | |
782 } | |
783 else | |
784 { | |
785 work = work->next; | |
786 } | |
787 } | |
788 | |
789 return list; | |
790 } | |
791 | |
792 /* | |
793 *----------------------------------------------------------------------------- | |
794 * filelist recursive | |
795 *----------------------------------------------------------------------------- | |
796 */ | |
797 | |
798 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b) | |
799 { | |
800 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path); | |
801 } | |
802 | |
803 GList *filelist_sort_path(GList *list) | |
804 { | |
805 return g_list_sort(list, filelist_sort_path_cb); | |
806 } | |
807 | |
808 static void filelist_recursive_append(GList **list, GList *dirs) | |
809 { | |
810 GList *work; | |
811 | |
812 work = dirs; | |
813 while (work) | |
814 { | |
815 FileData *fd = (FileData *)(work->data); | |
816 const gchar *path = fd->path; | |
817 GList *f = NULL; | |
818 GList *d = NULL; | |
819 | |
820 if (filelist_read(path, &f, &d)) | |
821 { | |
822 f = filelist_filter(f, FALSE); | |
823 f = filelist_sort_path(f); | |
824 *list = g_list_concat(*list, f); | |
825 | |
826 d = filelist_filter(d, TRUE); | |
827 d = filelist_sort_path(d); | |
828 filelist_recursive_append(list, d); | |
829 filelist_free(d); | |
830 } | |
831 | |
832 work = work->next; | |
833 } | |
834 } | |
835 | |
836 GList *filelist_recursive(const gchar *path) | |
837 { | |
838 GList *list = NULL; | |
839 GList *d = NULL; | |
840 | |
841 if (!filelist_read(path, &list, &d)) return NULL; | |
842 list = filelist_filter(list, FALSE); | |
843 list = filelist_sort_path(list); | |
844 | |
845 d = filelist_filter(d, TRUE); | |
846 d = filelist_sort_path(d); | |
847 filelist_recursive_append(&list, d); | |
848 filelist_free(d); | |
849 | |
850 return list; | |
851 } | |
590 | 852 |
853 | |
854 | |
855 /* | |
856 * file_data - operates on the given fd | |
857 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent | |
858 */ | |
859 | |
860 | |
861 /* return list of sidecar file extensions in a string */ | |
596 | 862 gchar *file_data_sc_list_to_string(FileData *fd) |
863 { | |
864 GList *work; | |
865 GString *result = g_string_new(""); | |
866 | |
867 work = fd->sidecar_files; | |
868 while (work) | |
869 { | |
870 FileData *sfd = work->data; | |
871 result = g_string_append(result, "+ "); | |
872 result = g_string_append(result, sfd->extension); | |
873 work = work->next; | |
874 if (work) result = g_string_append_c(result, ' '); | |
875 } | |
876 | |
877 return g_string_free(result, FALSE); | |
878 } | |
590 | 879 |
880 | |
881 /* disables / enables grouping for particular file, sends UPDATE notification */ | |
882 void file_data_disable_grouping(FileData *fd); // now file_data_disconnect_sidecar_file, broken | |
883 void file_data_disable_grouping(FileData *fd); | |
884 | |
885 /* runs stat on a file and sends UPDATE notification if it has been changed */ | |
886 void file_data_sc_update(FileData *fd); | |
887 | |
888 | |
889 | |
890 | |
891 /* | |
892 * add FileDataChangeInfo (see typedefs.h) for the given operation | |
893 * uses file_data_add_change_info | |
894 * | |
895 * fails if the fd->change already exists - change operations can't run in parallel | |
896 * fd->change_info works as a lock | |
897 * | |
898 * dest can be NULL - in this case the current name is used for now, it will | |
899 * be changed later | |
900 */ | |
901 | |
902 /* | |
903 FileDataChangeInfo types: | |
904 COPY | |
905 MOVE - patch is changed, name may be changed too | |
906 RENAME - path remains unchanged, name is changed | |
907 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping) | |
908 sidecar names are changed too, extensions are not changed | |
909 DELETE | |
910 UPDATE - file size, date or grouping has been changed | |
911 */ | |
912 | |
913 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest) | |
914 { | |
915 | |
916 FileDataChangeInfo *fdci; | |
917 | |
918 if (fd->change) return FALSE; | |
919 | |
920 fdci = g_new0(FileDataChangeInfo, 1); | |
921 | |
922 fdci->type = type; | |
923 | |
924 if (src) | |
925 fdci->source = g_strdup(src); | |
926 else | |
927 fdci->source = g_strdup(fd->path); | |
928 | |
929 if (dest) | |
930 fdci->dest = g_strdup(dest); | |
931 | |
932 fd->change = fdci; | |
933 | |
934 return TRUE; | |
935 } | |
936 | |
937 void file_data_free_ci(FileData *fd) | |
938 { | |
939 FileDataChangeInfo *fdci = fd->change; | |
940 | |
941 if (!fdci) | |
942 return; | |
943 | |
944 g_free(fdci->source); | |
945 g_free(fdci->dest); | |
946 | |
947 g_free(fdci); | |
948 | |
949 fd->change = NULL; | |
950 } | |
951 | |
952 | |
953 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type) | |
954 { | |
955 GList *work; | |
956 if (fd->parent) fd = fd->parent; | |
957 | |
958 if (fd->change) return FALSE; | |
959 work = fd->sidecar_files; | |
960 while (work) | |
961 { | |
962 FileData *sfd = work->data; | |
963 if (sfd->change) return FALSE; | |
964 work = work->next; | |
965 } | |
966 | |
967 file_data_add_ci(fd, type, NULL, NULL); | |
968 | |
969 work = fd->sidecar_files; | |
970 while (work) | |
971 { | |
972 FileData *sfd = work->data; | |
973 file_data_add_ci(sfd, type, NULL, NULL); | |
974 work = work->next; | |
975 } | |
976 | |
977 return TRUE; | |
978 } | |
979 | |
980 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type) | |
981 { | |
982 GList *work; | |
983 if (fd->parent) fd = fd->parent; | |
984 | |
985 if (!fd->change) return FALSE; | |
986 if (fd->change->type != type) return FALSE; | |
987 work = fd->sidecar_files; | |
988 while (work) | |
989 { | |
990 FileData *sfd = work->data; | |
991 if (!sfd->change) return FALSE; | |
992 if (sfd->change->type != type) return FALSE; | |
993 work = work->next; | |
994 } | |
995 return TRUE; | |
996 } | |
997 | |
998 | |
999 gboolean file_data_sc_add_ci_copy(FileData *fd, gchar *dest_path) | |
1000 { | |
1001 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE; | |
1002 file_data_sc_update_ci_copy(fd, dest_path); | |
1003 return TRUE; | |
1004 } | |
1005 | |
1006 gboolean file_data_sc_add_ci_move(FileData *fd, gchar *dest_path) | |
1007 { | |
1008 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE; | |
1009 file_data_sc_update_ci_move(fd, dest_path); | |
1010 return TRUE; | |
1011 } | |
1012 | |
1013 gboolean file_data_sc_add_ci_rename(FileData *fd, gchar *dest_path) | |
1014 { | |
1015 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE; | |
1016 file_data_sc_update_ci_rename(fd, dest_path); | |
1017 return TRUE; | |
1018 } | |
1019 | |
1020 gboolean file_data_sc_add_ci_delete(FileData *fd) | |
1021 { | |
1022 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE); | |
1023 } | |
1024 | |
1025 gboolean file_data_sc_add_ci_update(FileData *fd) | |
1026 { | |
1027 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_UPDATE); | |
1028 } | |
1029 | |
1030 void file_data_sc_free_ci(FileData *fd) | |
1031 { | |
1032 GList *work; | |
1033 if (fd->parent) fd = fd->parent; | |
1034 | |
1035 file_data_free_ci(fd); | |
1036 | |
1037 work = fd->sidecar_files; | |
1038 while (work) | |
1039 { | |
1040 FileData *sfd = work->data; | |
1041 file_data_free_ci(sfd); | |
1042 work = work->next; | |
1043 } | |
1044 } | |
1045 | |
1046 | |
1047 /* | |
1048 * update existing fd->change, it will be used from dialog callbacks for interactive editing | |
1049 * fails if fd->change does not exist or the change type does not match | |
1050 */ | |
1051 | |
1052 static void file_data_update_ci_dest(FileData *fd, gchar *dest_path) | |
1053 { | |
1054 g_free(fd->change->dest); | |
1055 fd->change->dest = g_strdup(dest_path); | |
1056 } | |
1057 | |
1058 static void file_data_update_ci_dest_preserve_ext(FileData *fd, gchar *dest_path) | |
1059 { | |
1060 const char *extension = extension_from_path(fd->change->source); | |
1061 g_free(fd->change->dest); | |
1062 fd->change->dest = g_strdup_printf("%*s%s", (int)(extension_from_path(dest_path) - dest_path), dest_path, extension); | |
1063 } | |
1064 | |
1065 static void file_data_sc_update_ci(FileData *fd, gchar *dest_path) | |
1066 { | |
1067 GList *work; | |
1068 if (fd->parent) fd = fd->parent; | |
1069 | |
1070 file_data_update_ci_dest(fd, dest_path); | |
1071 work = fd->sidecar_files; | |
1072 while (work) | |
1073 { | |
1074 FileData *sfd = work->data; | |
1075 file_data_update_ci_dest_preserve_ext(sfd, dest_path); | |
1076 work = work->next; | |
1077 } | |
1078 } | |
1079 | |
1080 gint file_data_sc_update_ci_copy(FileData *fd, gchar *dest_path) | |
1081 { | |
1082 if (!file_data_sc_check_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE; | |
1083 file_data_sc_update_ci(fd, dest_path); | |
1084 return TRUE; | |
1085 } | |
1086 | |
1087 gint file_data_sc_update_ci_move(FileData *fd, gchar *dest_path) | |
1088 { | |
1089 if (!file_data_sc_check_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE; | |
1090 file_data_sc_update_ci(fd, dest_path); | |
1091 return TRUE; | |
1092 } | |
1093 | |
1094 gint file_data_sc_update_ci_rename(FileData *fd, gchar *dest_path) | |
1095 { | |
1096 if (!file_data_sc_check_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE; | |
1097 file_data_sc_update_ci(fd, dest_path); | |
1098 return TRUE; | |
1099 } | |
1100 | |
1101 | |
1102 | |
1103 /* | |
1104 * check dest paths - dest image exists, etc. | |
1105 * returns FIXME | |
1106 * it should detect all possible problems with the planned operation | |
1107 */ | |
1108 | |
1109 gint file_data_sc_check_ci_dest(FileData *fd) | |
1110 { | |
1111 } | |
1112 | |
1113 | |
1114 | |
1115 | |
1116 /* | |
1117 * perform the change described by FileFataChangeInfo | |
1118 * it is used for internal operations, | |
1119 * this function actually operates with files on the filesystem | |
1120 * it should implement safe delete | |
1121 */ | |
1122 | |
1123 static gboolean file_data_perform_move(FileData *fd) | |
1124 { | |
1125 g_assert(!strcmp(fd->change->source, fd->path)); | |
1126 return move_file(fd->change->source, fd->change->dest); | |
1127 } | |
1128 | |
1129 static gboolean file_data_perform_copy(FileData *fd) | |
1130 { | |
1131 g_assert(!strcmp(fd->change->source, fd->path)); | |
1132 return copy_file(fd->change->source, fd->change->dest); | |
1133 } | |
1134 | |
1135 static gboolean file_data_perform_delete(FileData *fd) | |
1136 { | |
1137 return unlink_file(fd->path); | |
1138 } | |
1139 | |
1140 static gboolean file_data_perform_ci(FileData *fd) | |
1141 { | |
1142 FileDataChangeType type = fd->change->type; | |
1143 switch (type) | |
1144 { | |
1145 case FILEDATA_CHANGE_MOVE: | |
1146 return file_data_perform_move(fd); | |
1147 case FILEDATA_CHANGE_COPY: | |
1148 return file_data_perform_copy(fd); | |
1149 case FILEDATA_CHANGE_RENAME: | |
1150 return file_data_perform_move(fd); /* the same as move */ | |
1151 case FILEDATA_CHANGE_DELETE: | |
1152 return file_data_perform_delete(fd); | |
1153 case FILEDATA_CHANGE_UPDATE: | |
596 | 1154 /* nothing to do here */ |
590 | 1155 break; |
1156 } | |
1157 return TRUE; | |
1158 } | |
1159 | |
1160 | |
1161 | |
1162 gboolean file_data_sc_perform_ci(FileData *fd) | |
1163 { | |
1164 GList *work; | |
1165 gboolean ret = TRUE; | |
1166 FileDataChangeType type = fd->change->type; | |
1167 if (!file_data_sc_check_ci(fd, type)) return FALSE; | |
1168 | |
1169 work = fd->sidecar_files; | |
1170 while (work) | |
1171 { | |
1172 FileData *sfd = work->data; | |
1173 if (!file_data_perform_ci(sfd)) ret = FALSE; | |
1174 work = work->next; | |
1175 } | |
1176 if (!file_data_perform_ci(fd)) ret = FALSE; | |
1177 return ret; | |
1178 } | |
1179 | |
1180 /* | |
1181 * updates FileData structure according to FileDataChangeInfo | |
1182 */ | |
1183 | |
1184 static void file_data_apply_ci(FileData *fd) | |
1185 { | |
1186 FileDataChangeType type = fd->change->type; | |
1187 /* FIXME delete ?*/ | |
1188 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_COPY || type == FILEDATA_CHANGE_RENAME) | |
1189 { | |
1190 g_free(fd->path); | |
1191 g_hash_table_remove(file_data_pool, fd->original_path); | |
1192 g_free(fd->original_path); | |
1193 file_data_set_path(fd, fd->change->dest); | |
1194 fd->original_path = g_strdup(fd->change->dest); | |
1195 g_hash_table_insert(file_data_pool, fd->original_path, fd); | |
1196 } | |
1197 } | |
1198 | |
596 | 1199 gint file_data_sc_apply_ci(FileData *fd) |
590 | 1200 { |
1201 GList *work; | |
1202 FileDataChangeType type = fd->change->type; | |
1203 if (!file_data_sc_check_ci(fd, type)) return FALSE; | |
1204 | |
1205 work = fd->sidecar_files; | |
1206 while (work) | |
1207 { | |
1208 FileData *sfd = work->data; | |
1209 file_data_apply_ci(sfd); | |
1210 work = work->next; | |
1211 } | |
1212 file_data_apply_ci(fd); | |
1213 return TRUE; | |
1214 } | |
1215 | |
1216 | |
1217 /* | |
1218 * notify other modules about the change described by FileFataChangeInfo | |
1219 */ | |
1220 | |
1221 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks | |
1222 FIXME do we need the ignore_list? It looks like a workaround for ineffective | |
1223 implementation in view_file_list.c */ | |
1224 | |
1225 void file_data_sc_send_notification(FileData *fd) | |
1226 { | |
1227 } | |
1228 | |
1229 |