static gboolean _fm_file_ops_job_check_paths(FmFileOpsJob* job, GFile* src, GFileInfo* src_inf, GFile* dest) { GError* err = NULL; FmJob* fmjob = FM_JOB(job); if(job->type == FM_FILE_OP_MOVE && g_file_equal(src, dest)) { err = g_error_new_literal(G_IO_ERROR, G_IO_ERROR_FAILED, _("Source and destination are the same.")); } else if(g_file_info_get_file_type(src_inf) == G_FILE_TYPE_DIRECTORY && g_file_has_prefix(dest, src) ) { const char* msg = NULL; if(job->type == FM_FILE_OP_MOVE) msg = _("Cannot move a folder into its sub folder"); else if(job->type == FM_FILE_OP_COPY) msg = _("Cannot copy a folder into its sub folder"); else msg = _("Destination is a sub folder of source"); err = g_error_new_literal(G_IO_ERROR, G_IO_ERROR_FAILED, msg); } if(err) { if(!fm_job_is_cancelled(fmjob)) { fm_file_ops_job_emit_cur_file(job, g_file_info_get_display_name(src_inf)); fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); } g_error_free(err); } return (err == NULL); }
static inline FmFileInfo *_new_info_for_native_file(FmDirListJob* job, FmPath* path, const char* path_str, GError** err) { FmFileInfo *fi; if (fm_job_is_cancelled(FM_JOB(job))) return NULL; if (!(job->flags & FM_DIR_LIST_JOB_DETAILED)) return fm_file_info_new_from_native_file(path, path_str, err); fi = fm_file_info_new(); fm_file_info_set_path(fi, path); if (fm_file_info_set_from_native_file(fi, path_str, err)) return fi; fm_file_info_unref(fi); return NULL; }
gboolean fm_deep_count_job_run(FmJob* job) { FmDeepCountJob* dc = (FmDeepCountJob*)job; GList* l; l = fm_list_peek_head_link(dc->paths); for(; !fm_job_is_cancelled(job) && l; l=l->next) { FmPath* path = FM_PATH(l->data); if(fm_path_is_native(path)) /* if it's a native file, use posix APIs */ deep_count_posix( dc, path ); else { GFile* gf = fm_path_to_gfile(path); deep_count_gio( dc, NULL, gf ); g_object_unref(gf); } } return TRUE; }
void FilePropsDialog::onFileSizeTimerTimeout() { if(deepCountJob && !fm_job_is_cancelled(FM_JOB(deepCountJob))) { char size_str[128]; fm_file_size_to_str(size_str, sizeof(size_str), deepCountJob->total_size, fm_config->si_unit); // FIXME: // OMG! It's really unbelievable that Qt developers only implement // QObject::tr(... int n). GNU gettext developers are smarter and // they use unsigned long instead of int. // We cannot use Qt here to handle plural forms. So sad. :-( QString str = QString::fromUtf8(size_str) % QString(" (%1 B)").arg(deepCountJob->total_size); // tr(" (%n) byte(s)", "", deepCountJob->total_size); ui->fileSize->setText(str); fm_file_size_to_str(size_str, sizeof(size_str), deepCountJob->total_ondisk_size, fm_config->si_unit); str = QString::fromUtf8(size_str) % QString(" (%1 B)").arg(deepCountJob->total_ondisk_size); // tr(" (%n) byte(s)", "", deepCountJob->total_ondisk_size); ui->onDiskSize->setText(str); } }
static gboolean on_timeout (FmFilePropData *data) { char size_str[128]; FmDeepCountJob *dc = (FmDeepCountJob*) data->dc_job; //gdk_threads_enter (); if (G_LIKELY (dc && !fm_job_is_cancelled (FM_JOB (dc)))) { char *str; fm_file_size_to_str (size_str, dc->total_size, TRUE); str = g_strdup_printf ("%s (%'llu %s)", size_str, dc->total_size, ngettext ("byte", "bytes", dc->total_size)); gtk_label_set_text (GTK_LABEL (data->total_size), str); g_free (str); fm_file_size_to_str (size_str, dc->total_block_size, TRUE); str = g_strdup_printf ("%s (%'llu %s)", size_str, dc->total_block_size, ngettext ("byte", "bytes", dc->total_block_size)); gtk_label_set_text (GTK_LABEL (data->size_on_disk), str); g_free (str); } //gdk_threads_leave (); return TRUE; }
static void on_finished (FmFileOpsJob *job, FmProgressDisplay *data) { GtkWidget *parent; if (data->update_timeout) { g_source_remove (data->update_timeout); data->update_timeout = 0; } parent = data->parent; if (data->dlg) { // errors happened if (data->has_error && data->dlg) { gtk_label_set_text (GTK_LABEL (data->current), ""); gtk_label_set_text (GTK_LABEL (data->remaining_time), "00:00:00"); gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dlg), GTK_RESPONSE_CANCEL, FALSE); gtk_dialog_add_button (GTK_DIALOG (data->dlg), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); gtk_image_set_from_stock (GTK_IMAGE (data->icon), GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); gtk_widget_show (data->msg); if (fm_job_is_cancelled (FM_JOB (data->job))) { gtk_label_set_text (GTK_LABEL (data->msg), _("The file operation is cancelled and there are some errors.")); gtk_window_set_title (GTK_WINDOW (data->dlg), _("Cancelled")); } else { gtk_label_set_text (GTK_LABEL (data->msg), _("The file operation is finished, but there are some errors.")); gtk_window_set_title (GTK_WINDOW (data->dlg), _("Finished")); } } else fm_progress_display_destroy (data); g_debug ("file operation is finished!"); } else fm_progress_display_destroy (data); /* sepcial handling for trash * FIXME_pcm: need to refactor this to use a more elegant way later. */ if (job->type == FM_FILE_OP_TRASH) { FmPathList *unsupported = (FmPathList*)g_object_get_data (G_OBJECT (job), "trash-unsupported"); // some files cannot be trashed because underlying filesystems don't support it. // delete them instead if (unsupported) { if (fm_yes_no (GTK_WINDOW (parent), NULL, _("Some files cannot be moved to trash can because " "the underlying file systems don't support this operation.\n" "Do you want to delete them instead?"), TRUE)) { FmJob *job = fm_file_ops_job_new (FM_FILE_OP_DELETE, unsupported); fm_file_ops_job_run_with_progress (GTK_WINDOW (data->parent), (FmFileOpsJob*) job); } } } }
static gboolean fm_file_info_job_run(FmJob* fmjob) { GList* l; FmFileInfoJob* job = (FmFileInfoJob*)fmjob; GError* err = NULL; if(job->file_infos == NULL) return FALSE; for(l = fm_file_info_list_peek_head_link(job->file_infos); !fm_job_is_cancelled(fmjob) && l;) { FmFileInfo* fi = (FmFileInfo*)l->data; GList* next = l->next; FmPath* path = fm_file_info_get_path(fi); if(job->current) fm_path_unref(job->current); job->current = fm_path_ref(path); if(fm_path_is_native(path)) { char* path_str = fm_path_to_str(path); if(!_fm_file_info_job_get_info_for_native_file(fmjob, fi, path_str, &err)) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) { g_free(path_str); continue; /* retry */ } fm_file_info_list_delete_link(job->file_infos, l); /* also calls unref */ } else if(G_UNLIKELY(job->flags & FM_FILE_INFO_JOB_EMIT_FOR_EACH_FILE)) fm_job_call_main_thread(fmjob, _emit_current_file, fi); g_free(path_str); /* recursively set display names for path parents */ _check_native_display_names(fm_path_get_parent(path)); } else { GFile* gf; gf = fm_path_to_gfile(path); if(!_fm_file_info_job_get_info_for_gfile(fmjob, fi, gf, &err)) { if(err->domain == G_IO_ERROR && err->code == G_IO_ERROR_NOT_MOUNTED) { GFileInfo *inf; /* location by link isn't mounted; unfortunately we cannot launch a target if we don't know what kind of target we have; lets make a simplest directory-kind GFIleInfo */ /* FIXME: this may be dirty a bit */ g_error_free(err); err = NULL; inf = g_file_info_new(); g_file_info_set_file_type(inf, G_FILE_TYPE_DIRECTORY); g_file_info_set_name(inf, fm_path_get_basename(path)); g_file_info_set_display_name(inf, fm_path_get_basename(path)); fm_file_info_set_from_g_file_data(fi, gf, inf); g_object_unref(inf); } else { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) { g_object_unref(gf); continue; /* retry */ } fm_file_info_list_delete_link(job->file_infos, l); /* also calls unref */ goto _next; } } else if(G_UNLIKELY(job->flags & FM_FILE_INFO_JOB_EMIT_FOR_EACH_FILE)) fm_job_call_main_thread(fmjob, _emit_current_file, fi); /* recursively set display names for path parents */ _check_gfile_display_names(fm_path_get_parent(path), gf); _next: g_object_unref(gf); } l = next; } return TRUE; }
static void on_finished(FmFileOpsJob* job, FmProgressDisplay* data) { GtkWindow* parent = NULL; /* preserve pointers that fm_progress_display_destroy() will unreference as they may be requested by trash support below */ if(data->parent) parent = g_object_ref(data->parent); g_object_ref(job); if(data->dlg) { /* errors happened */ if(data->has_error) { gtk_label_set_text(data->current, ""); gtk_label_set_text(data->remaining_time, "00:00:00"); gtk_dialog_set_response_sensitive(data->dlg, GTK_RESPONSE_CANCEL, FALSE); gtk_dialog_add_button(data->dlg, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); gtk_image_set_from_stock(data->icon, GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); gtk_widget_show(GTK_WIDGET(data->msg)); if(fm_job_is_cancelled(FM_JOB(job))) { gtk_label_set_text(data->msg, _("The file operation is cancelled and there are some errors.")); gtk_window_set_title(GTK_WINDOW(data->dlg), _("Cancelled")); } else { gtk_label_set_text(data->msg, _("The file operation is finished, but there are some errors.")); gtk_window_set_title(GTK_WINDOW(data->dlg), _("Finished")); } } else fm_progress_display_destroy(data); g_debug("file operation is finished!"); } else fm_progress_display_destroy(data); /* if it's not destroyed yet then it will be destroyed with dialog window */ /* sepcial handling for trash * FIXME: need to refactor this to use a more elegant way later. */ if(job->type == FM_FILE_OP_TRASH) /* FIXME: direct access to job struct! */ { FmPathList* unsupported = (FmPathList*)g_object_get_data(G_OBJECT(job), "trash-unsupported"); /* some files cannot be trashed because underlying filesystems don't support it. */ g_object_unref(job); if(unsupported) /* delete them instead */ { /* FIXME: parent window might be already destroyed! */ if(fm_yes_no(parent, NULL, _("Some files cannot be moved to trash can because " "the underlying file systems don't support this operation.\n" "Do you want to delete them instead?"), TRUE)) { job = fm_file_ops_job_new(FM_FILE_OP_DELETE, unsupported); fm_file_ops_job_run_with_progress(parent, job); /* it eats reference! */ } } } else g_object_unref(job); if(parent) g_object_unref(parent); }
static gboolean fm_dir_list_job_run_gio(FmDirListJob* job) { GFileEnumerator *enu; GFileInfo *inf; FmFileInfo* fi; GError *err = NULL; FmJob* fmjob = FM_JOB(job); GFile* gf; const char* query; gf = fm_path_to_gfile(job->dir_path); _retry: inf = g_file_query_info(gf, gfile_info_query_attribs, 0, fm_job_get_cancellable(fmjob), &err); if(!inf ) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); if( act == FM_JOB_RETRY ) { err = NULL; goto _retry; } else { g_object_unref(gf); return FALSE; } } if( g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) { char *path_str = fm_path_to_str(job->dir_path); err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The specified directory '%s' is not valid"), path_str); fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_free(path_str); g_error_free(err); g_object_unref(gf); g_object_unref(inf); return FALSE; } /* check if FS is R/O and set attr. into inf */ _fm_file_info_job_update_fs_readonly(gf, inf, NULL, NULL); job->dir_fi = fm_file_info_new_from_g_file_data(gf, inf, job->dir_path); g_object_unref(inf); if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY)) { query = G_FILE_ATTRIBUTE_STANDARD_TYPE","G_FILE_ATTRIBUTE_STANDARD_NAME"," G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN","G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP"," G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK","G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL"," G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME"," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME","G_FILE_ATTRIBUTE_STANDARD_ICON"," G_FILE_ATTRIBUTE_STANDARD_SIZE","G_FILE_ATTRIBUTE_STANDARD_TARGET_URI"," "unix::*,time::*,access::*,id::filesystem"; } else query = gfile_info_query_attribs; enu = g_file_enumerate_children (gf, query, 0, fm_job_get_cancellable(fmjob), &err); g_object_unref(gf); if(enu) { while( ! fm_job_is_cancelled(fmjob) ) { inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err); if(inf) { FmPath *dir, *sub; GFile *child; if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY)) { /* FIXME: handle symlinks */ if(g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) { g_object_unref(inf); continue; } } /* virtual folders may return children not within them */ dir = fm_path_new_for_gfile(g_file_enumerator_get_container(enu)); if (fm_path_equal(job->dir_path, dir)) sub = fm_path_new_child(job->dir_path, g_file_info_get_name(inf)); else sub = fm_path_new_child(dir, g_file_info_get_name(inf)); child = g_file_get_child(g_file_enumerator_get_container(enu), g_file_info_get_name(inf)); if (g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY) /* for dir: check if its FS is R/O and set attr. into inf */ _fm_file_info_job_update_fs_readonly(child, inf, NULL, NULL); fi = fm_file_info_new_from_g_file_data(child, inf, sub); fm_path_unref(sub); fm_path_unref(dir); g_object_unref(child); fm_dir_list_job_add_found_file(job, fi); fm_file_info_unref(fi); } else { if(err) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); /* FM_JOB_RETRY is not supported. */ if(act == FM_JOB_ABORT) fm_job_cancel(fmjob); } /* otherwise it's EOL */ break; } g_object_unref(inf); } g_file_enumerator_close(enu, NULL, &err); g_object_unref(enu); } else { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_error_free(err); return FALSE; } return TRUE; }
static gboolean fm_dir_list_job_run_posix(FmDirListJob* job) { FmJob* fmjob = FM_JOB(job); FmFileInfo* fi; GError *err = NULL; char* path_str; GDir* dir; path_str = fm_path_to_str(job->dir_path); fi = _new_info_for_native_file(job, job->dir_path, path_str, NULL); if(fi) { if(! fm_file_info_is_dir(fi)) { err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The specified directory '%s' is not valid"), path_str); fm_file_info_unref(fi); fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_error_free(err); g_free(path_str); return FALSE; } job->dir_fi = fi; } else { err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The specified directory '%s' is not valid"), path_str); fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_error_free(err); g_free(path_str); return FALSE; } dir = g_dir_open(path_str, 0, &err); if( dir ) { const char* name; GString* fpath = g_string_sized_new(4096); int dir_len = strlen(path_str); g_string_append_len(fpath, path_str, dir_len); if(fpath->str[dir_len-1] != '/') { g_string_append_c(fpath, '/'); ++dir_len; } while( ! fm_job_is_cancelled(fmjob) && (name = g_dir_read_name(dir)) ) { FmPath* new_path; g_string_truncate(fpath, dir_len); g_string_append(fpath, name); if(job->flags & FM_DIR_LIST_JOB_DIR_ONLY) /* if we only want directories */ { struct stat st; /* FIXME: this results in an additional stat() call, which is inefficient */ if(stat(fpath->str, &st) == -1 || !S_ISDIR(st.st_mode)) continue; } new_path = fm_path_new_child(job->dir_path, name); _retry: fi = _new_info_for_native_file(job, new_path, fpath->str, &err); if (fi == NULL) /* we got a damaged file */ { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); GFile *gf; GFileInfo *inf; gchar *disp_basename; g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry; /* bug #3615271: Damaged mountpoint isn't shown let make a simple file info then */ inf = g_file_info_new(); gf = fm_path_to_gfile(new_path); g_file_info_set_file_type(inf, G_FILE_TYPE_UNKNOWN); g_file_info_set_name(inf, name); disp_basename = g_filename_display_basename(fpath->str); g_file_info_set_display_name(inf, disp_basename); g_free(disp_basename); g_file_info_set_content_type(inf, "inode/x-corrupted"); fi = fm_file_info_new_from_g_file_data(gf, inf, new_path); g_object_unref(inf); g_object_unref(gf); } fm_dir_list_job_add_found_file(job, fi); fm_file_info_unref(fi); fm_path_unref(new_path); } g_string_free(fpath, TRUE); g_dir_close(dir); } else { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_error_free(err); } g_free(path_str); return TRUE; }
/********************************************************************* * ... * * ********************************************************************/ gboolean fm_file_info_job_run (FmJob *fmjob) { FmFileInfoJob *job = (FmFileInfoJob*) fmjob; GError *err = NULL; GList *l; for (l = fm_list_peek_head_link (job->file_infos); !fm_job_is_cancelled (fmjob) && l; ) { FmFileInfo *file_info = (FmFileInfo*) l->data; GList *next = l->next; job->current = file_info->path; if (fm_path_is_native (file_info->path)) { char *path_str = fm_path_to_str (file_info->path); // FileInfo rework: new function for testing... // this one is not cancellable and doesn't handle errors... // if (!fm_file_info_job_get_info_for_native_file (FM_JOB (job), file_info, path_str, &err)) if (!fm_file_info_set_for_native_file (file_info, path_str)) { //~ FmJobErrorAction act = fm_job_emit_error (FM_JOB(job), err, FM_JOB_ERROR_MILD); //~ //~ g_error_free (err); //~ err = NULL; //~ //~ if (act == FM_JOB_RETRY) //~ continue; DEBUG ("fm_file_info_set_for_native_file: error reading %s\n", path_str); next = l->next; fm_list_delete_link (job->file_infos, l); // Also calls unref... } g_free (path_str); } else { GFile *gf; if (fm_path_is_virtual (file_info->path)) { // This is a xdg menu if (fm_path_is_xdg_menu (file_info->path)) { MenuCache *mc; MenuCacheDir *dir; char *path_str = fm_path_to_str (file_info->path); char *menu_name = path_str + 5, ch; char *dir_name; while (*menu_name == '/') ++menu_name; dir_name = menu_name; while (*dir_name && *dir_name != '/') ++dir_name; ch = *dir_name; *dir_name = '\0'; menu_name = g_strconcat (menu_name, ".menu", NULL); mc = menu_cache_lookup_sync (menu_name); g_free (menu_name); if (*dir_name && !(*dir_name == '/' && dir_name[1] == '\0')) { char *tmp = g_strconcat ("/", menu_cache_item_get_id (MENU_CACHE_ITEM(menu_cache_get_root_dir (mc))), dir_name, NULL); dir = menu_cache_get_dir_from_path (mc, tmp); g_free (tmp); } else { dir = menu_cache_get_root_dir (mc); } if (dir) { fm_file_info_set_from_menu_cache_item (file_info, (MenuCacheItem*) dir); } else { next = l->next; fm_list_delete_link (job->file_infos, l); // Also calls unref... } g_free (path_str); menu_cache_unref (mc); l = l->next; continue; } } gf = fm_path_to_gfile (file_info->path); if (!fm_file_info_job_get_info_for_gfile (FM_JOB (job), file_info, gf, &err)) { FmJobErrorAction act = fm_job_emit_error (FM_JOB (job), err, FM_JOB_ERROR_MILD); g_error_free (err); err = NULL; if (act == FM_JOB_RETRY) continue; next = l->next; fm_list_delete_link (job->file_infos, l); // Also calls unref... } g_object_unref (gf); } l = next; } return TRUE; }
static gboolean deep_count_gio(FmDeepCountJob* job, GFileInfo* inf, GFile* gf) { FmJob* fmjob = FM_JOB(job); GError* err = NULL; GFileType type; const char* fs_id; gboolean descend; if(inf) g_object_ref(inf); else { _retry_query_info: inf = g_file_query_info(gf, query_str, (job->flags & FM_DC_JOB_FOLLOW_LINKS) ? 0 : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if(!inf) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_query_info; return FALSE; } } if(fm_job_is_cancelled(fmjob)) { g_object_unref(inf); return FALSE; } type = g_file_info_get_file_type(inf); descend = TRUE; ++job->count; job->total_size += g_file_info_get_size(inf); job->total_ondisk_size += g_file_info_get_attribute_uint64(inf, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE); /* prepare for moving across different devices */ if( job->flags & FM_DC_JOB_PREPARE_MOVE ) { fs_id = g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM); if( g_strcmp0(fs_id, job->dest_fs_id) != 0 ) { /* files on different device requires an additional 'delete' for the source file. */ ++job->total_size; /* this is for the additional delete */ ++job->total_ondisk_size; ++job->count; } else descend = FALSE; } if( type == G_FILE_TYPE_DIRECTORY ) { FmPath* fm_path = fm_path_new_for_gfile(gf); /* check if we need to decends into the dir. */ /* trash:/// doesn't support deleting files recursively */ if(job->flags & FM_DC_JOB_PREPARE_DELETE && fm_path_is_trash(fm_path) && ! fm_path_is_trash_root(fm_path)) descend = FALSE; else { /* only descends into files on the same filesystem */ if( job->flags & FM_DC_JOB_SAME_FS ) { fs_id = g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM); descend = (g_strcmp0(fs_id, job->dest_fs_id) == 0); } } fm_path_unref(fm_path); g_object_unref(inf); inf = NULL; if(descend) { GFileEnumerator* enu; _retry_enum_children: enu = g_file_enumerate_children(gf, query_str, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if(enu) { while( !fm_job_is_cancelled(fmjob) ) { inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err); if(inf) { GFile* child = g_file_get_child(gf, g_file_info_get_name(inf)); deep_count_gio(job, inf, child); g_object_unref(child); g_object_unref(inf); inf = NULL; } else { if(err) /* error! */ { /* FM_JOB_RETRY is not supported */ /*FmJobErrorAction act = */ fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); err = NULL; } else { /* EOF is reached, do nothing. */ break; } } } g_file_enumerator_close(enu, NULL, NULL); g_object_unref(enu); } else { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_enum_children; } } } else g_object_unref(inf); return TRUE; }
static gboolean deep_count_posix(FmDeepCountJob* job, const char *path) { FmJob* fmjob = FM_JOB(job); struct stat st; int ret; _retry_stat: if( G_UNLIKELY(job->flags & FM_DC_JOB_FOLLOW_LINKS) ) ret = stat(path, &st); else ret = lstat(path, &st); if( ret == 0 ) { ++job->count; job->total_size += (goffset)st.st_size; job->total_ondisk_size += (st.st_blocks * 512); /* NOTE: if job->dest_dev is 0, that means our destination * folder is not on native UNIX filesystem. Hence it's not * on the same device. Our st.st_dev will always be non-zero * since our file is on a native UNIX filesystem. */ /* only descends into files on the same filesystem */ if( job->flags & FM_DC_JOB_SAME_FS ) { if( st.st_dev != job->dest_dev ) return TRUE; } /* only descends into files on the different filesystem */ else if( job->flags & FM_DC_JOB_PREPARE_MOVE ) { if( st.st_dev == job->dest_dev ) return TRUE; } } else { GError* err = g_error_new(G_IO_ERROR, g_io_error_from_errno(errno), "%s", g_strerror(errno)); FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_stat; return FALSE; } if(fm_job_is_cancelled(fmjob)) return FALSE; if( S_ISDIR(st.st_mode) ) /* if it's a dir */ { GDir* dir_ent = g_dir_open(path, 0, NULL); if(dir_ent) { const char* basename; while( !fm_job_is_cancelled(fmjob) && (basename = g_dir_read_name(dir_ent)) ) { char *sub = g_build_filename(path, basename, NULL); if(!fm_job_is_cancelled(fmjob)) { if(deep_count_posix(job, sub)) { /* for moving across different devices, an additional 'delete' * for source file is needed. so let's +1 for the delete.*/ if(job->flags & FM_DC_JOB_PREPARE_MOVE) { ++job->total_size; ++job->total_ondisk_size; ++job->count; } } } g_free(sub); } g_dir_close(dir_ent); } } return TRUE; }
static gboolean _fm_file_ops_job_copy_file(FmFileOpsJob* job, GFile* src, GFileInfo* inf, GFile* dest, FmFolder *src_folder, /* if move */ FmFolder *dest_folder) { gboolean ret = FALSE; gboolean delete_src = FALSE; GError* err = NULL; GFileType type; guint64 size; GFile* new_dest = NULL; GFileCopyFlags flags; FmJob* fmjob = FM_JOB(job); FmPath *fm_dest; guint32 mode; gboolean skip_dir_content = FALSE; /* FIXME: g_file_get_child() failed? generate error! */ g_return_val_if_fail(dest != NULL, FALSE); job->supported_options = FM_FILE_OP_RENAME | FM_FILE_OP_SKIP | FM_FILE_OP_OVERWRITE; if( G_LIKELY(inf) ) g_object_ref(inf); else { _retry_query_src_info: inf = g_file_query_info(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if( !inf ) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_query_src_info; return FALSE; } } if(!_fm_file_ops_job_check_paths(job, src, inf, dest)) { g_object_unref(inf); return FALSE; } /* if this is a cross-device move operation, delete source files. */ if( job->type == FM_FILE_OP_MOVE ) delete_src = TRUE; /* showing currently processed file. */ fm_file_ops_job_emit_cur_file(job, g_file_info_get_display_name(inf)); type = g_file_info_get_file_type(inf); size = g_file_info_get_size(inf); mode = g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_UNIX_MODE); g_object_unref(inf); inf = NULL; switch(type) { case G_FILE_TYPE_DIRECTORY: { GFileEnumerator* enu; gboolean dir_created = FALSE; _retry_mkdir: if( !fm_job_is_cancelled(fmjob) && !job->skip_dir_content && !g_file_make_directory(dest, fm_job_get_cancellable(fmjob), &err) ) { if(err->domain == G_IO_ERROR && (err->code == G_IO_ERROR_EXISTS || err->code == G_IO_ERROR_INVALID_FILENAME || err->code == G_IO_ERROR_FILENAME_TOO_LONG)) { GFile* dest_cp = new_dest; gboolean dest_exists = (err->code == G_IO_ERROR_EXISTS); FmFileOpOption opt = 0; g_error_free(err); err = NULL; new_dest = NULL; opt = _fm_file_ops_job_ask_new_name(job, src, dest, &new_dest, dest_exists); if(!new_dest) /* restoring status quo */ new_dest = dest_cp; else if(dest_cp) /* we got new new_dest, forget old one */ g_object_unref(dest_cp); switch(opt) { case FM_FILE_OP_RENAME: dest = new_dest; goto _retry_mkdir; break; case FM_FILE_OP_SKIP: /* when a dir is skipped, we need to know its total size to calculate correct progress */ job->finished += size; fm_file_ops_job_emit_percent(job); job->skip_dir_content = skip_dir_content = TRUE; dir_created = TRUE; /* pretend that dir creation succeeded */ break; case FM_FILE_OP_OVERWRITE: dir_created = TRUE; /* pretend that dir creation succeeded */ break; case FM_FILE_OP_CANCEL: fm_job_cancel(fmjob); break; case FM_FILE_OP_SKIP_ERROR: ; /* FIXME */ } } else if(!fm_job_is_cancelled(fmjob)) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_mkdir; } job->finished += size; fm_file_ops_job_emit_percent(job); } else { /* chmod the newly created dir properly */ if(!fm_job_is_cancelled(fmjob) && !job->skip_dir_content) { if(mode) { _retry_chmod_for_dir: mode |= (S_IRUSR|S_IWUSR); /* ensure we have rw permission to this file. */ if( !g_file_set_attribute_uint32(dest, G_FILE_ATTRIBUTE_UNIX_MODE, mode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err) ) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_chmod_for_dir; /* FIXME: some filesystems may not support this. */ } } dir_created = TRUE; } job->finished += size; fm_file_ops_job_emit_percent(job); } if(!dir_created) /* if target dir is not created, don't copy dir content */ { if(!job->skip_dir_content) job->skip_dir_content = skip_dir_content = TRUE; } /* the dest dir is created. let's copy its content. */ /* FIXME: handle the case when the dir cannot be created. */ else if(!fm_job_is_cancelled(fmjob)) { FmFolder *sub_folder; FmFolder *sub_src = NULL; if (delete_src) { FmPath *src_path = fm_path_new_for_gfile(src); sub_src = fm_folder_find_by_path(src_path); fm_path_unref(src_path); } fm_dest = fm_path_new_for_gfile(dest); sub_folder = fm_folder_find_by_path(fm_dest); /* inform folder we created directory */ if (!dest_folder || !_fm_folder_event_file_added(dest_folder, fm_dest)) fm_path_unref(fm_dest); _retry_enum_children: enu = g_file_enumerate_children(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if(enu) { int n_children = 0; int n_copied = 0; ret = TRUE; while( !fm_job_is_cancelled(fmjob) ) { inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err); if( inf ) { ++n_children; /* don't overwrite dir content, only calculate progress. */ if(G_UNLIKELY(job->skip_dir_content)) { /* FIXME: this is incorrect as we don't do the calculation recursively. */ job->finished += g_file_info_get_size(inf); fm_file_ops_job_emit_percent(job); } else { gboolean ret2; GFile* sub = g_file_get_child(src, g_file_info_get_name(inf)); GFile* sub_dest; char* tmp_basename; if(g_file_is_native(src) == g_file_is_native(dest)) /* both are native or both are virtual */ tmp_basename = NULL; else if(g_file_is_native(src)) /* copy from native to virtual */ tmp_basename = g_filename_to_utf8(g_file_info_get_name(inf), -1, NULL, NULL, NULL); /* gvfs escapes it itself */ else /* copy from virtual to native */ tmp_basename = fm_uri_subpath_to_native_subpath(g_file_info_get_name(inf), NULL); sub_dest = g_file_get_child(dest, tmp_basename ? tmp_basename : g_file_info_get_name(inf)); g_free(tmp_basename); ret2 = _fm_file_ops_job_copy_file(job, sub, inf, sub_dest, sub_src, sub_folder); g_object_unref(sub); g_object_unref(sub_dest); if(ret2) ++n_copied; else ret = FALSE; } g_object_unref(inf); } else { if(err) { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; /* FM_JOB_RETRY is not supported here */ ret = FALSE; } else /* EOF is reached */ { /* all files are successfully copied. */ if(fm_job_is_cancelled(fmjob)) ret = FALSE; else { if(dir_created) /* target dir is created */ { /* some files are not copied */ if(n_children != n_copied) { /* if the copy actions are skipped deliberately, it's ok */ if(!job->skip_dir_content) ret = FALSE; } } /* else job->skip_dir_content is TRUE */ } break; } } } g_file_enumerator_close(enu, NULL, &err); g_object_unref(enu); } else { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_enum_children; } if (sub_src) g_object_unref(sub_src); if (sub_folder) g_object_unref(sub_folder); } if(job->skip_dir_content) delete_src = FALSE; if(skip_dir_content) job->skip_dir_content = FALSE; } break; case G_FILE_TYPE_SPECIAL: /* only handle FIFO for local files */ if(g_file_is_native(src) && g_file_is_native(dest)) { char* src_path = g_file_get_path(src); struct stat src_st; int r; r = lstat(src_path, &src_st); g_free(src_path); if(r == 0) { /* Handle FIFO on native file systems. */ if(S_ISFIFO(src_st.st_mode)) { char* dest_path = g_file_get_path(dest); int r = mkfifo(dest_path, src_st.st_mode); g_free(dest_path); if( r == 0) ret = TRUE; } /* FIXME: how about block device, char device, and socket? */ } } if (!ret) { g_set_error(&err, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot copy file '%s': not supported"), g_file_info_get_display_name(inf)); fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_clear_error(&err); } goto _file_copied; default: flags = G_FILE_COPY_ALL_METADATA|G_FILE_COPY_NOFOLLOW_SYMLINKS; _retry_copy: if( !g_file_copy(src, dest, flags, fm_job_get_cancellable(fmjob), progress_cb, fmjob, &err) ) { flags &= ~G_FILE_COPY_OVERWRITE; /* handle existing files or file name conflict */ if(err->domain == G_IO_ERROR && (err->code == G_IO_ERROR_EXISTS || err->code == G_IO_ERROR_INVALID_FILENAME || err->code == G_IO_ERROR_FILENAME_TOO_LONG)) { GFile* dest_cp = new_dest; gboolean dest_exists = (err->code == G_IO_ERROR_EXISTS); FmFileOpOption opt = 0; g_error_free(err); err = NULL; new_dest = NULL; opt = _fm_file_ops_job_ask_new_name(job, src, dest, &new_dest, dest_exists); if(!new_dest) /* restoring status quo */ new_dest = dest_cp; else if(dest_cp) /* we got new new_dest, forget old one */ g_object_unref(dest_cp); switch(opt) { case FM_FILE_OP_RENAME: dest = new_dest; goto _retry_copy; break; case FM_FILE_OP_OVERWRITE: flags |= G_FILE_COPY_OVERWRITE; goto _retry_copy; break; case FM_FILE_OP_CANCEL: fm_job_cancel(fmjob); break; case FM_FILE_OP_SKIP: ret = TRUE; delete_src = FALSE; /* don't delete source file. */ break; case FM_FILE_OP_SKIP_ERROR: ; /* FIXME */ } } else { gboolean is_no_space = (err->domain == G_IO_ERROR && err->code == G_IO_ERROR_NO_SPACE); FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) { job->current_file_finished = 0; goto _retry_copy; } /* FIXME: ask to leave partial content? */ if(is_no_space) g_file_delete(dest, fm_job_get_cancellable(fmjob), NULL); ret = FALSE; delete_src = FALSE; } } else ret = TRUE; _file_copied: job->finished += size; job->current_file_finished = 0; if(ret && dest_folder) { fm_dest = fm_path_new_for_gfile(dest); if(!_fm_folder_event_file_added(dest_folder, fm_dest)) fm_path_unref(fm_dest); } /* update progress */ fm_file_ops_job_emit_percent(job); break; } /* if this is a cross-device move operation, delete source files. */ /* ret == TRUE means the copy is successful. */ if( !fm_job_is_cancelled(fmjob) && ret && delete_src ) ret = _fm_file_ops_job_delete_file(fmjob, src, inf, src_folder); /* delete the source file. */ if(new_dest) g_object_unref(new_dest); return ret; }
gboolean _fm_file_ops_job_move_run(FmFileOpsJob* job) { GFile *dest_dir; GFileInfo* inf; GList* l; GError* err = NULL; FmJob* fmjob = FM_JOB(job); dev_t dest_dev = 0; gboolean ret = TRUE; FmDeepCountJob* dc; FmPath *parent = NULL; FmFolder *df, *sf = NULL; /* get information of destination folder */ g_return_val_if_fail(job->dest, FALSE); dest_dir = fm_path_to_gfile(job->dest); _retry_query_dest_info: inf = g_file_query_info(dest_dir, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL"," G_FILE_ATTRIBUTE_UNIX_DEVICE"," G_FILE_ATTRIBUTE_ID_FILESYSTEM"," G_FILE_ATTRIBUTE_UNIX_DEVICE, 0, fm_job_get_cancellable(fmjob), &err); if(inf) { job->dest_fs_id = g_intern_string(g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM)); dest_dev = g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_UNIX_DEVICE); /* needed by deep count */ g_object_unref(inf); } else { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_query_dest_info; else { g_object_unref(dest_dir); return FALSE; } } /* prepare the job, count total work needed with FmDeepCountJob */ dc = fm_deep_count_job_new(job->srcs, FM_DC_JOB_PREPARE_MOVE); fm_deep_count_job_set_dest(dc, dest_dev, job->dest_fs_id); fm_job_run_sync(FM_JOB(dc)); job->total = dc->total_size; if( fm_job_is_cancelled(FM_JOB(dc)) ) { g_object_unref(dest_dir); g_object_unref(dc); return FALSE; } g_object_unref(dc); g_debug("total size to move: %llu, dest_fs: %s", (long long unsigned int)job->total, job->dest_fs_id); fm_file_ops_job_emit_prepared(job); /* suspend updates for destination */ df = fm_folder_find_by_path(job->dest); if (df) fm_folder_block_updates(df); for(l = fm_path_list_peek_head_link(job->srcs); !fm_job_is_cancelled(fmjob) && l; l=l->next) { FmPath* path = FM_PATH(l->data); GFile* src = fm_path_to_gfile(path); GFile* dest; char* tmp_basename; /* do with updates for source */ if (fm_path_get_parent(path) != parent && fm_path_get_parent(path) != NULL) { FmFolder *pf; pf = fm_folder_find_by_path(fm_path_get_parent(path)); if (pf != sf) { if (sf) { fm_folder_unblock_updates(sf); g_object_unref(sf); } if (pf) fm_folder_block_updates(pf); sf = pf; } else if (pf) g_object_unref(pf); } parent = fm_path_get_parent(path); if(g_file_is_native(src) && g_file_is_native(dest_dir)) /* both are native */ tmp_basename = NULL; else if(g_file_is_native(src)) /* move from native to virtual */ tmp_basename = g_filename_to_utf8(fm_path_get_basename(path), -1, NULL, NULL, NULL); /* gvfs escapes it itself */ else /* move from virtual to native/virtual */ tmp_basename = fm_uri_subpath_to_native_subpath(fm_path_get_basename(path), NULL); dest = g_file_get_child(dest_dir, tmp_basename ? tmp_basename : fm_path_get_basename(path)); g_free(tmp_basename); if(!_fm_file_ops_job_move_file(job, src, NULL, dest, path, sf, df)) ret = FALSE; g_object_unref(src); g_object_unref(dest); if(!ret) break; } /* restore updates for destination and source */ if (df) { fm_folder_unblock_updates(df); g_object_unref(df); } if (sf) { fm_folder_unblock_updates(sf); g_object_unref(sf); } g_object_unref(dest_dir); return ret; }
gboolean _fm_file_ops_job_copy_run(FmFileOpsJob* job) { gboolean ret = TRUE; GFile *dest_dir; GList* l; FmJob* fmjob = FM_JOB(job); /* prepare the job, count total work needed with FmDeepCountJob */ FmDeepCountJob* dc = fm_deep_count_job_new(job->srcs, FM_DC_JOB_DEFAULT); FmFolder *df; /* let the deep count job share the same cancellable object. */ fm_job_set_cancellable(FM_JOB(dc), fm_job_get_cancellable(fmjob)); fm_job_run_sync(FM_JOB(dc)); job->total = dc->total_size; if(fm_job_is_cancelled(fmjob)) { g_object_unref(dc); return FALSE; } g_object_unref(dc); g_debug("total size to copy: %llu", (long long unsigned int)job->total); dest_dir = fm_path_to_gfile(job->dest); /* suspend updates for destination */ df = fm_folder_find_by_path(job->dest); if (df) fm_folder_block_updates(df); fm_file_ops_job_emit_prepared(job); for(l = fm_path_list_peek_head_link(job->srcs); !fm_job_is_cancelled(fmjob) && l; l=l->next) { FmPath* path = FM_PATH(l->data); GFile* src = fm_path_to_gfile(path); GFile* dest; char* tmp_basename; if(g_file_is_native(src) && g_file_is_native(dest_dir)) /* both are native */ tmp_basename = NULL; else if(g_file_is_native(src)) /* copy from native to virtual */ tmp_basename = g_filename_to_utf8(fm_path_get_basename(path), -1, NULL, NULL, NULL); /* gvfs escapes it itself */ else /* copy from virtual to native/virtual */ { /* if we drop URI query onto native filesystem, omit query part */ const char *basename = fm_path_get_basename(path); char *sub_name; sub_name = strchr(basename, '?'); if (sub_name) { sub_name = g_strndup(basename, sub_name - basename); basename = strrchr(sub_name, G_DIR_SEPARATOR); if (basename) basename++; else basename = sub_name; } tmp_basename = fm_uri_subpath_to_native_subpath(basename, NULL); g_free(sub_name); } dest = g_file_get_child(dest_dir, tmp_basename ? tmp_basename : fm_path_get_basename(path)); g_free(tmp_basename); if(!_fm_file_ops_job_copy_file(job, src, NULL, dest, NULL, df)) ret = FALSE; g_object_unref(src); g_object_unref(dest); } /* g_debug("finished: %llu, total: %llu", job->finished, job->total); */ fm_file_ops_job_emit_percent(job); /* restore updates for destination */ if (df) { fm_folder_unblock_updates(df); g_object_unref(df); } g_object_unref(dest_dir); return ret; }
gboolean _fm_file_ops_job_move_file(FmFileOpsJob* job, GFile* src, GFileInfo* inf, GFile* dest, FmPath *src_path, FmFolder *src_folder, FmFolder *dest_folder) { GError* err = NULL; FmJob* fmjob = FM_JOB(job); const char* src_fs_id; gboolean ret = TRUE; GFile* new_dest = NULL; job->supported_options = FM_FILE_OP_RENAME | FM_FILE_OP_SKIP | FM_FILE_OP_OVERWRITE; if( G_LIKELY(inf) ) g_object_ref(inf); else { _retry_query_src_info: inf = g_file_query_info(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if( !inf ) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_query_src_info; return FALSE; } } if(!_fm_file_ops_job_check_paths(job, src, inf, dest)) { g_object_unref(inf); return FALSE; } src_fs_id = g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM); /* Check if source and destination are on the same device */ if( job->type == FM_FILE_OP_UNTRASH || g_strcmp0(src_fs_id, job->dest_fs_id) == 0 ) /* same device */ { guint64 size; GFileCopyFlags flags = G_FILE_COPY_ALL_METADATA|G_FILE_COPY_NOFOLLOW_SYMLINKS; FmPath *fm_dest; fm_dest = fm_path_new_for_gfile(dest); /* showing currently processed file. */ fm_file_ops_job_emit_cur_file(job, g_file_info_get_display_name(inf)); _retry_move: if( !g_file_move(src, dest, flags, fm_job_get_cancellable(fmjob), progress_cb, job, &err)) { flags &= ~G_FILE_COPY_OVERWRITE; if(err->domain == G_IO_ERROR && err->code == G_IO_ERROR_EXISTS) { GFile* dest_cp = new_dest; FmFileOpOption opt = 0; new_dest = NULL; opt = _fm_file_ops_job_ask_new_name(job, src, dest, &new_dest, TRUE); if(!new_dest) /* restoring status quo */ new_dest = dest_cp; else if(dest_cp) /* we got new new_dest, forget old one */ g_object_unref(dest_cp); g_error_free(err); err = NULL; switch(opt) { case FM_FILE_OP_RENAME: dest = new_dest; goto _retry_move; break; case FM_FILE_OP_OVERWRITE: flags |= G_FILE_COPY_OVERWRITE; if(g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY) /* merge dirs */ { GFileEnumerator* enu = g_file_enumerate_children(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if(enu) { GFileInfo* child_inf; FmFolder *sub_src_folder = fm_folder_find_by_path(src_path); FmFolder *sub_dest_folder = fm_folder_find_by_path(fm_dest); while(!fm_job_is_cancelled(fmjob)) { child_inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err); if(child_inf) { GFile* child = g_file_get_child(src, g_file_info_get_name(child_inf)); GFile* child_dest = g_file_get_child(dest, g_file_info_get_name(child_inf)); FmPath *child_path = fm_path_new_for_gfile(child); _fm_file_ops_job_move_file(job, child, child_inf, child_dest, child_path, sub_src_folder, sub_dest_folder); g_object_unref(child); g_object_unref(child_dest); g_object_unref(child_inf); fm_path_unref(child_path); } else { if(err) /* error */ { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; } else /* EOF */ { break; } } } if (sub_src_folder) g_object_unref(sub_src_folder); if (sub_dest_folder) g_object_unref(sub_dest_folder); g_object_unref(enu); } else { /*FmJobErrorAction act = */ fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; /* if(act == FM_JOB_RETRY) goto _retry_move; */ } /* remove source dir after its content is merged with destination dir */ if(!g_file_delete(src, fm_job_get_cancellable(fmjob), &err)) { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; /* FIXME: should this be recoverable? */ } } else /* the destination is a file, just overwrite it. */ goto _retry_move; break; case FM_FILE_OP_CANCEL: fm_job_cancel(fmjob); ret = FALSE; break; case FM_FILE_OP_SKIP: ret = TRUE; break; case FM_FILE_OP_SKIP_ERROR: ; /* FIXME */ } } if(err) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_move; } fm_path_unref(fm_dest); } else { if (src_folder) _fm_folder_event_file_deleted(src_folder, src_path); if (!dest_folder || !_fm_folder_event_file_added(dest_folder, fm_dest)) fm_path_unref(fm_dest); } /* size = g_file_info_get_attribute_uint64(inf, G_FILE_ATTRIBUTE_UNIX_BLOCKS); size *= g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE); */ size = g_file_info_get_size(inf); job->finished += size; fm_file_ops_job_emit_percent(job); } else /* use copy if they are on different devices */ { /* use copy & delete */ /* source file will be deleted in _fm_file_ops_job_copy_file() */ ret = _fm_file_ops_job_copy_file(job, src, inf, dest, src_folder, dest_folder); } if(new_dest) g_object_unref(new_dest); g_object_unref(inf); return ret; }