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"
24 static GHashTable *file_data_pool = NULL;
25 static GHashTable *file_data_planned_change_hash = NULL;
27 static gint sidecar_file_priority(const gchar *path);
31 *-----------------------------------------------------------------------------
32 * text conversion utils
33 *-----------------------------------------------------------------------------
36 gchar *text_from_size(gint64 size)
42 /* what I would like to use is printf("%'d", size)
43 * BUT: not supported on every libc :(
47 /* the %lld conversion is not valid in all libcs, so use a simple work-around */
48 a = g_strdup_printf("%d%09d", (guint)(size / 1000000000), (guint)(size % 1000000000));
52 a = g_strdup_printf("%d", (guint)size);
58 b = g_new(gchar, l + n + 1);
83 gchar *text_from_size_abrev(gint64 size)
85 if (size < (gint64)1024)
87 return g_strdup_printf(_("%d bytes"), (gint)size);
89 if (size < (gint64)1048576)
91 return g_strdup_printf(_("%.1f K"), (gdouble)size / 1024.0);
93 if (size < (gint64)1073741824)
95 return g_strdup_printf(_("%.1f MB"), (gdouble)size / 1048576.0);
98 /* to avoid overflowing the gdouble, do division in two steps */
100 return g_strdup_printf(_("%.1f GB"), (gdouble)size / 1024.0);
103 /* note: returned string is valid until next call to text_from_time() */
104 const gchar *text_from_time(time_t t)
106 static gchar *ret = NULL;
110 GError *error = NULL;
112 btime = localtime(&t);
114 /* the %x warning about 2 digit years is not an error */
115 buflen = strftime(buf, sizeof(buf), "%x %H:%M", btime);
116 if (buflen < 1) return "";
119 ret = g_locale_to_utf8(buf, buflen, NULL, NULL, &error);
122 log_printf("Error converting locale strftime to UTF-8: %s\n", error->message);
131 *-----------------------------------------------------------------------------
133 *-----------------------------------------------------------------------------
136 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source);
137 static void file_data_check_sidecars(FileData *fd);
138 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
141 void file_data_increment_version(FileData *fd)
144 if (fd->parent) fd->parent->version++;
147 static void file_data_set_collate_keys(FileData *fd)
149 gchar *caseless_name;
151 caseless_name = g_utf8_casefold(fd->name, -1);
153 g_free(fd->collate_key_name);
154 g_free(fd->collate_key_name_nocase);
156 #if GLIB_CHECK_VERSION(2, 8, 0)
157 fd->collate_key_name = g_utf8_collate_key_for_filename(fd->name, -1);
158 fd->collate_key_name_nocase = g_utf8_collate_key_for_filename(caseless_name, -1);
160 fd->collate_key_name = g_utf8_collate_key(fd->name, -1);
161 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1);
163 g_free(caseless_name);
166 static void file_data_set_path(FileData *fd, const gchar *path)
168 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */
169 g_assert(file_data_pool);
173 if (fd->original_path)
175 g_hash_table_remove(file_data_pool, fd->original_path);
176 g_free(fd->original_path);
179 g_assert(!g_hash_table_lookup(file_data_pool, path));
181 fd->original_path = g_strdup(path);
182 g_hash_table_insert(file_data_pool, fd->original_path, fd);
184 if (strcmp(path, G_DIR_SEPARATOR_S) == 0)
186 fd->path = g_strdup(path);
188 fd->extension = fd->name + 1;
189 file_data_set_collate_keys(fd);
193 fd->path = g_strdup(path);
194 fd->name = filename_from_path(fd->path);
196 if (strcmp(fd->name, "..") == 0)
198 gchar *dir = remove_level_from_path(path);
200 fd->path = remove_level_from_path(dir);
203 fd->extension = fd->name + 2;
204 file_data_set_collate_keys(fd);
207 else if (strcmp(fd->name, ".") == 0)
210 fd->path = remove_level_from_path(path);
212 fd->extension = fd->name + 1;
213 file_data_set_collate_keys(fd);
217 fd->extension = extension_from_path(fd->path);
218 if (fd->extension == NULL)
219 fd->extension = fd->name + strlen(fd->name);
221 file_data_set_collate_keys(fd);
224 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
226 gboolean ret = FALSE;
229 if (fd->size != st->st_size ||
230 fd->date != st->st_mtime)
232 fd->size = st->st_size;
233 fd->date = st->st_mtime;
234 fd->mode = st->st_mode;
235 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
236 fd->thumb_pixbuf = NULL;
237 file_data_increment_version(fd);
238 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
242 work = fd->sidecar_files;
245 FileData *sfd = work->data;
249 if (!stat_utf8(sfd->path, &st))
253 file_data_disconnect_sidecar_file(fd, sfd);
258 ret |= file_data_check_changed_files_recursive(sfd, &st);
264 gboolean file_data_check_changed_files(FileData *fd)
266 gboolean ret = FALSE;
269 if (fd->parent) fd = fd->parent;
271 if (!stat_utf8(fd->path, &st))
274 FileData *sfd = NULL;
276 /* parent is missing, we have to rebuild whole group */
281 work = fd->sidecar_files;
287 file_data_disconnect_sidecar_file(fd, sfd);
289 if (sfd) file_data_check_sidecars(sfd); /* this will group the sidecars back together */
290 file_data_send_notification(fd, NOTIFY_TYPE_REREAD);
294 ret |= file_data_check_changed_files_recursive(fd, &st);
300 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars)
304 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars);
307 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal);
309 fd = g_hash_table_lookup(file_data_pool, path_utf8);
315 if (!fd && file_data_planned_change_hash)
317 fd = g_hash_table_lookup(file_data_planned_change_hash, path_utf8);
320 DEBUG_1("planned change: using %s -> %s", path_utf8, fd->path);
322 file_data_apply_ci(fd);
331 changed = file_data_check_changed_files(fd);
333 changed = file_data_check_changed_files_recursive(fd, st);
334 if (changed && check_sidecars && sidecar_file_priority(fd->extension))
335 file_data_check_sidecars(fd);
336 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
341 fd = g_new0(FileData, 1);
345 fd->collate_key_name = NULL;
346 fd->collate_key_name_nocase = NULL;
347 fd->original_path = NULL;
349 fd->size = st->st_size;
350 fd->date = st->st_mtime;
351 fd->mode = st->st_mode;
352 fd->thumb_pixbuf = NULL;
353 fd->sidecar_files = NULL;
355 fd->magick = 0x12345678;
357 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */
360 file_data_check_sidecars(fd);
365 static void file_data_check_sidecars(FileData *fd)
369 FileData *parent_fd = NULL;
372 if (fd->disable_grouping || !sidecar_file_priority(fd->extension))
375 base_len = fd->extension - fd->path;
376 fname = g_string_new_len(fd->path, base_len);
377 work = sidecar_ext_get_list();
381 /* check for possible sidecar files;
382 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent,
383 they have fd->ref set to 0 and file_data unref must chack and free them all together
384 (using fd->ref would cause loops and leaks)
388 gchar *ext = work->data;
392 if (strcmp(ext, fd->extension) == 0)
394 new_fd = fd; /* processing the original file */
399 g_string_truncate(fname, base_len);
400 g_string_append(fname, ext);
402 if (!stat_utf8(fname->str, &nst))
405 new_fd = file_data_new(fname->str, &nst, FALSE);
407 if (new_fd->disable_grouping)
409 file_data_unref(new_fd);
413 new_fd->ref--; /* do not use ref here */
417 parent_fd = new_fd; /* parent is the one with the highest prio, found first */
419 file_data_merge_sidecar_files(parent_fd, new_fd);
421 g_string_free(fname, TRUE);
425 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars)
427 gchar *path_utf8 = path_to_utf8(path);
428 FileData *ret = file_data_new(path_utf8, st, check_sidecars);
434 FileData *file_data_new_simple(const gchar *path_utf8)
438 if (!stat_utf8(path_utf8, &st))
444 return file_data_new(path_utf8, &st, TRUE);
447 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
449 sfd->parent = target;
450 if (!g_list_find(target->sidecar_files, sfd))
451 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd);
452 file_data_increment_version(sfd); /* increments both sfd and target */
457 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
461 file_data_add_sidecar_file(target, source);
463 work = source->sidecar_files;
466 FileData *sfd = work->data;
467 file_data_add_sidecar_file(target, sfd);
471 g_list_free(source->sidecar_files);
472 source->sidecar_files = NULL;
474 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE);
479 #ifdef DEBUG_FILEDATA
480 FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd)
482 FileData *file_data_ref(FileData *fd)
485 if (fd == NULL) return NULL;
486 #ifdef DEBUG_FILEDATA
487 if (fd->magick != 0x12345678)
488 DEBUG_0("fd magick mismatch at %s:%d", file, line);
490 g_assert(fd->magick == 0x12345678);
493 #ifdef DEBUG_FILEDATA
494 DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
496 DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
501 static void file_data_free(FileData *fd)
503 g_assert(fd->magick == 0x12345678);
504 g_assert(fd->ref == 0);
506 g_hash_table_remove(file_data_pool, fd->original_path);
509 g_free(fd->original_path);
510 g_free(fd->collate_key_name);
511 g_free(fd->collate_key_name_nocase);
512 if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
514 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
516 file_data_change_info_free(NULL, fd);
520 #ifdef DEBUG_FILEDATA
521 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
523 void file_data_unref(FileData *fd)
526 if (fd == NULL) return;
527 #ifdef DEBUG_FILEDATA
528 if (fd->magick != 0x12345678)
529 DEBUG_0("fd magick mismatch @ %s:%d", file, line);
531 g_assert(fd->magick == 0x12345678);
534 #ifdef DEBUG_FILEDATA
535 DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
538 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
543 FileData *parent = fd->parent ? fd->parent : fd;
548 work = parent->sidecar_files;
551 FileData *sfd = work->data;
557 /* none of parent/children is referenced, we can free everything */
559 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
561 work = parent->sidecar_files;
564 FileData *sfd = work->data;
569 g_list_free(parent->sidecar_files);
570 parent->sidecar_files = NULL;
572 file_data_free(parent);
576 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
578 sfd->parent = target;
579 g_assert(g_list_find(target->sidecar_files, sfd));
581 file_data_increment_version(sfd); /* increments both sfd and target */
583 target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
595 /* disables / enables grouping for particular file, sends UPDATE notification */
596 void file_data_disable_grouping(FileData *fd, gboolean disable)
598 if (!fd->disable_grouping == !disable) return;
599 fd->disable_grouping = !!disable;
605 FileData *parent = file_data_ref(fd->parent);
606 file_data_disconnect_sidecar_file(parent, fd);
607 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
608 file_data_send_notification(parent, NOTIFY_TYPE_INTERNAL);
609 file_data_unref(parent);
611 else if (fd->sidecar_files)
613 GList *sidecar_files = filelist_copy(fd->sidecar_files);
614 GList *work = sidecar_files;
617 FileData *sfd = work->data;
619 file_data_disconnect_sidecar_file(fd, sfd);
620 file_data_send_notification(sfd, NOTIFY_TYPE_INTERNAL);
622 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
623 file_data_check_sidecars((FileData *)sidecar_files->data); /* this will group the sidecars back together */
624 filelist_free(sidecar_files);
629 file_data_check_sidecars(fd);
630 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
634 /* compare name without extension */
635 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2)
637 size_t len1 = fd1->extension - fd1->name;
638 size_t len2 = fd2->extension - fd2->name;
640 if (len1 < len2) return -1;
641 if (len1 > len2) return 1;
643 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */
646 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd)
654 g_free(fdci->source);
667 *-----------------------------------------------------------------------------
668 * sidecar file info struct
669 *-----------------------------------------------------------------------------
674 static gint sidecar_file_priority(const gchar *path)
676 const gchar *extension = extension_from_path(path);
680 if (extension == NULL)
683 work = sidecar_ext_get_list();
686 gchar *ext = work->data;
689 if (strcmp(extension, ext) == 0) return i;
697 *-----------------------------------------------------------------------------
699 *-----------------------------------------------------------------------------
702 static SortType filelist_sort_method = SORT_NONE;
703 static gint filelist_sort_ascend = TRUE;
706 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
708 if (!filelist_sort_ascend)
715 switch (filelist_sort_method)
720 if (fa->size < fb->size) return -1;
721 if (fa->size > fb->size) return 1;
722 /* fall back to name */
725 if (fa->date < fb->date) return -1;
726 if (fa->date > fb->date) return 1;
727 /* fall back to name */
729 #ifdef HAVE_STRVERSCMP
731 return strverscmp(fa->name, fb->name);
738 if (options->file_sort.case_sensitive)
739 return strcmp(fa->collate_key_name, fb->collate_key_name);
741 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase);
744 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gint ascend)
746 filelist_sort_method = method;
747 filelist_sort_ascend = ascend;
748 return filelist_sort_compare_filedata(fa, fb);
751 static gint filelist_sort_file_cb(gpointer a, gpointer b)
753 return filelist_sort_compare_filedata(a, b);
756 GList *filelist_sort_full(GList *list, SortType method, gint ascend, GCompareFunc cb)
758 filelist_sort_method = method;
759 filelist_sort_ascend = ascend;
760 return g_list_sort(list, cb);
763 GList *filelist_insert_sort_full(GList *list, gpointer data, SortType method, gint ascend, GCompareFunc cb)
765 filelist_sort_method = method;
766 filelist_sort_ascend = ascend;
767 return g_list_insert_sorted(list, data, cb);
770 GList *filelist_sort(GList *list, SortType method, gint ascend)
772 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
775 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gint ascend)
777 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb);
781 static GList *filelist_filter_out_sidecars(GList *flist)
784 GList *flist_filtered = NULL;
788 FileData *fd = work->data;
791 if (fd->parent) /* remove fd's that are children */
794 flist_filtered = g_list_prepend(flist_filtered, fd);
798 return flist_filtered;
801 static gint filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gint follow_symlinks)
808 gint (*stat_func)(const gchar *path, struct stat *buf);
810 g_assert(files || dirs);
812 if (files) *files = NULL;
813 if (dirs) *dirs = NULL;
815 pathl = path_from_utf8(dir_fd->path);
816 if (!pathl) return FALSE;
830 while ((dir = readdir(dp)) != NULL)
832 struct stat ent_sbuf;
833 const gchar *name = dir->d_name;
836 if (!options->file_filter.show_hidden_files && ishidden(name))
839 filepath = g_build_filename(pathl, name, NULL);
840 if (stat_func(filepath, &ent_sbuf) >= 0)
842 if (S_ISDIR(ent_sbuf.st_mode))
844 /* we ignore the .thumbnails dir for cleanliness */
846 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
847 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
848 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
849 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
851 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE));
856 if (files && filter_name_exists(name))
858 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE));
869 if (dirs) *dirs = dlist;
870 if (files) *files = filelist_filter_out_sidecars(flist);
875 gint filelist_read(FileData *dir_fd, GList **files, GList **dirs)
877 return filelist_read_real(dir_fd, files, dirs, TRUE);
880 gint filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs)
882 return filelist_read_real(dir_fd, files, dirs, FALSE);
885 void filelist_free(GList *list)
892 file_data_unref((FileData *)work->data);
900 GList *filelist_copy(GList *list)
902 GList *new_list = NULL;
913 new_list = g_list_prepend(new_list, file_data_ref(fd));
916 return g_list_reverse(new_list);
919 GList *filelist_from_path_list(GList *list)
921 GList *new_list = NULL;
932 new_list = g_list_prepend(new_list, file_data_new_simple(path));
935 return g_list_reverse(new_list);
938 GList *filelist_to_path_list(GList *list)
940 GList *new_list = NULL;
951 new_list = g_list_prepend(new_list, g_strdup(fd->path));
954 return g_list_reverse(new_list);
957 GList *filelist_filter(GList *list, gint is_dir_list)
961 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list;
966 FileData *fd = (FileData *)(work->data);
967 const gchar *name = fd->name;
969 if ((!options->file_filter.show_hidden_files && ishidden(name)) ||
970 (!is_dir_list && !filter_name_exists(name)) ||
971 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 ||
972 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) )
976 list = g_list_remove_link(list, link);
988 *-----------------------------------------------------------------------------
990 *-----------------------------------------------------------------------------
993 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b)
995 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path);
998 GList *filelist_sort_path(GList *list)
1000 return g_list_sort(list, filelist_sort_path_cb);
1003 static void filelist_recursive_append(GList **list, GList *dirs)
1010 FileData *fd = (FileData *)(work->data);
1014 if (filelist_read(fd, &f, &d))
1016 f = filelist_filter(f, FALSE);
1017 f = filelist_sort_path(f);
1018 *list = g_list_concat(*list, f);
1020 d = filelist_filter(d, TRUE);
1021 d = filelist_sort_path(d);
1022 filelist_recursive_append(list, d);
1030 GList *filelist_recursive(FileData *dir_fd)
1035 if (!filelist_read(dir_fd, &list, &d)) return NULL;
1036 list = filelist_filter(list, FALSE);
1037 list = filelist_sort_path(list);
1039 d = filelist_filter(d, TRUE);
1040 d = filelist_sort_path(d);
1041 filelist_recursive_append(&list, d);
1049 * marks and orientation
1053 gboolean file_data_get_mark(FileData *fd, gint n)
1055 return !!(fd->marks & (1 << n));
1058 guint file_data_get_marks(FileData *fd)
1063 void file_data_set_mark(FileData *fd, gint n, gboolean value)
1065 guint old = fd->marks;
1066 if (!value == !(fd->marks & (1 << n))) return;
1068 fd->marks = fd->marks ^ (1 << n);
1070 if (old && !fd->marks) /* keep files with non-zero marks in memory */
1072 file_data_unref(fd);
1074 else if (!old && fd->marks)
1079 file_data_increment_version(fd);
1080 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1083 gboolean file_data_filter_marks(FileData *fd, guint filter)
1085 return ((fd->marks & filter) == filter);
1088 GList *file_data_filter_marks_list(GList *list, guint filter)
1095 FileData *fd = work->data;
1099 if (!file_data_filter_marks(fd, filter))
1101 list = g_list_remove_link(list, link);
1102 file_data_unref(fd);
1110 gint file_data_get_user_orientation(FileData *fd)
1112 return fd->user_orientation;
1115 void file_data_set_user_orientation(FileData *fd, gint value)
1117 if (fd->user_orientation == value) return;
1119 fd->user_orientation = value;
1120 file_data_increment_version(fd);
1121 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL);
1126 * file_data - operates on the given fd
1127 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent
1131 /* return list of sidecar file extensions in a string */
1132 gchar *file_data_sc_list_to_string(FileData *fd)
1135 GString *result = g_string_new("");
1137 work = fd->sidecar_files;
1140 FileData *sfd = work->data;
1142 result = g_string_append(result, "+ ");
1143 result = g_string_append(result, sfd->extension);
1145 if (work) result = g_string_append_c(result, ' ');
1148 return g_string_free(result, FALSE);
1154 * add FileDataChangeInfo (see typedefs.h) for the given operation
1155 * uses file_data_add_change_info
1157 * fails if the fd->change already exists - change operations can't run in parallel
1158 * fd->change_info works as a lock
1160 * dest can be NULL - in this case the current name is used for now, it will
1165 FileDataChangeInfo types:
1167 MOVE - path is changed, name may be changed too
1168 RENAME - path remains unchanged, name is changed
1169 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
1170 sidecar names are changed too, extensions are not changed
1172 UPDATE - file size, date or grouping has been changed
1175 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest)
1177 FileDataChangeInfo *fdci;
1179 if (fd->change) return FALSE;
1181 fdci = g_new0(FileDataChangeInfo, 1);
1186 fdci->source = g_strdup(src);
1188 fdci->source = g_strdup(fd->path);
1191 fdci->dest = g_strdup(dest);
1198 static void file_data_planned_change_remove(FileData *fd)
1200 if (file_data_planned_change_hash &&
1201 (fd->change->type == FILEDATA_CHANGE_MOVE || fd->change->type == FILEDATA_CHANGE_RENAME))
1203 if (g_hash_table_lookup(file_data_planned_change_hash, fd->change->dest) == fd)
1205 DEBUG_1("planned change: removing %s -> %s", fd->change->dest, fd->path);
1206 g_hash_table_remove(file_data_planned_change_hash, fd->change->dest);
1207 file_data_unref(fd);
1208 if (g_hash_table_size(file_data_planned_change_hash) == 0)
1210 g_hash_table_destroy(file_data_planned_change_hash);
1211 file_data_planned_change_hash = NULL;
1212 DEBUG_1("planned change: empty");
1219 void file_data_free_ci(FileData *fd)
1221 FileDataChangeInfo *fdci = fd->change;
1226 file_data_planned_change_remove(fd);
1228 g_free(fdci->source);
1237 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type)
1241 if (fd->parent) fd = fd->parent;
1243 if (fd->change) return FALSE;
1245 work = fd->sidecar_files;
1248 FileData *sfd = work->data;
1250 if (sfd->change) return FALSE;
1254 file_data_add_ci(fd, type, NULL, NULL);
1256 work = fd->sidecar_files;
1259 FileData *sfd = work->data;
1261 file_data_add_ci(sfd, type, NULL, NULL);
1268 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type)
1272 if (fd->parent) fd = fd->parent;
1274 if (!fd->change || fd->change->type != type) return FALSE;
1276 work = fd->sidecar_files;
1279 FileData *sfd = work->data;
1281 if (!sfd->change || sfd->change->type != type) return FALSE;
1289 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path)
1291 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE;
1292 file_data_sc_update_ci_copy(fd, dest_path);
1296 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path)
1298 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE;
1299 file_data_sc_update_ci_move(fd, dest_path);
1303 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path)
1305 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE;
1306 file_data_sc_update_ci_rename(fd, dest_path);
1310 gboolean file_data_sc_add_ci_delete(FileData *fd)
1312 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE);
1315 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path)
1317 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE;
1318 file_data_sc_update_ci_unspecified(fd, dest_path);
1322 gboolean file_data_add_ci_write_metadata(FileData *fd)
1324 return file_data_add_ci(fd, FILEDATA_CHANGE_WRITE_METADATA, NULL, NULL);
1327 void file_data_sc_free_ci(FileData *fd)
1331 if (fd->parent) fd = fd->parent;
1333 file_data_free_ci(fd);
1335 work = fd->sidecar_files;
1338 FileData *sfd = work->data;
1340 file_data_free_ci(sfd);
1345 gboolean file_data_sc_add_ci_delete_list(GList *fd_list)
1348 gboolean ret = TRUE;
1353 FileData *fd = work->data;
1355 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE;
1362 static void file_data_sc_revert_ci_list(GList *fd_list)
1369 FileData *fd = work->data;
1371 file_data_sc_free_ci(fd);
1376 static gboolean file_data_sc_add_ci_list_call_func(GList *fd_list, const gchar *dest, gboolean (*func)(FileData *, const gchar *))
1383 FileData *fd = work->data;
1385 if (!func(fd, dest))
1387 file_data_sc_revert_ci_list(work->prev);
1396 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest)
1398 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_copy);
1401 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest)
1403 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_move);
1406 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest)
1408 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_rename);
1411 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest)
1413 return file_data_sc_add_ci_list_call_func(fd_list, dest, file_data_sc_add_ci_unspecified);
1416 gboolean file_data_add_ci_write_metadata_list(GList *fd_list)
1419 gboolean ret = TRUE;
1424 FileData *fd = work->data;
1426 if (!file_data_add_ci_write_metadata(fd)) ret = FALSE;
1433 void file_data_free_ci_list(GList *fd_list)
1440 FileData *fd = work->data;
1442 file_data_free_ci(fd);
1447 void file_data_sc_free_ci_list(GList *fd_list)
1454 FileData *fd = work->data;
1456 file_data_sc_free_ci(fd);
1462 * update existing fd->change, it will be used from dialog callbacks for interactive editing
1463 * fails if fd->change does not exist or the change type does not match
1466 static void file_data_update_planned_change_hash(FileData *fd, const gchar *old_path, gchar *new_path)
1468 FileDataChangeType type = fd->change->type;
1470 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
1474 if (!file_data_planned_change_hash)
1475 file_data_planned_change_hash = g_hash_table_new(g_str_hash, g_str_equal);
1477 if (old_path && g_hash_table_lookup(file_data_planned_change_hash, old_path) == fd)
1479 DEBUG_1("planned change: removing %s -> %s", old_path, fd->path);
1480 g_hash_table_remove(file_data_planned_change_hash, old_path);
1481 file_data_unref(fd);
1484 ofd = g_hash_table_lookup(file_data_planned_change_hash, new_path);
1489 DEBUG_1("planned change: replacing %s -> %s", new_path, ofd->path);
1490 g_hash_table_remove(file_data_planned_change_hash, new_path);
1491 file_data_unref(ofd);
1494 DEBUG_1("planned change: inserting %s -> %s", new_path, fd->path);
1496 g_hash_table_insert(file_data_planned_change_hash, new_path, fd);
1501 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
1503 gchar *old_path = fd->change->dest;
1505 fd->change->dest = g_strdup(dest_path);
1506 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1510 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
1512 const gchar *extension = extension_from_path(fd->change->source);
1513 gchar *base = remove_extension_from_path(dest_path);
1514 gchar *old_path = fd->change->dest;
1516 fd->change->dest = g_strconcat(base, extension, NULL);
1517 file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
1523 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path)
1526 gchar *dest_path_full = NULL;
1528 if (fd->parent) fd = fd->parent;
1532 dest_path = fd->path;
1534 else if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */
1536 gchar *dir = remove_level_from_path(fd->path);
1538 dest_path_full = g_build_filename(dir, dest_path, NULL);
1540 dest_path = dest_path_full;
1542 else if (fd->change->type != FILEDATA_CHANGE_RENAME && isdir(dest_path)) /* rename should not move files between directories */
1544 dest_path_full = g_build_filename(dest_path, fd->name, NULL);
1545 dest_path = dest_path_full;
1548 file_data_update_ci_dest(fd, dest_path);
1550 work = fd->sidecar_files;
1553 FileData *sfd = work->data;
1555 file_data_update_ci_dest_preserve_ext(sfd, dest_path);
1559 g_free(dest_path_full);
1562 static gint file_data_sc_check_update_ci(FileData *fd, const gchar *dest_path, FileDataChangeType type)
1564 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1565 file_data_sc_update_ci(fd, dest_path);
1569 gint file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path)
1571 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_COPY);
1574 gint file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path)
1576 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_MOVE);
1579 gint file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path)
1581 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_RENAME);
1584 gint file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path)
1586 return file_data_sc_check_update_ci(fd, dest_path, FILEDATA_CHANGE_UNSPECIFIED);
1589 static gboolean file_data_sc_update_ci_list_call_func(GList *fd_list,
1591 gboolean (*func)(FileData *, const gchar *))
1594 gboolean ret = TRUE;
1599 FileData *fd = work->data;
1601 if (!func(fd, dest)) ret = FALSE;
1608 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest)
1610 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_move);
1613 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest)
1615 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_copy);
1618 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest)
1620 return file_data_sc_update_ci_list_call_func(fd_list, dest, file_data_sc_update_ci_unspecified);
1625 * verify source and dest paths - dest image exists, etc.
1626 * it should detect all possible problems with the planned operation
1629 gint file_data_verify_ci(FileData *fd)
1631 gint ret = CHANGE_OK;
1636 DEBUG_1("Change checked: no change info: %s", fd->path);
1640 if (!isname(fd->path) &&
1641 !filter_file_class(fd->extension, FORMAT_CLASS_META)) /* xmp sidecar can be eventually created from scratch */
1643 /* this probably should not happen */
1644 ret |= CHANGE_NO_SRC;
1645 DEBUG_1("Change checked: file does not exist: %s", fd->path);
1649 dir = remove_level_from_path(fd->path);
1651 if (fd->change->type != FILEDATA_CHANGE_DELETE &&
1652 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1653 !access_file(fd->path, R_OK))
1655 ret |= CHANGE_NO_READ_PERM;
1656 DEBUG_1("Change checked: no read permission: %s", fd->path);
1658 else if ((fd->change->type == FILEDATA_CHANGE_DELETE || fd->change->type == FILEDATA_CHANGE_MOVE) &&
1659 !access_file(dir, W_OK))
1661 ret |= CHANGE_NO_WRITE_PERM_DIR;
1662 DEBUG_1("Change checked: source dir is readonly: %s", fd->path);
1664 else if (fd->change->type != FILEDATA_CHANGE_COPY &&
1665 fd->change->type != FILEDATA_CHANGE_UNSPECIFIED &&
1666 fd->change->type != FILEDATA_CHANGE_WRITE_METADATA &&
1667 !access_file(fd->path, W_OK))
1669 ret |= CHANGE_WARN_NO_WRITE_PERM;
1670 DEBUG_1("Change checked: no write permission: %s", fd->path);
1672 /* WRITE_METADATA is special because it can be configured to silently write to ~/.geeqie/...
1673 - that means that there are no hard errors and warnings can be disabled
1675 else if (fd->change->type == FILEDATA_CHANGE_WRITE_METADATA &&
1676 options->metadata.save_in_image_file && options->metadata.warn_on_write_problems)
1678 if (isname(fd->path) && !access_file(fd->path, W_OK))
1680 ret |= CHANGE_WARN_NO_WRITE_PERM;
1681 DEBUG_1("Change checked: file is readonly: %s", fd->path);
1684 else if (!access_file(dir, W_OK))
1686 ret |= CHANGE_WARN_NO_WRITE_PERM;
1687 DEBUG_1("Change checked: dir is readonly: %s", fd->path);
1691 if (fd->change->dest)
1696 same = (strcmp(fd->path, fd->change->dest) == 0);
1700 const gchar *dest_ext = extension_from_path(fd->change->dest);
1701 if (!dest_ext) dest_ext = "";
1703 if (strcasecmp(fd->extension, dest_ext) != 0)
1705 ret |= CHANGE_WARN_CHANGED_EXT;
1706 DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
1711 if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
1713 ret |= CHANGE_WARN_SAME;
1714 DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
1718 dest_dir = remove_level_from_path(fd->change->dest);
1720 if (!isdir(dest_dir))
1722 ret |= CHANGE_NO_DEST_DIR;
1723 DEBUG_1("Change checked: destination dir does not exist: %s -> %s", fd->path, fd->change->dest);
1725 else if (!access_file(dest_dir, W_OK))
1727 ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
1728 DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
1732 if (isfile(fd->change->dest))
1734 if (!access_file(fd->change->dest, W_OK))
1736 ret |= CHANGE_NO_WRITE_PERM_DEST;
1737 DEBUG_1("Change checked: destination file exists and is readonly: %s -> %s", fd->path, fd->change->dest);
1741 ret |= CHANGE_WARN_DEST_EXISTS;
1742 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1745 else if (isdir(fd->change->dest))
1747 ret |= CHANGE_DEST_EXISTS;
1748 DEBUG_1("Change checked: destination exists: %s -> %s", fd->path, fd->change->dest);
1755 fd->change->error = ret;
1756 if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
1763 gint file_data_sc_verify_ci(FileData *fd)
1768 ret = file_data_verify_ci(fd);
1770 work = fd->sidecar_files;
1773 FileData *sfd = work->data;
1775 ret |= file_data_verify_ci(sfd);
1782 gchar *file_data_get_error_string(gint error)
1784 GString *result = g_string_new("");
1786 if (error & CHANGE_NO_SRC)
1788 if (result->len > 0) g_string_append(result, ", ");
1789 g_string_append(result, _("file or directory does not exist"));
1792 if (error & CHANGE_DEST_EXISTS)
1794 if (result->len > 0) g_string_append(result, ", ");
1795 g_string_append(result, _("destination already exists"));
1798 if (error & CHANGE_NO_WRITE_PERM_DEST)
1800 if (result->len > 0) g_string_append(result, ", ");
1801 g_string_append(result, _("destination can't be overwritten"));
1804 if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
1806 if (result->len > 0) g_string_append(result, ", ");
1807 g_string_append(result, _("destination directory is not writable"));
1810 if (error & CHANGE_NO_DEST_DIR)
1812 if (result->len > 0) g_string_append(result, ", ");
1813 g_string_append(result, _("destination directory does not exist"));
1816 if (error & CHANGE_NO_WRITE_PERM_DIR)
1818 if (result->len > 0) g_string_append(result, ", ");
1819 g_string_append(result, _("source directory is not writable"));
1822 if (error & CHANGE_NO_READ_PERM)
1824 if (result->len > 0) g_string_append(result, ", ");
1825 g_string_append(result, _("no read permission"));
1828 if (error & CHANGE_WARN_NO_WRITE_PERM)
1830 if (result->len > 0) g_string_append(result, ", ");
1831 g_string_append(result, _("file is readonly"));
1834 if (error & CHANGE_WARN_DEST_EXISTS)
1836 if (result->len > 0) g_string_append(result, ", ");
1837 g_string_append(result, _("destination already exists and will be overwritten"));
1840 if (error & CHANGE_WARN_SAME)
1842 if (result->len > 0) g_string_append(result, ", ");
1843 g_string_append(result, _("source and destination are the same"));
1846 if (error & CHANGE_WARN_CHANGED_EXT)
1848 if (result->len > 0) g_string_append(result, ", ");
1849 g_string_append(result, _("source and destination have different extension"));
1852 return g_string_free(result, FALSE);
1855 gint file_data_verify_ci_list(GList *list, gchar **desc, gboolean with_sidecars)
1858 gint all_errors = 0;
1859 gint common_errors = ~0;
1864 if (!list) return 0;
1866 num = g_list_length(list);
1867 errors = g_new(int, num);
1878 error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
1879 all_errors |= error;
1880 common_errors &= error;
1887 if (desc && all_errors)
1890 GString *result = g_string_new("");
1894 gchar *str = file_data_get_error_string(common_errors);
1895 g_string_append(result, str);
1896 g_string_append(result, "\n");
1910 error = errors[i] & ~common_errors;
1914 gchar *str = file_data_get_error_string(error);
1915 g_string_append_printf(result, "%s: %s\n", fd->name, str);
1920 *desc = g_string_free(result, FALSE);
1929 * perform the change described by FileFataChangeInfo
1930 * it is used for internal operations,
1931 * this function actually operates with files on the filesystem
1932 * it should implement safe delete
1935 static gboolean file_data_perform_move(FileData *fd)
1937 g_assert(!strcmp(fd->change->source, fd->path));
1938 return move_file(fd->change->source, fd->change->dest);
1941 static gboolean file_data_perform_copy(FileData *fd)
1943 g_assert(!strcmp(fd->change->source, fd->path));
1944 return copy_file(fd->change->source, fd->change->dest);
1947 static gboolean file_data_perform_delete(FileData *fd)
1949 if (isdir(fd->path) && !islink(fd->path))
1950 return rmdir_utf8(fd->path);
1952 return unlink_file(fd->path);
1955 gboolean file_data_perform_ci(FileData *fd)
1957 FileDataChangeType type = fd->change->type;
1960 case FILEDATA_CHANGE_MOVE:
1961 return file_data_perform_move(fd);
1962 case FILEDATA_CHANGE_COPY:
1963 return file_data_perform_copy(fd);
1964 case FILEDATA_CHANGE_RENAME:
1965 return file_data_perform_move(fd); /* the same as move */
1966 case FILEDATA_CHANGE_DELETE:
1967 return file_data_perform_delete(fd);
1968 case FILEDATA_CHANGE_WRITE_METADATA:
1969 return metadata_write_perform(fd);
1970 case FILEDATA_CHANGE_UNSPECIFIED:
1971 /* nothing to do here */
1979 gboolean file_data_sc_perform_ci(FileData *fd)
1982 gboolean ret = TRUE;
1983 FileDataChangeType type = fd->change->type;
1985 if (!file_data_sc_check_ci(fd, type)) return FALSE;
1987 work = fd->sidecar_files;
1990 FileData *sfd = work->data;
1992 if (!file_data_perform_ci(sfd)) ret = FALSE;
1996 if (!file_data_perform_ci(fd)) ret = FALSE;
2002 * updates FileData structure according to FileDataChangeInfo
2005 gint file_data_apply_ci(FileData *fd)
2007 FileDataChangeType type = fd->change->type;
2010 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
2012 DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
2013 file_data_planned_change_remove(fd);
2015 if (g_hash_table_lookup(file_data_pool, fd->change->dest))
2017 /* this change overwrites another file which is already known to other modules
2018 renaming fd would create duplicate FileData structure
2019 the best thing we can do is nothing
2020 FIXME: maybe we could copy stuff like marks
2022 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2026 file_data_set_path(fd, fd->change->dest);
2029 file_data_increment_version(fd);
2030 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE);
2035 gint file_data_sc_apply_ci(FileData *fd)
2038 FileDataChangeType type = fd->change->type;
2040 if (!file_data_sc_check_ci(fd, type)) return FALSE;
2042 work = fd->sidecar_files;
2045 FileData *sfd = work->data;
2047 file_data_apply_ci(sfd);
2051 file_data_apply_ci(fd);
2057 * notify other modules about the change described by FileFataChangeInfo
2060 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks
2061 FIXME do we need the ignore_list? It looks like a workaround for ineffective
2062 implementation in view_file_list.c */
2067 typedef struct _NotifyData NotifyData;
2069 struct _NotifyData {
2070 FileDataNotifyFunc func;
2072 NotifyPriority priority;
2075 static GList *notify_func_list = NULL;
2077 static gint file_data_notify_sort(gconstpointer a, gconstpointer b)
2079 NotifyData *nda = (NotifyData *)a;
2080 NotifyData *ndb = (NotifyData *)b;
2082 if (nda->priority < ndb->priority) return -1;
2083 if (nda->priority > ndb->priority) return 1;
2087 gint file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority)
2091 nd = g_new(NotifyData, 1);
2094 nd->priority = priority;
2096 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort);
2097 DEBUG_1("Notify func registered: %p", nd);
2102 gint file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data)
2104 GList *work = notify_func_list;
2108 NotifyData *nd = (NotifyData *)work->data;
2110 if (nd->func == func && nd->data == data)
2112 notify_func_list = g_list_delete_link(notify_func_list, work);
2114 DEBUG_1("Notify func unregistered: %p", nd);
2124 void file_data_send_notification(FileData *fd, NotifyType type)
2126 GList *work = notify_func_list;
2130 NotifyData *nd = (NotifyData *)work->data;
2132 DEBUG_1("Notify func calling: %p %s", nd, fd->path);
2133 nd->func(fd, type, nd->data);
2138 static GHashTable *file_data_monitor_pool = NULL;
2139 static gint realtime_monitor_id = -1;
2141 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data)
2145 file_data_check_changed_files(fd);
2147 DEBUG_1("monitor %s", fd->path);
2150 static gboolean realtime_monitor_cb(gpointer data)
2152 if (!options->update_on_time_change) return TRUE;
2153 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL);
2157 gint file_data_register_real_time_monitor(FileData *fd)
2163 if (!file_data_monitor_pool)
2164 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal);
2166 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2168 DEBUG_1("Register realtime %d %s", count, fd->path);
2171 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2173 if (realtime_monitor_id == -1)
2175 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL);
2181 gint file_data_unregister_real_time_monitor(FileData *fd)
2185 g_assert(file_data_monitor_pool);
2187 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd));
2189 DEBUG_1("Unregister realtime %d %s", count, fd->path);
2191 g_assert(count > 0);
2196 g_hash_table_remove(file_data_monitor_pool, fd);
2198 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count));
2200 file_data_unref(fd);
2202 if (g_hash_table_size(file_data_monitor_pool) == 0)
2204 g_source_remove(realtime_monitor_id);
2205 realtime_monitor_id = -1;
2211 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */