4 * Copyright (C) 2008 The Geeqie Team
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!
17 #include "filefilter.h"
19 #include "thumb_standard.h"
20 #include "ui_fileops.h"
25 static GHashTable *file_data_pool = NULL;
26 static GHashTable *file_data_planned_change_hash = NULL;
28 static gint sidecar_file_priority(const gchar *path);
32 *-----------------------------------------------------------------------------
33 * text conversion utils
34 *-----------------------------------------------------------------------------
37 gchar *text_from_size(gint64 size)
43 /* what I would like to use is printf("%'d", size)
44 * BUT: not supported on every libc :(
48 /* the %lld conversion is not valid in all libcs, so use a simple work-around */
49 a = g_strdup_printf("%d%09d", (guint)(size / 1000000000), (guint)(size % 1000000000));
53 a = g_strdup_printf("%d", (guint)size);
59 b = g_new(gchar, l + n + 1);
84 gchar *text_from_size_abrev(gint64 size)
86 if (size < (gint64)1024)
88 return g_strdup_printf(_("%d bytes"), (gint)size);
90 if (size < (gint64)1048576)
92 return g_strdup_printf(_("%.1f K"), (gdouble)size / 1024.0);
94 if (size < (gint64)1073741824)
96 return g_strdup_printf(_("%.1f MB"), (gdouble)size / 1048576.0);
99 /* to avoid overflowing the gdouble, do division in two steps */
101 return g_strdup_printf(_("%.1f GB"), (gdouble)size / 1024.0);
104 /* note: returned string is valid until next call to text_from_time() */
105 const gchar *text_from_time(time_t t)
107 static gchar *ret = NULL;
111 GError *error = NULL;
113 btime = localtime(&t);
115 /* the %x warning about 2 digit years is not an error */
116 buflen = strftime(buf, sizeof(buf), "%x %H:%M", btime);
117 if (buflen < 1) return "";
120 ret = g_locale_to_utf8(buf, buflen, NULL, NULL, &error);
123 log_printf("Error converting locale strftime to UTF-8: %s\n", error->message);
132 *-----------------------------------------------------------------------------
134 *-----------------------------------------------------------------------------
137 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source);
138 static void file_data_check_sidecars(FileData *fd);
139 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
142 void file_data_increment_version(FileData *fd)
145 if (fd->parent) fd->parent->version++;
148 static void file_data_set_collate_keys(FileData *fd)
150 gchar *caseless_name;
152 caseless_name = g_utf8_casefold(fd->name, -1);
154 g_free(fd->collate_key_name);
155 g_free(fd->collate_key_name_nocase);
157 #if GLIB_CHECK_VERSION(2, 8, 0)
158 fd->collate_key_name = g_utf8_collate_key_for_filename(fd->name, -1);
159 fd->collate_key_name_nocase = g_utf8_collate_key_for_filename(caseless_name, -1);
161 fd->collate_key_name = g_utf8_collate_key(fd->name, -1);
162 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1);
164 g_free(caseless_name);
167 static void file_data_set_path(FileData *fd, const gchar *path)
169 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */
170 g_assert(file_data_pool);
174 if (fd->original_path)
176 g_hash_table_remove(file_data_pool, fd->original_path);
177 g_free(fd->original_path);
180 g_assert(!g_hash_table_lookup(file_data_pool, path));
182 fd->original_path = g_strdup(path);
183 g_hash_table_insert(file_data_pool, fd->original_path, fd);
185 if (strcmp(path, G_DIR_SEPARATOR_S) == 0)
187 fd->path = g_strdup(path);
189 fd->extension = fd->name + 1;
190 file_data_set_collate_keys(fd);
194 fd->path = g_strdup(path);
195 fd->name = filename_from_path(fd->path);
197 if (strcmp(fd->name, "..") == 0)
199 gchar *dir = remove_level_from_path(path);
201 fd->path = remove_level_from_path(dir);
204 fd->extension = fd->name + 2;
205 file_data_set_collate_keys(fd);
208 else if (strcmp(fd->name, ".") == 0)
211 fd->path = remove_level_from_path(path);
213 fd->extension = fd->name + 1;
214 file_data_set_collate_keys(fd);
218 fd->extension = extension_from_path(fd->path);
219 if (fd->extension == NULL)
220 fd->extension = fd->name + strlen(fd->name);
222 file_data_set_collate_keys(fd);
225 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
227 gboolean ret = FALSE;
230 if (fd->size != st->st_size ||
231 fd->date != st->st_mtime)
233 fd->size = st->st_size;
234 fd->date = st->st_mtime;
235 fd->mode = st->st_mode;
236 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
237 fd->thumb_pixbuf = NULL;
238 file_data_increment_version(fd);
239 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
243 work = fd->sidecar_files;
246 FileData *sfd = work->data;
250 if (!stat_utf8(sfd->path, &st))
254 file_data_disconnect_sidecar_file(fd, sfd);
259 ret |= file_data_check_changed_files_recursive(sfd, &st);
265 gboolean file_data_check_changed_files(FileData *fd)
267 gboolean ret = FALSE;
270 if (fd->parent) fd = fd->parent;
272 if (!stat_utf8(fd->path, &st))
275 FileData *sfd = NULL;
277 /* parent is missing, we have to rebuild whole group */
282 work = fd->sidecar_files;
288 file_data_disconnect_sidecar_file(fd, sfd);
290 if (sfd) file_data_check_sidecars(sfd); /* this will group the sidecars back together */
291 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
295 ret |= file_data_check_changed_files_recursive(fd, &st);
301 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars)
305 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars);
308 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal);
310 fd = g_hash_table_lookup(file_data_pool, path_utf8);
316 if (!fd && file_data_planned_change_hash)
318 fd = g_hash_table_lookup(file_data_planned_change_hash, path_utf8);
321 DEBUG_1("planned change: using %s -> %s", path_utf8, fd->path);
323 file_data_apply_ci(fd);
332 changed = file_data_check_changed_files(fd);
334 changed = file_data_check_changed_files_recursive(fd, st);
335 if (changed && check_sidecars && sidecar_file_priority(fd->extension))
336 file_data_check_sidecars(fd);
337 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
342 fd = g_new0(FileData, 1);
346 fd->collate_key_name = NULL;
347 fd->collate_key_name_nocase = NULL;
348 fd->original_path = NULL;
350 fd->size = st->st_size;
351 fd->date = st->st_mtime;
352 fd->mode = st->st_mode;
353 fd->thumb_pixbuf = NULL;
354 fd->sidecar_files = NULL;
356 fd->magick = 0x12345678;
358 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */
361 file_data_check_sidecars(fd);
366 static void file_data_check_sidecars(FileData *fd)
370 FileData *parent_fd = NULL;
373 if (fd->disable_grouping || !sidecar_file_priority(fd->extension))
376 base_len = fd->extension - fd->path;
377 fname = g_string_new_len(fd->path, base_len);
378 work = sidecar_ext_get_list();
382 /* check for possible sidecar files;
383 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent,
384 they have fd->ref set to 0 and file_data unref must chack and free them all together
385 (using fd->ref would cause loops and leaks)
389 gchar *ext = work->data;
393 if (strcmp(ext, fd->extension) == 0)
395 new_fd = fd; /* processing the original file */
400 g_string_truncate(fname, base_len);
401 g_string_append(fname, ext);
403 if (!stat_utf8(fname->str, &nst))
406 new_fd = file_data_new(fname->str, &nst, FALSE);
408 if (new_fd->disable_grouping)
410 file_data_unref(new_fd);
414 new_fd->ref--; /* do not use ref here */
418 parent_fd = new_fd; /* parent is the one with the highest prio, found first */
420 file_data_merge_sidecar_files(parent_fd, new_fd);
422 g_string_free(fname, TRUE);
426 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars)
428 gchar *path_utf8 = path_to_utf8(path);
429 FileData *ret = file_data_new(path_utf8, st, check_sidecars);
435 FileData *file_data_new_simple(const gchar *path_utf8)
439 if (!stat_utf8(path_utf8, &st))
445 return file_data_new(path_utf8, &st, TRUE);
448 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
450 sfd->parent = target;
451 if (!g_list_find(target->sidecar_files, sfd))
452 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd);
453 file_data_increment_version(sfd); /* increments both sfd and target */
458 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
462 file_data_add_sidecar_file(target, source);
464 work = source->sidecar_files;
467 FileData *sfd = work->data;
468 file_data_add_sidecar_file(target, sfd);
472 g_list_free(source->sidecar_files);
473 source->sidecar_files = NULL;
475 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE);
480 #ifdef DEBUG_FILEDATA
481 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
483 FileData *file_data_ref(FileData *fd)
486 if (fd == NULL) return NULL;
487 #ifdef DEBUG_FILEDATA
488 if (fd->magick != 0x12345678)
489 DEBUG_0("fd magick mismatch at %s:%d", file, line);
491 g_assert(fd->magick == 0x12345678);
494 #ifdef DEBUG_FILEDATA
495 DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
497 DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
502 static void file_data_free(FileData *fd)
504 g_assert(fd->magick == 0x12345678);
505 g_assert(fd->ref == 0);
507 g_hash_table_remove(file_data_pool, fd->original_path);
510 g_free(fd->original_path);
511 g_free(fd->collate_key_name);
512 g_free(fd->collate_key_name_nocase);
513 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
515 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
517 file_data_change_info_free(NULL, fd);
521 #ifdef DEBUG_FILEDATA
522 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
524 void file_data_unref(FileData *fd)
527 if (fd == NULL) return;
528 #ifdef DEBUG_FILEDATA
529 if (fd->magick != 0x12345678)
530 DEBUG_0("fd magick mismatch @ %s:%d", file, line);
532 g_assert(fd->magick == 0x12345678);
535 #ifdef DEBUG_FILEDATA
536 DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
539 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
544 FileData *parent = fd->parent ? fd->parent : fd;
549 work = parent->sidecar_files;
552 FileData *sfd = work->data;
558 /* none of parent/children is referenced, we can free everything */
560 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
562 work = parent->sidecar_files;
565 FileData *sfd = work->data;
570 g_list_free(parent->sidecar_files);
571 parent->sidecar_files = NULL;
573 file_data_free(parent);
577 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
579 sfd->parent = target;
580 g_assert(g_list_find(target->sidecar_files, sfd));
582 file_data_increment_version(sfd); /* increments both sfd and target */
584 target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
596 /* disables / enables grouping for particular file, sends UPDATE notification */
597 void file_data_disable_grouping(FileData *fd, gboolean disable)
599 if (!fd->disable_grouping == !disable) return;
600 fd->disable_grouping = !!disable;
606 FileData *parent = file_data_ref(fd->parent);
607 file_data_disconnect_sidecar_file(parent, fd);
608 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
609 file_data_send_notification(parent, NOTIFY_TYPE_INTERNAL);
610 file_data_unref(parent);
612 else if (fd->sidecar_files)
614 GList *sidecar_files = filelist_copy(fd->sidecar_files);
615 GList *work = sidecar_files;
618 FileData *sfd = work->data;
620 file_data_disconnect_sidecar_file(fd, sfd);
621 file_data_send_notification(sfd, NOTIFY_TYPE_INTERNAL);
623 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
624 file_data_check_sidecars((FileData *)sidecar_files->data); /* this will group the sidecars back together */
625 filelist_free(sidecar_files);
630 file_data_check_sidecars(fd);
631 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
635 /* compare name without extension */
636 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
638 size_t len1 = fd1->extension - fd1->name;
639 size_t len2 = fd2->extension - fd2->name;
641 if (len1 < len2) return -1;
642 if (len1 > len2) return 1;
644 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
647 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
655 g_free(fdci->source);
668 *-----------------------------------------------------------------------------
669 * sidecar file info struct
670 *-----------------------------------------------------------------------------
675 static gint sidecar_file_priority(const gchar *path)
677 const gchar *extension = extension_from_path(path);
681 if (extension == NULL)
684 work = sidecar_ext_get_list();
687 gchar *ext = work->data;
690 if (strcmp(extension, ext) == 0) return i;
698 *-----------------------------------------------------------------------------
700 *-----------------------------------------------------------------------------
703 static SortType filelist_sort_method = SORT_NONE;
704 static gint filelist_sort_ascend = TRUE;
707 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
709 if (!filelist_sort_ascend)
716 switch (filelist_sort_method)
721 if (fa->size < fb->size) return -1;
722 if (fa->size > fb->size) return 1;
723 /* fall back to name */
726 if (fa->date < fb->date) return -1;
727 if (fa->date > fb->date) return 1;
728 /* fall back to name */
730 #ifdef HAVE_STRVERSCMP
732 return strverscmp(fa->name, fb->name);
739 if (options->file_sort.case_sensitive)
740 return strcmp(fa->collate_key_name, fb->collate_key_name);
742 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase);
745 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gint ascend)
747 filelist_sort_method = method;
748 filelist_sort_ascend = ascend;
749 return filelist_sort_compare_filedata(fa, fb);
752 static gint filelist_sort_file_cb(gpointer a, gpointer b)
754 return filelist_sort_compare_filedata(a, b);
757 GList *filelist_sort_full(GList *list, SortType method, gint ascend, GCompareFunc cb)
759 filelist_sort_method = method;
760 filelist_sort_ascend = ascend;
761 return g_list_sort(list, cb);
764 GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gint ascend, GCompareFunc cb)
766 filelist_sort_method = method;
767 filelist_sort_ascend = ascend;
768 return g_list_insert_sorted(list, data, cb);
771 GList *filelist_sort(GList *list, SortType method, gint ascend)
773 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
776 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gint ascend)
778 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
782 static GList *filelist_filter_out_sidecars(GList *flist)
785 GList *flist_filtered = NULL;
789 FileData *fd = work->data;
792 if (fd->parent) /* remove fd's that are children */
795 flist_filtered = g_list_prepend(flist_filtered, fd);
799 return flist_filtered;
802 static gint filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gint follow_symlinks)
809 gint (*stat_func)(const gchar *path, struct stat *buf);
811 g_assert(files || dirs);
813 if (files) *files = NULL;
814 if (dirs) *dirs = NULL;
816 pathl = path_from_utf8(dir_fd->path);
817 if (!pathl) return FALSE;
831 while ((dir = readdir(dp)) != NULL)
833 struct stat ent_sbuf;
834 const gchar *name = dir->d_name;
837 if (!options->file_filter.show_hidden_files && ishidden(name))
840 filepath = g_build_filename(pathl, name, NULL);
841 if (stat_func(filepath, &ent_sbuf) >= 0)
843 if (S_ISDIR(ent_sbuf.st_mode))
845 /* we ignore the .thumbnails dir for cleanliness */
847 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
848 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
849 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
850 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
852 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
857 if (files && filter_name_exists(name))
859 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
870 if (dirs) *dirs = dlist;
871 if (files) *files = filelist_filter_out_sidecars(flist);
876 gint filelist_read(FileData *dir_fd, GList **files, GList **dirs)
878 return filelist_read_real(dir_fd, files, dirs, TRUE);
881 gint filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
883 return filelist_read_real(dir_fd, files, dirs, FALSE);
886 void filelist_free(GList *list)
893 file_data_unref((FileData *)work->data);
901 GList *filelist_copy(GList *list)
903 GList *new_list = NULL;
914 new_list = g_list_prepend(new_list, file_data_ref(fd));
917 return g_list_reverse(new_list);
920 GList *filelist_from_path_list(GList *list)
922 GList *new_list = NULL;
933 new_list = g_list_prepend(new_list, file_data_new_simple(path));
936 return g_list_reverse(new_list);
939 GList *filelist_to_path_list(GList *list)
941 GList *new_list = NULL;
952 new_list = g_list_prepend(new_list, g_strdup(fd->path));
955 return g_list_reverse(new_list);
958 GList *filelist_filter(GList *list, gint is_dir_list)
962 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
967 FileData *fd = (FileData *)(work->data);
968 const gchar *name = fd->name;
970 if ((!options->file_filter.show_hidden_files && ishidden(name)) ||
971 (!is_dir_list && !filter_name_exists(name)) ||
972 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
973 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
977 list = g_list_remove_link(list, link);
989 *-----------------------------------------------------------------------------
991 *-----------------------------------------------------------------------------
994 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
996 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
999 GList *filelist_sort_path(GList *list)
1001 return g_list_sort(list, filelist_sort_path_cb);
1004 static void filelist_recursive_append(GList **list, GList *dirs)
1011 FileData *fd = (FileData *)(work->data);
1015 if (filelist_read(fd, &f, &d))
1017 f = filelist_filter(f, FALSE);
1018 f = filelist_sort_path(f);
1019 *list = g_list_concat(*list, f);
1021 d = filelist_filter(d, TRUE);
1022 d = filelist_sort_path(d);
1023 filelist_recursive_append(list, d);
1031 GList *filelist_recursive(FileData *dir_fd)
1036 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1037 list = filelist_filter(list, FALSE);
1038 list = filelist_sort_path(list);
1040 d = filelist_filter(d, TRUE);
1041 d = filelist_sort_path(d);
1042 filelist_recursive_append(&list, d);
1050 * marks and orientation
1054 gboolean file_data_get_mark(FileData *fd, gint n)
1056 return !!(fd->marks & (1 << n));
1059 guint file_data_get_marks(FileData *fd)
1064 void file_data_set_mark(FileData *fd, gint n, gboolean value)
1066 guint old = fd->marks;
1067 if (!value == !(fd->marks & (1 << n))) return;
1069 fd->marks = fd->marks ^ (1 << n);
1071 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1073 file_data_unref(fd);
1075 else if (!old && fd->marks)
1080 file_data_increment_version(fd);
1081 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1084 gboolean file_data_filter_marks(FileData *fd, guint filter)
1086 return ((fd->marks & filter) == filter);
1089 GList *file_data_filter_marks_list(GList *list, guint filter)
1096 FileData *fd = work->data;
1100 if (!file_data_filter_marks(fd, filter))
1102 list = g_list_remove_link(list, link);
1103 file_data_unref(fd);
1111 gint file_data_get_user_orientation(FileData *fd)
1113 return fd->user_orientation;
1116 void file_data_set_user_orientation(FileData *fd, gint value)
1118 if (fd->user_orientation == value) return;
1120 fd->user_orientation = value;
1121 file_data_increment_version(fd);
1122 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1127 * file_data - operates on the given fd
1128 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1132 /* return list of sidecar file extensions in a string */
1133 gchar *file_data_sc_list_to_string(FileData *fd)
1136 GString *result = g_string_new("");
1138 work = fd->sidecar_files;
1141 FileData *sfd = work->data;
1143 result = g_string_append(result, "+ ");
1144 result = g_string_append(result, sfd->extension);
1146 if (work) result = g_string_append_c(result, ' ');
1149 return g_string_free(result, FALSE);
1155 * add FileDataChangeInfo (see typedefs.h) for the given operation
1156 * uses file_data_add_change_info
1158 * fails if the fd->change already exists - change operations can't run in parallel
1159 * fd->change_info works as a lock
1161 * dest can be NULL - in this case the current name is used for now, it will
1166 FileDataChangeInfo types:
1168 MOVE - path is changed, name may be changed too
1169 RENAME - path remains unchanged, name is changed
1170 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1171 sidecar names are changed too, extensions are not changed
1173 UPDATE - file size, date or grouping has been changed
1176 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1178 FileDataChangeInfo *fdci;
1180 if (fd->change) return FALSE;
1182 fdci = g_new0(FileDataChangeInfo, 1);
1187 fdci->source = g_strdup(src);
1189 fdci->source = g_strdup(fd->path);
1192 fdci->dest = g_strdup(dest);
1199 static void file_data_planned_change_remove(FileData *fd)
1201 if (file_data_planned_change_hash &&
1202 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1204 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1206 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1207 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1208 file_data_unref(fd);
1209 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1211 g_hash_table_destroy(file_data_planned_change_hash);
1212 file_data_planned_change_hash = NULL;
1213 DEBUG_1("planned change: empty");
1220 void file_data_free_ci(FileData *fd)
1222 FileDataChangeInfo *fdci = fd->change;
1227 file_data_planned_change_remove(fd);
1229 g_free(fdci->source);
1238 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1242 if (fd->parent) fd = fd->parent;
1244 if (fd->change) return FALSE;
1246 work = fd->sidecar_files;
1249 FileData *sfd = work->data;
1251 if (sfd->change) return FALSE;
1255 file_data_add_ci(fd, type, NULL, NULL);
1257 work = fd->sidecar_files;
1260 FileData *sfd = work->data;
1262 file_data_add_ci(sfd, type, NULL, NULL);
1269 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1273 if (fd->parent) fd = fd->parent;
1275 if (!fd->change || fd->change->type != type) return FALSE;
1277 work = fd->sidecar_files;
1280 FileData *sfd = work->data;
1282 if (!sfd->change || sfd->change->type != type) return FALSE;
1290 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1292 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1293 file_data_sc_update_ci_copy(fd, dest_path);
1297 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1299 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1300 file_data_sc_update_ci_move(fd, dest_path);
1304 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1306 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1307 file_data_sc_update_ci_rename(fd, dest_path);
1311 gboolean file_data_sc_add_ci_delete(FileData *fd)
1313 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1316 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1318 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1319 file_data_sc_update_ci_unspecified(fd, dest_path);
1323 gboolean file_data_add_ci_write_metadata(FileData *fd)
1325 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1328 void file_data_sc_free_ci(FileData *fd)
1332 if (fd->parent) fd = fd->parent;
1334 file_data_free_ci(fd);
1336 work = fd->sidecar_files;
1339 FileData *sfd = work->data;
1341 file_data_free_ci(sfd);
1346 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1349 gboolean ret = TRUE;
1354 FileData *fd = work->data;
1356 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1363 static void file_data_sc_revert_ci_list(GList *fd_list)
1370 FileData *fd = work->data;
1372 file_data_sc_free_ci(fd);
1377 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1384 FileData *fd = work->data;
1386 if (!func(fd, dest))
1388 file_data_sc_revert_ci_list(work->prev);
1397 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1399 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1402 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1404 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1407 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest)
1409 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_rename);
1412 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest)
1414 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_unspecified);
1417 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1420 gboolean ret = TRUE;
1425 FileData *fd = work->data;
1427 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1434 void file_data_free_ci_list(GList *fd_list)
1441 FileData *fd = work->data;
1443 file_data_free_ci(fd);
1448 void file_data_sc_free_ci_list(GList *fd_list)
1455 FileData *fd = work->data;
1457 file_data_sc_free_ci(fd);
1463 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1464 * fails if fd->change does not exist or the change type does not match
1467 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1469 FileDataChangeType type = fd->change->type;
1471 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1475 if (!file_data_planned_change_hash)
1476 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1478 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1480 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1481 g_hash_table_remove(file_data_planned_change_hash, old_path);
1482 file_data_unref(fd);
1485 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1490 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1491 g_hash_table_remove(file_data_planned_change_hash, new_path);
1492 file_data_unref(ofd);
1495 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1497 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1502 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1504 gchar *old_path = fd->change->dest;
1506 fd->change->dest = g_strdup(dest_path);
1507 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1511 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1513 const gchar *extension = extension_from_path(fd->change->source);
1514 gchar *base = remove_extension_from_path(dest_path);
1515 gchar *old_path = fd->change->dest;
1517 fd->change->dest = g_strconcat(base, extension, NULL);
1518 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1524 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1527 gchar *dest_path_full = NULL;
1529 if (fd->parent) fd = fd->parent;
1533 dest_path = fd->path;
1535 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1537 gchar *dir = remove_level_from_path(fd->path);
1539 dest_path_full = g_build_filename(dir, dest_path, NULL);
1541 dest_path = dest_path_full;
1543 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1545 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1546 dest_path = dest_path_full;
1549 file_data_update_ci_dest(fd, dest_path);
1551 work = fd->sidecar_files;
1554 FileData *sfd = work->data;
1556 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1560 g_free(dest_path_full);
1563 static gint file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1565 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1566 file_data_sc_update_ci(fd, dest_path);
1570 gint file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1572 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1575 gint file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1577 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1580 gint file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1582 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1585 gint file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1587 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1590 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1592 gboolean (*func)(FileData *, const gchar *))
1595 gboolean ret = TRUE;
1600 FileData *fd = work->data;
1602 if (!func(fd, dest)) ret = FALSE;
1609 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1611 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1614 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1616 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1619 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest)
1621 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_unspecified);
1626 * verify source and dest paths - dest image exists, etc.
1627 * it should detect all possible problems with the planned operation
1630 gint file_data_verify_ci(FileData *fd)
1632 gint ret = CHANGE_OK;
1637 DEBUG_1("Change checked: no change info: %s", fd->path);
1641 if (!isname(fd->path) &&
1642 !filter_file_class(fd->extension, FORMAT_CLASS_META)) /* xmp sidecar can be eventually created from scratch */
1644 /* this probably should not happen */
1645 ret |= CHANGE_NO_SRC;
1646 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1650 dir = remove_level_from_path(fd->path);
1652 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1653 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1654 !access_file(fd->path, R_OK))
1656 ret |= CHANGE_NO_READ_PERM;
1657 DEBUG_1("Change checked: no read permission: %s", fd->path);
1659 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1660 !access_file(dir, W_OK))
1662 ret |= CHANGE_NO_WRITE_PERM_DIR;
1663 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1665 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1666 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1667 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1668 !access_file(fd->path, W_OK))
1670 ret |= CHANGE_WARN_NO_WRITE_PERM;
1671 DEBUG_1("Change checked: no write permission: %s", fd->path);
1673 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1674 - that means that there are no hard errors and warnings can be disabled
1676 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA &&
1677 options->metadata.save_in_image_file && options->metadata.warn_on_write_problems)
1679 if (isname(fd->path) && !access_file(fd->path, W_OK))
1681 ret |= CHANGE_WARN_NO_WRITE_PERM;
1682 DEBUG_1("Change checked: file is readonly: %s", fd->path);
1685 else if (!access_file(dir, W_OK))
1687 ret |= CHANGE_WARN_NO_WRITE_PERM;
1688 DEBUG_1("Change checked: dir is readonly: %s", fd->path);
1692 if (fd->change->dest)
1697 same = (strcmp(fd->path, fd->change->dest) == 0);
1701 const gchar *dest_ext = extension_from_path(fd->change->dest);
1702 if (!dest_ext) dest_ext = "";
1704 if (strcasecmp(fd->extension, dest_ext) != 0)
1706 ret |= CHANGE_WARN_CHANGED_EXT;
1707 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1712 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1714 ret |= CHANGE_WARN_SAME;
1715 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1719 dest_dir = remove_level_from_path(fd->change->dest);
1721 if (!isdir(dest_dir))
1723 ret |= CHANGE_NO_DEST_DIR;
1724 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1726 else if (!access_file(dest_dir, W_OK))
1728 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1729 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1733 if (isfile(fd->change->dest))
1735 if (!access_file(fd->change->dest, W_OK))
1737 ret |= CHANGE_NO_WRITE_PERM_DEST;
1738 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1742 ret |= CHANGE_WARN_DEST_EXISTS;
1743 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1746 else if (isdir(fd->change->dest))
1748 ret |= CHANGE_DEST_EXISTS;
1749 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1756 fd->change->error = ret;
1757 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1764 gint file_data_sc_verify_ci(FileData *fd)
1769 ret = file_data_verify_ci(fd);
1771 work = fd->sidecar_files;
1774 FileData *sfd = work->data;
1776 ret |= file_data_verify_ci(sfd);
1783 gchar *file_data_get_error_string(gint error)
1785 GString *result = g_string_new("");
1787 if (error & CHANGE_NO_SRC)
1789 if (result->len > 0) g_string_append(result, ", ");
1790 g_string_append(result, _("file or directory does not exist"));
1793 if (error & CHANGE_DEST_EXISTS)
1795 if (result->len > 0) g_string_append(result, ", ");
1796 g_string_append(result, _("destination already exists"));
1799 if (error & CHANGE_NO_WRITE_PERM_DEST)
1801 if (result->len > 0) g_string_append(result, ", ");
1802 g_string_append(result, _("destination can't be overwritten"));
1805 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1807 if (result->len > 0) g_string_append(result, ", ");
1808 g_string_append(result, _("destination directory is not writable"));
1811 if (error & CHANGE_NO_DEST_DIR)
1813 if (result->len > 0) g_string_append(result, ", ");
1814 g_string_append(result, _("destination directory does not exist"));
1817 if (error & CHANGE_NO_WRITE_PERM_DIR)
1819 if (result->len > 0) g_string_append(result, ", ");
1820 g_string_append(result, _("source directory is not writable"));
1823 if (error & CHANGE_NO_READ_PERM)
1825 if (result->len > 0) g_string_append(result, ", ");
1826 g_string_append(result, _("no read permission"));
1829 if (error & CHANGE_WARN_NO_WRITE_PERM)
1831 if (result->len > 0) g_string_append(result, ", ");
1832 g_string_append(result, _("file is readonly"));
1835 if (error & CHANGE_WARN_DEST_EXISTS)
1837 if (result->len > 0) g_string_append(result, ", ");
1838 g_string_append(result, _("destination already exists and will be overwritten"));
1841 if (error & CHANGE_WARN_SAME)
1843 if (result->len > 0) g_string_append(result, ", ");
1844 g_string_append(result, _("source and destination are the same"));
1847 if (error & CHANGE_WARN_CHANGED_EXT)
1849 if (result->len > 0) g_string_append(result, ", ");
1850 g_string_append(result, _("source and destination have different extension"));
1853 return g_string_free(result, FALSE);
1856 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
1859 gint all_errors = 0;
1860 gint common_errors = ~0;
1865 if (!list) return 0;
1867 num = g_list_length(list);
1868 errors = g_new(int, num);
1879 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
1880 all_errors |= error;
1881 common_errors &= error;
1888 if (desc && all_errors)
1891 GString *result = g_string_new("");
1895 gchar *str = file_data_get_error_string(common_errors);
1896 g_string_append(result, str);
1897 g_string_append(result, "\n");
1911 error = errors[i] & ~common_errors;
1915 gchar *str = file_data_get_error_string(error);
1916 g_string_append_printf(result, "%s: %s\n", fd->name, str);
1921 *desc = g_string_free(result, FALSE);
1930 * perform the change described by FileFataChangeInfo
1931 * it is used for internal operations,
1932 * this function actually operates with files on the filesystem
1933 * it should implement safe delete
1936 static gboolean file_data_perform_move(FileData *fd)
1938 g_assert(!strcmp(fd->change->source, fd->path));
1939 return move_file(fd->change->source, fd->change->dest);
1942 static gboolean file_data_perform_copy(FileData *fd)
1944 g_assert(!strcmp(fd->change->source, fd->path));
1945 return copy_file(fd->change->source, fd->change->dest);
1948 static gboolean file_data_perform_delete(FileData *fd)
1950 if (isdir(fd->path) && !islink(fd->path))
1951 return rmdir_utf8(fd->path);
1953 if (options->file_ops.safe_delete_enable)
1954 return file_util_safe_unlink(fd->path);
1956 return unlink_file(fd->path);
1959 gboolean file_data_perform_ci(FileData *fd)
1961 FileDataChangeType type = fd->change->type;
1964 case FILEDATA_CHANGE_MOVE:
1965 return file_data_perform_move(fd);
1966 case FILEDATA_CHANGE_COPY:
1967 return file_data_perform_copy(fd);
1968 case FILEDATA_CHANGE_RENAME:
1969 return file_data_perform_move(fd); /* the same as move */
1970 case FILEDATA_CHANGE_DELETE:
1971 return file_data_perform_delete(fd);
1972 case FILEDATA_CHANGE_WRITE_METADATA:
1973 return metadata_write_perform(fd);
1974 case FILEDATA_CHANGE_UNSPECIFIED:
1975 /* nothing to do here */
1983 gboolean file_data_sc_perform_ci(FileData *fd)
1986 gboolean ret = TRUE;
1987 FileDataChangeType type = fd->change->type;
1989 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1991 work = fd->sidecar_files;
1994 FileData *sfd = work->data;
1996 if (!file_data_perform_ci(sfd)) ret = FALSE;
2000 if (!file_data_perform_ci(fd)) ret = FALSE;
2006 * updates FileData structure according to FileDataChangeInfo
2009 gint file_data_apply_ci(FileData *fd)
2011 FileDataChangeType type = fd->change->type;
2014 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2016 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2017 file_data_planned_change_remove(fd);
2019 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2021 /* this change overwrites another file which is already known to other modules
2022 renaming fd would create duplicate FileData structure
2023 the best thing we can do is nothing
2024 FIXME: maybe we could copy stuff like marks
2026 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2030 file_data_set_path(fd, fd->change->dest);
2033 file_data_increment_version(fd);
2034 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE);
2039 gint file_data_sc_apply_ci(FileData *fd)
2042 FileDataChangeType type = fd->change->type;
2044 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2046 work = fd->sidecar_files;
2049 FileData *sfd = work->data;
2051 file_data_apply_ci(sfd);
2055 file_data_apply_ci(fd);
2061 * notify other modules about the change described by FileFataChangeInfo
2064 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2065 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2066 implementation in view_file_list.c */
2071 typedef struct _NotifyData NotifyData;
2073 struct _NotifyData {
2074 FileDataNotifyFunc func;
2076 NotifyPriority priority;
2079 static GList *notify_func_list = NULL;
2081 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2083 NotifyData *nda = (NotifyData *)a;
2084 NotifyData *ndb = (NotifyData *)b;
2086 if (nda->priority < ndb->priority) return -1;
2087 if (nda->priority > ndb->priority) return 1;
2091 gint file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2095 nd = g_new(NotifyData, 1);
2098 nd->priority = priority;
2100 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2101 DEBUG_1("Notify func registered: %p", nd);
2106 gint file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2108 GList *work = notify_func_list;
2112 NotifyData *nd = (NotifyData *)work->data;
2114 if (nd->func == func && nd->data == data)
2116 notify_func_list = g_list_delete_link(notify_func_list, work);
2118 DEBUG_1("Notify func unregistered: %p", nd);
2128 void file_data_send_notification(FileData *fd, NotifyType type)
2130 GList *work = notify_func_list;
2134 NotifyData *nd = (NotifyData *)work->data;
2136 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2137 nd->func(fd, type, nd->data);
2142 static GHashTable *file_data_monitor_pool = NULL;
2143 static gint realtime_monitor_id = -1;
2145 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2149 file_data_check_changed_files(fd);
2151 DEBUG_1("monitor %s", fd->path);
2154 static gboolean realtime_monitor_cb(gpointer data)
2156 if (!options->update_on_time_change) return TRUE;
2157 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2161 gint file_data_register_real_time_monitor(FileData *fd)
2167 if (!file_data_monitor_pool)
2168 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2170 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2172 DEBUG_1("Register realtime %d %s", count, fd->path);
2175 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2177 if (realtime_monitor_id == -1)
2179 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2185 gint file_data_unregister_real_time_monitor(FileData *fd)
2189 g_assert(file_data_monitor_pool);
2191 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2193 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2195 g_assert(count > 0);
2200 g_hash_table_remove(file_data_monitor_pool, fd);
2202 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2204 file_data_unref(fd);
2206 if (g_hash_table_size(file_data_monitor_pool) == 0)
2208 g_source_remove(realtime_monitor_id);
2209 realtime_monitor_id = -1;
2215 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */