/** * @file gnome-cmd-xfer.cc * @copyright (C) 2001-2006 Marcus Bjurman\n * @copyright (C) 2007-2012 Piotr Eljasiak\n * @copyright (C) 2013-2023 Uwe Scholz\n * * @copyright This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * @copyright This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * @copyright You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "gnome-cmd-includes.h" #include "gnome-cmd-con-list.h" #include "gnome-cmd-plain-path.h" #include "gnome-cmd-xfer.h" #include "gnome-cmd-file-selector.h" #include "gnome-cmd-file-list.h" #include "gnome-cmd-dir.h" #include "gnome-cmd-main-win.h" #include "gnome-cmd-data.h" #include "utils.h" #include "src/dialogs/gnome-cmd-delete-dialog.h" using namespace std; inline void free_xfer_data (XferData *xferData) { // free the list with target uris for (GList *i = xferData->destGFileList; i; i = i->next) { auto gFile = (GFile *) i->data; g_object_unref (gFile); } // free the list with target uris for (GList *i = xferData->srcGFileList; i; i = i->next) { auto gFile = (GFile *) i->data; g_object_unref (gFile); } g_list_free (xferData->destGFileList); g_list_free (xferData->srcGFileList); g_free(xferData->curSrcFileName); g_free (xferData); } static XferData * create_xfer_data (GFileCopyFlags copyFlags, GList *srcGFileList, GList *destGFileList, GnomeCmdDir *to_dir, GnomeCmdFileList *src_fl, GList *src_files, GnomeCmdConfirmOverwriteMode overwriteMode, GFunc on_completed_func, gpointer on_completed_data) { XferData *xferData = g_new0 (XferData, 1); xferData->copyFlags = copyFlags; xferData->srcGFileList = srcGFileList; xferData->destGFileList = destGFileList; xferData->destGnomeCmdDir = to_dir; xferData->src_fl = src_fl; xferData->src_files = src_files; xferData->win = nullptr; xferData->cur_file_name = nullptr; xferData->curFileNumber = 0; xferData->filesTotal = 0; xferData->first_time = TRUE; xferData->on_completed_func = on_completed_func; xferData->on_completed_data = on_completed_data; xferData->done = FALSE; xferData->aborted = FALSE; xferData->problem = FALSE; xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; xferData->thread = nullptr; xferData->error = nullptr; xferData->overwriteMode = overwriteMode; return xferData; } inline gchar *get_file_details_string(GFile *gFile) { auto gFileInfoTmp = g_file_query_info(gFile, G_FILE_ATTRIBUTE_STANDARD_SIZE "," G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, nullptr, nullptr); gchar *size = create_nice_size_str (g_file_info_get_attribute_uint64(gFileInfoTmp, G_FILE_ATTRIBUTE_STANDARD_SIZE)); gchar *details = gFileInfoTmp != nullptr ? g_strdup_printf ("%s, %s", size, time2string (g_file_info_get_modification_date_time(gFileInfoTmp), gnome_cmd_data.options.date_format)) : g_strdup (""); g_free (size); g_object_unref(gFileInfoTmp); return details; } static void run_simple_error_dialog(const char *msg, XferData *xferData) { gdk_threads_enter (); gint guiResponse = run_simple_dialog ( *main_win, TRUE, GTK_MESSAGE_ERROR, msg, _("Transfer problem"), -1, _("Abort"), _("Retry"), _("Skip"), NULL); gdk_threads_leave (); switch (guiResponse) { case 0: xferData->problem_action = COPY_ERROR_ACTION_ABORT; break; case 1: xferData->problem_action = COPY_ERROR_ACTION_RETRY; break; case 2: xferData->problem_action = COPY_ERROR_ACTION_SKIP; break; default: xferData->problem_action = COPY_ERROR_ACTION_RETRY; break; } } static void run_directory_copy_overwrite_dialog(const char *msg, XferData *xferData) { gint guiResponse = -1; g_list_length(xferData->srcGFileList) > 1 ? guiResponse = run_simple_dialog ( *main_win, TRUE, GTK_MESSAGE_ERROR, msg, _("Copy problem"), -1, _("Abort"), _("Retry"), _("Copy into"), _("Rename"), _("Rename all"), _("Skip"), _("Skip all"), NULL) : guiResponse = run_simple_dialog ( *main_win, TRUE, GTK_MESSAGE_ERROR, msg, _("Copy problem"), -1, _("Abort"), _("Retry"), _("Copy into"), _("Rename"), NULL); switch (guiResponse) { case 0: xferData->problem_action = COPY_ERROR_ACTION_ABORT; break; case 1: xferData->problem_action = COPY_ERROR_ACTION_RETRY; break; case 2: xferData->problem_action = COPY_ERROR_ACTION_COPY_INTO; break; case 3: xferData->problem_action = COPY_ERROR_ACTION_RENAME; break; case 4: xferData->problem_action = COPY_ERROR_ACTION_RENAME_ALL; break; case 5: xferData->problem_action = COPY_ERROR_ACTION_SKIP; break; case 6: xferData->problem_action = COPY_ERROR_ACTION_SKIP_ALL; break; default: xferData->problem_action = COPY_ERROR_ACTION_RETRY; break; } } static void run_file_copy_overwrite_dialog(XferData *xferData) { gint guiResponse = -1; auto problemSrcBasename = get_gfile_attribute_string(xferData->problemSrcGFile, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); auto problemDestBasename = get_gfile_attribute_string(xferData->problemDestGFile, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); auto sourceDetails = get_file_details_string (xferData->problemSrcGFile); auto targetDetails = get_file_details_string (xferData->problemDestGFile); // ToDo: Adjust wording dynamically, see also below at run_move_overwrite_dialog auto msg = g_strdup_printf (_("Overwrite file:\n\n%s\n%s\n\nWith:\n\n%s\n%s"), problemDestBasename, targetDetails, problemSrcBasename, sourceDetails); g_free (problemSrcBasename); g_free (problemDestBasename); g_free (sourceDetails); g_free (targetDetails); gdk_threads_enter (); xferData->filesTotal > 1 ? guiResponse = run_simple_dialog ( *main_win, TRUE, GTK_MESSAGE_ERROR, msg, _("Copy problem"), -1, _("Abort"), _("Retry"), _("Replace"), _("Rename"), _("Skip"), _("Replace all"), _("Rename all"), _("Skip all"), NULL) : guiResponse = run_simple_dialog ( *main_win, TRUE, GTK_MESSAGE_ERROR, msg, _("Copy problem"), -1, _("Abort"), _("Retry"), _("Replace"), _("Rename"), NULL); gdk_threads_leave (); switch (guiResponse) { case 0: xferData->problem_action = COPY_ERROR_ACTION_ABORT; break; case 1: xferData->problem_action = COPY_ERROR_ACTION_RETRY; break; case 2: xferData->problem_action = COPY_ERROR_ACTION_REPLACE; break; case 3: xferData->problem_action = COPY_ERROR_ACTION_RENAME; break; case 4: xferData->problem_action = COPY_ERROR_ACTION_SKIP; break; case 5: xferData->problem_action = COPY_ERROR_ACTION_REPLACE_ALL; break; case 6: xferData->problem_action = COPY_ERROR_ACTION_RENAME_ALL; break; case 7: xferData->problem_action = COPY_ERROR_ACTION_SKIP_ALL; break; default: xferData->problem_action = COPY_ERROR_ACTION_RETRY; break; } g_free (msg); } static void update_transfer_gui_error_copy (XferData *xferData) { gchar *msg = g_strdup_printf (_("Error while transferring “%s”\n\n%s"), g_file_peek_path(xferData->problemSrcGFile), xferData->error->message); if(!g_error_matches(xferData->error, G_IO_ERROR, G_IO_ERROR_EXISTS)) { run_simple_error_dialog(msg, xferData); } else { if (xferData->currentSrcFileType == G_FILE_TYPE_DIRECTORY) { gdk_threads_enter (); switch(xferData->overwriteMode) { case GNOME_CMD_CONFIRM_OVERWRITE_SKIP_ALL: xferData->problem_action = COPY_ERROR_ACTION_SKIP_ALL; break; case GNOME_CMD_CONFIRM_OVERWRITE_RENAME_ALL: xferData->problem_action = COPY_ERROR_ACTION_RENAME_ALL; break; case GNOME_CMD_CONFIRM_OVERWRITE_SILENTLY: xferData->problem_action = COPY_ERROR_ACTION_COPY_INTO; break; case GNOME_CMD_CONFIRM_OVERWRITE_QUERY: default: run_directory_copy_overwrite_dialog(msg, xferData); break; } gdk_threads_leave (); } if (xferData->currentSrcFileType == G_FILE_TYPE_REGULAR) { switch(xferData->overwriteMode) { case GNOME_CMD_CONFIRM_OVERWRITE_SKIP_ALL: xferData->problem_action = COPY_ERROR_ACTION_SKIP_ALL; break; case GNOME_CMD_CONFIRM_OVERWRITE_RENAME_ALL: xferData->problem_action = COPY_ERROR_ACTION_RENAME_ALL; break; case GNOME_CMD_CONFIRM_OVERWRITE_SILENTLY: xferData->problem_action = COPY_ERROR_ACTION_REPLACE_ALL; break; case GNOME_CMD_CONFIRM_OVERWRITE_QUERY: default: run_file_copy_overwrite_dialog(xferData); break; } } } g_free (msg); } static void run_move_overwrite_dialog(XferData *xferData) { gint guiResponse = -1; auto problemSrcBasename = get_gfile_attribute_string(xferData->problemSrcGFile, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); auto problemDestBasename = get_gfile_attribute_string(xferData->problemDestGFile, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); auto sourceDetails = get_file_details_string (xferData->problemSrcGFile); auto targetDetails = get_file_details_string (xferData->problemDestGFile); // ToDo: Adjust wording dynamically, see also above at run_file_copy_overwrite_dialog auto msg = g_strdup_printf (_("Overwrite file:\n\n%s\n%s\n\nWith:\n\n%s\n%s"), problemDestBasename, targetDetails, problemSrcBasename, sourceDetails); g_free (problemSrcBasename); g_free (problemDestBasename); g_free (sourceDetails); g_free (targetDetails); xferData->filesTotal > 1 ? guiResponse = run_simple_dialog ( *main_win, TRUE, GTK_MESSAGE_ERROR, msg, _("Move problem"), -1, _("Abort"), _("Retry"), _("Replace"), _("Rename"), _("Skip"), _("Replace all"), _("Rename all"), _("Skip all"), NULL) : guiResponse = run_simple_dialog ( *main_win, TRUE, GTK_MESSAGE_ERROR, msg, _("Move problem"), -1, _("Abort"), _("Retry"), _("Replace"), _("Rename"), NULL); switch (guiResponse) { case 0: xferData->problem_action = COPY_ERROR_ACTION_ABORT; break; case 1: xferData->problem_action = COPY_ERROR_ACTION_RETRY; break; case 2: xferData->problem_action = COPY_ERROR_ACTION_REPLACE; break; case 3: xferData->problem_action = COPY_ERROR_ACTION_RENAME; break; case 4: xferData->problem_action = COPY_ERROR_ACTION_SKIP; break; case 5: xferData->problem_action = COPY_ERROR_ACTION_REPLACE_ALL; break; case 6: xferData->problem_action = COPY_ERROR_ACTION_RENAME_ALL; break; case 7: xferData->problem_action = COPY_ERROR_ACTION_SKIP_ALL; break; default: xferData->problem_action = COPY_ERROR_ACTION_RETRY; break; } } static void update_transfer_gui_error_move (XferData *xferData) { if(!g_error_matches(xferData->error, G_IO_ERROR, G_IO_ERROR_EXISTS)) { gchar *msg = g_strdup_printf (_("Error while transferring “%s”\n\n%s"), g_file_peek_path(xferData->problemSrcGFile), xferData->error->message); run_simple_error_dialog(msg, xferData); g_free (msg); } else { gdk_threads_enter (); switch(xferData->overwriteMode) { case GNOME_CMD_CONFIRM_OVERWRITE_SKIP_ALL: xferData->problem_action = COPY_ERROR_ACTION_SKIP_ALL; break; case GNOME_CMD_CONFIRM_OVERWRITE_RENAME_ALL: xferData->problem_action = COPY_ERROR_ACTION_RENAME_ALL; break; case GNOME_CMD_CONFIRM_OVERWRITE_SILENTLY: // Note: When doing a native move operation, there is no option to // move a folder into another folder like it is available for the copy // process. The reason is that in the former case, we don't step recursively // through the subdirectories. That's why we only offer a 'replace', but no // 'copy into' when moving folders. xferData->problem_action = COPY_ERROR_ACTION_REPLACE_ALL; break; case GNOME_CMD_CONFIRM_OVERWRITE_QUERY: default: run_move_overwrite_dialog(xferData); break; } gdk_threads_leave (); } } static void finish_xfer(XferData *xferData, gboolean threaded) { if (threaded) g_thread_join (xferData->thread); // Only update the files if needed if (xferData->destGnomeCmdDir) { gnome_cmd_dir_relist_files (xferData->destGnomeCmdDir, FALSE); main_win->focus_file_lists(); gnome_cmd_dir_unref (xferData->destGnomeCmdDir); xferData->destGnomeCmdDir = nullptr; } else main_win->focus_file_lists(); if (xferData->win) { gtk_widget_destroy (GTK_WIDGET (xferData->win)); xferData->win = nullptr; } if ((!threaded || xferData->aborted || xferData->problem_action == COPY_ERROR_ACTION_NO_ACTION_YET) && xferData->on_completed_func) { xferData->on_completed_func (xferData->on_completed_data, nullptr); } if (threaded) g_mutex_clear(&xferData->mutex); if (xferData->cancellable) g_object_unref (xferData->cancellable); free_xfer_data (xferData); } static gboolean update_transfer_gui (XferData *xferData) { g_mutex_lock (&xferData->mutex); if (xferData->problem && xferData->error) { switch (xferData->transferType) { case COPY: update_transfer_gui_error_copy(xferData); break; case MOVE: update_transfer_gui_error_move(xferData); break; case LINK: break; } xferData->problem = FALSE; } if (xferData->win && xferData->win->cancel_pressed) { xferData->aborted = TRUE; if (xferData->cancellable) g_cancellable_cancel (xferData->cancellable); g_mutex_unlock (&xferData->mutex); finish_xfer(xferData, TRUE); return FALSE; } if (xferData->bytesTotalTransferred == 0) gnome_cmd_xfer_progress_win_set_action (xferData->win, _("copying…")); if (xferData->curFileNumber) { gchar *msg = g_strdup_printf (_("[file %ld of %ld] “%s”"), xferData->curFileNumber, xferData->filesTotal, xferData->curSrcFileName); gnome_cmd_xfer_progress_win_set_msg (xferData->win, msg); g_free (msg); } if (xferData->bytesTotal > 0) { gfloat totalProgress = (gfloat)((gdouble)(xferData->bytesTotalTransferred + xferData->bytesCopiedFile) / (gdouble)xferData->bytesTotal); if ((totalProgress >= 0.0 && totalProgress <= 1.0) || xferData->first_time) { xferData->first_time = FALSE; gnome_cmd_xfer_progress_win_set_total_progress (xferData->win, xferData->bytesCopiedFile, xferData->fileSize, xferData->bytesTotalTransferred + xferData->bytesCopiedFile, xferData->bytesTotal); while (gtk_events_pending ()) gtk_main_iteration_do (FALSE); } } g_mutex_unlock (&xferData->mutex); if (xferData->done) { finish_xfer(xferData, TRUE); return FALSE; } return TRUE; } static gboolean update_tmp_download_gui (XferData *xferData) { g_mutex_lock (&xferData->mutex); if (xferData->problem && xferData->error) { gchar *msg = g_strdup_printf (_("Error while transferring “%s”\n\n%s"), g_file_peek_path(xferData->problemSrcGFile), xferData->error->message); run_simple_error_dialog(msg, xferData); g_free (msg); xferData->problem = FALSE; } if (xferData->win && xferData->win->cancel_pressed) { xferData->aborted = TRUE; if (xferData->cancellable) g_cancellable_cancel (xferData->cancellable); g_mutex_unlock (&xferData->mutex); finish_xfer(xferData, TRUE); return FALSE; } g_mutex_unlock (&xferData->mutex); if (xferData->done) { finish_xfer(xferData, TRUE); return FALSE; } return TRUE; } inline gboolean gfile_is_parent_to_dir_or_equal (GFile *gFile, GnomeCmdDir *dir) { auto dir_gFile = GNOME_CMD_FILE (dir)->get_gfile (); gboolean is_parent = g_file_has_parent (dir_gFile, gFile); gboolean are_equal = g_file_equal (gFile, dir_gFile); g_object_unref (dir_gFile); return is_parent || are_equal; } inline gchar *remove_basename (const gchar *in) { gchar *out = g_strdup (in); for (gint i=strlen(out)-1; i>0; i--) if (out[i] == '/') { out[i] = '\0'; return out; } return nullptr; } inline gboolean file_is_already_in_dir (GFile *gFile, GnomeCmdDir *dir) { gchar *uri_str = remove_basename (g_file_get_uri(gFile)); if (!uri_str) return false; gchar *dir_uri_str = GNOME_CMD_FILE (dir)->get_uri_str(); gboolean ret = (strcmp (uri_str, dir_uri_str) == 0); g_free (uri_str); g_free (dir_uri_str); return ret; } static void set_files_total(XferData *xferData) { for(auto gFileSrcListItem = xferData->srcGFileList; gFileSrcListItem; gFileSrcListItem = gFileSrcListItem->next) { guint64 diskUsage = 0; guint64 numFiles = 0; auto gFile = (GFile *) gFileSrcListItem->data; g_return_if_fail(G_IS_FILE(gFile)); g_file_measure_disk_usage (gFile, G_FILE_MEASURE_NONE, nullptr, nullptr, nullptr, &diskUsage, nullptr, &numFiles, nullptr); xferData->bytesTotal += diskUsage; xferData->filesTotal += numFiles; } } void gnome_cmd_copy_gfiles_start (GList *srcGFileGList, GnomeCmdDir *destGnomeCmdDir, GnomeCmdFileList *srcGnomeCmdFileList, GList *srcFilesGList, gchar *destFileName, GFileCopyFlags copyFlags, GnomeCmdConfirmOverwriteMode overwriteMode, GtkSignalFunc on_completed_func, gpointer on_completed_data) { g_return_if_fail (srcGFileGList != nullptr); g_return_if_fail (GNOME_CMD_IS_DIR (destGnomeCmdDir)); GFile *srcGFile, *destGFile; // Sanity check for (GList *i = srcGFileGList; i; i = i->next) { srcGFile = (GFile *) i->data; if (gfile_is_parent_to_dir_or_equal (srcGFile, destGnomeCmdDir)) { gnome_cmd_show_message (*main_win, _("Copying a directory into itself is a bad idea."), _("The whole operation was cancelled.")); return; } if (file_is_already_in_dir (srcGFile, destGnomeCmdDir)) { auto srcFilename = g_file_get_basename(srcGFile); if (destFileName && strcmp (destFileName, srcFilename) == 0) { g_free(srcFilename); DEBUG ('x', "Copying a file to the same directory as it's already in, is not permitted\n"); return; } g_free(srcFilename); } } XferData *xferData = create_xfer_data (copyFlags, srcGFileGList, nullptr, destGnomeCmdDir, srcGnomeCmdFileList, srcFilesGList, overwriteMode, (GFunc) on_completed_func, on_completed_data); xferData->transferType = COPY; if (g_list_length (srcGFileGList) == 1 && destFileName != nullptr) { destGFile = gnome_cmd_dir_get_child_gfile (destGnomeCmdDir, destFileName); xferData->destGFileList = g_list_append (xferData->destGFileList, destGFile); } else { for (auto srcGFileGListItem = srcGFileGList; srcGFileGListItem; srcGFileGListItem = srcGFileGListItem->next) { srcGFile = (GFile *) srcGFileGListItem->data; auto basename = g_file_get_basename(srcGFile); destGFile = gnome_cmd_dir_get_child_gfile (destGnomeCmdDir, basename); g_free (basename); xferData->destGFileList = g_list_append (xferData->destGFileList, destGFile); } } g_free (destFileName); xferData->cancellable = g_cancellable_new(); set_files_total(xferData); xferData->win = GNOME_CMD_XFER_PROGRESS_WIN (gnome_cmd_xfer_progress_win_new (xferData->filesTotal)); gtk_widget_ref (GTK_WIDGET (xferData->win)); gtk_window_set_title (GTK_WINDOW (xferData->win), _("preparing…")); gtk_widget_show (GTK_WIDGET (xferData->win)); g_mutex_init(&xferData->mutex); // start the transfer xferData->thread = g_thread_new (NULL, (GThreadFunc) gnome_cmd_transfer_gfiles, xferData); g_timeout_add (gnome_cmd_data.gui_update_rate, (GSourceFunc) update_transfer_gui, xferData); } void gnome_cmd_move_gfiles_start (GList *srcGFileGList, GnomeCmdDir *destGnomeCmdDir, GnomeCmdFileList *srcGnomeCmdFileList, GList *srcGnomeCmdFileGList, gchar *destFileName, GFileCopyFlags copyFlags, GnomeCmdConfirmOverwriteMode overwriteMode, GtkSignalFunc on_completed_func, gpointer on_completed_data) { g_return_if_fail (srcGFileGList != nullptr); g_return_if_fail (GNOME_CMD_IS_DIR (destGnomeCmdDir)); GFile *srcGFile, *destGFile; // Sanity check for (GList *i = srcGFileGList; i; i = i->next) { srcGFile = (GFile *) i->data; if (gfile_is_parent_to_dir_or_equal (srcGFile, destGnomeCmdDir)) { gnome_cmd_show_message (*main_win, _("Moving a directory into itself is a bad idea."), _("The whole operation was cancelled.")); return; } if (file_is_already_in_dir (srcGFile, destGnomeCmdDir)) { auto srcFilename = g_file_get_basename(srcGFile); if (destFileName && strcmp (destFileName, srcFilename) == 0) { g_free(srcFilename); DEBUG ('x', "Moving a file to the same directory as it's already in, is not permitted\n"); return; } g_free(srcFilename); } } XferData *xferData = create_xfer_data (copyFlags, srcGFileGList, nullptr, destGnomeCmdDir, srcGnomeCmdFileList, srcGnomeCmdFileGList, overwriteMode, (GFunc) on_completed_func, on_completed_data); xferData->transferType = MOVE; if (g_list_length (srcGFileGList) == 1 && destFileName != nullptr) { destGFile = gnome_cmd_dir_get_child_gfile (destGnomeCmdDir, destFileName); xferData->destGFileList = g_list_append (xferData->destGFileList, destGFile); } else { for (auto srcGFileGListItem = srcGFileGList; srcGFileGListItem; srcGFileGListItem = srcGFileGListItem->next) { srcGFile = (GFile *) srcGFileGListItem->data; auto basename = g_file_get_basename(srcGFile); destGFile = gnome_cmd_dir_get_child_gfile (destGnomeCmdDir, basename); g_free (basename); xferData->destGFileList = g_list_append (xferData->destGFileList, destGFile); } } g_free (destFileName); xferData->cancellable = g_cancellable_new(); set_files_total(xferData); xferData->win = GNOME_CMD_XFER_PROGRESS_WIN (gnome_cmd_xfer_progress_win_new (xferData->filesTotal)); gtk_widget_ref (GTK_WIDGET (xferData->win)); gtk_window_set_title (GTK_WINDOW (xferData->win), _("preparing…")); gtk_widget_show (GTK_WIDGET (xferData->win)); g_mutex_init(&xferData->mutex); // start the transfer xferData->thread = g_thread_new (NULL, (GThreadFunc) gnome_cmd_transfer_gfiles, xferData); g_timeout_add (gnome_cmd_data.gui_update_rate, (GSourceFunc) update_transfer_gui, xferData); } void gnome_cmd_link_gfiles_start (GList *srcGFileGList, GnomeCmdDir *destGnomeCmdDir, GnomeCmdFileList *srcGnomeCmdFileList, GList *srcGnomeCmdFileGList, gchar *destFileName, GFileCopyFlags copyFlags, GnomeCmdConfirmOverwriteMode overwriteMode, GtkSignalFunc on_completed_func, gpointer on_completed_data) { g_return_if_fail (srcGFileGList != nullptr); g_return_if_fail (GNOME_CMD_IS_DIR (destGnomeCmdDir)); GFile *srcGFile, *destGFile; // Sanity check for (GList *i = srcGFileGList; i; i = i->next) { srcGFile = (GFile *) i->data; if (gfile_is_parent_to_dir_or_equal (srcGFile, destGnomeCmdDir)) { gnome_cmd_show_message (*main_win, _("Moving a directory into itself is a bad idea."), _("The whole operation was cancelled.")); return; } if (file_is_already_in_dir (srcGFile, destGnomeCmdDir)) { auto srcFilename = g_file_get_basename(srcGFile); if (destFileName && strcmp (destFileName, srcFilename) == 0) { g_free(srcFilename); DEBUG ('x', "Moving a file to the same directory as it's already in, is not permitted\n"); return; } g_free(srcFilename); } } XferData *xferData = create_xfer_data (copyFlags, srcGFileGList, nullptr, destGnomeCmdDir, srcGnomeCmdFileList, srcGnomeCmdFileGList, overwriteMode, (GFunc) on_completed_func, on_completed_data); xferData->transferType = LINK; if (g_list_length (srcGFileGList) == 1 && destFileName != nullptr) { destGFile = gnome_cmd_dir_get_child_gfile (destGnomeCmdDir, destFileName); xferData->destGFileList = g_list_append (xferData->destGFileList, destGFile); } else { for (auto srcGFileGListItem = srcGFileGList; srcGFileGListItem; srcGFileGListItem = srcGFileGListItem->next) { srcGFile = (GFile *) srcGFileGListItem->data; auto basename = g_file_get_basename(srcGFile); destGFile = gnome_cmd_dir_get_child_gfile (destGnomeCmdDir, basename); g_free (basename); xferData->destGFileList = g_list_append (xferData->destGFileList, destGFile); } } g_free (destFileName); set_files_total(xferData); gnome_cmd_transfer_gfiles(xferData); finish_xfer(xferData, FALSE); } void gnome_cmd_tmp_download (GList *srcGFileList, GList *destGFileList, GFileCopyFlags copyFlags, GtkSignalFunc on_completed_func, gpointer on_completed_data) { g_return_if_fail (srcGFileList != nullptr && srcGFileList->data != nullptr); g_return_if_fail (destGFileList != nullptr && destGFileList->data != nullptr); auto xferData = create_xfer_data (copyFlags, srcGFileList, destGFileList, nullptr, nullptr, nullptr, GNOME_CMD_CONFIRM_OVERWRITE_QUERY, (GFunc) on_completed_func, on_completed_data); xferData->transferType = COPY; xferData->cancellable = g_cancellable_new(); g_mutex_init(&xferData->mutex); xferData->win = GNOME_CMD_XFER_PROGRESS_WIN (gnome_cmd_xfer_progress_win_new (g_list_length (xferData->srcGFileList))); gtk_window_set_title (GTK_WINDOW (xferData->win), _("downloading to /tmp")); gtk_widget_ref (GTK_WIDGET (xferData->win)); gtk_widget_show (GTK_WIDGET (xferData->win)); xferData->thread = g_thread_new (NULL, (GThreadFunc) gnome_cmd_transfer_gfiles, xferData); g_timeout_add (gnome_cmd_data.gui_update_rate, (GSourceFunc) update_tmp_download_gui, xferData); } void gnome_cmd_transfer_gfiles (XferData *xferData) { g_return_if_fail (xferData->srcGFileList != nullptr); g_return_if_fail (xferData->destGFileList != nullptr); if (g_list_length(xferData->srcGFileList) != g_list_length(xferData->destGFileList)) return; GError *error = nullptr; auto gFileDestListItem = xferData->destGFileList; for (auto gFileSrcListItem = xferData->srcGFileList; gFileSrcListItem; gFileSrcListItem = gFileSrcListItem->next, gFileDestListItem = gFileDestListItem->next) { auto srcGFile = (GFile *) gFileSrcListItem->data; auto destGFile = (GFile *) gFileDestListItem->data; if (xferData->aborted) break; switch (xferData->transferType) { case COPY: gnome_cmd_copy_gfile_recursive(srcGFile, destGFile, xferData->copyFlags, xferData); break; case MOVE: gnome_cmd_move_gfile_recursive(srcGFile, destGFile, xferData->copyFlags, xferData); break; case LINK: auto symlinkSrcPath = g_file_get_path(srcGFile); g_file_make_symbolic_link (destGFile, symlinkSrcPath, nullptr, &error); g_free(symlinkSrcPath); if (error) { gchar *msg = g_strdup_printf (_("Error while creating symlink “%s”\n\n%s"), g_file_peek_path(destGFile), error->message); run_simple_dialog ( *main_win, TRUE, GTK_MESSAGE_ERROR, msg, _("Symlink creation problem"), -1, _("Ignore"), NULL); g_free(msg); g_error_free(error); error = nullptr; } break; } } xferData->done = TRUE; } static void update_transferred_data (goffset currentNumBytes, goffset totalNumBytes, gpointer xferDataPointer) { auto xferData = (XferData *) xferDataPointer; xferData->bytesCopiedFile = currentNumBytes; xferData->fileSize = totalNumBytes; } /** * This function forces the thread for copying files and folders to wait until * the user clicked something on the popup gui which makes problem_action becomes != -1. */ static void xfer_progress_update (XferData *xferData) { g_mutex_lock (&xferData->mutex); if (xferData->error) { xferData->problem = TRUE; g_mutex_unlock (&xferData->mutex); while (xferData->problem_action == -1) g_thread_yield (); g_mutex_lock (&xferData->mutex); g_object_unref(xferData->problemSrcGFile); g_object_unref(xferData->problemDestGFile); g_clear_error (&(xferData->error)); } g_mutex_unlock (&xferData->mutex); } static void do_the_copy(GFile *srcGFile, GFile *destGFile, GFileCopyFlags copyFlags, gpointer xferDataPointer) { g_return_if_fail(xferDataPointer != nullptr); g_return_if_fail(srcGFile != nullptr); g_return_if_fail(destGFile != nullptr); GError *tmpError = nullptr; auto xferData = (XferData *) xferDataPointer; g_free(xferData->curSrcFileName); xferData->curSrcFileName = get_gfile_attribute_string(srcGFile, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); if(!g_file_copy(srcGFile, destGFile, copyFlags, xferData->cancellable, update_transferred_data, xferDataPointer, &tmpError)) { //ToDo: Remove partially copied file ? if (g_error_matches (tmpError, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free(tmpError); } else { g_propagate_error(&(xferData->error), tmpError); xferData->problemSrcGFile = g_file_dup(srcGFile); xferData->problemDestGFile = g_file_dup(destGFile); xfer_progress_update(xferData); } } else { xferData->bytesCopiedFile = 0; xferData->curFileNumber++; xferData->bytesTotalTransferred += xferData->fileSize; if (xferData->problem_action == COPY_ERROR_ACTION_RETRY) xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; } } static GFile* get_new_dest_gfile(GFile *srcGFile, GFile *destGFile, const char *copyString, guint increment) { auto destGFileParent = g_file_get_parent(destGFile); auto srcBasename = g_file_get_basename(srcGFile); auto newDestGFilePath = g_strdup_printf("%s%s%s (%s %d)", g_file_peek_path(destGFileParent), G_DIR_SEPARATOR_S, srcBasename, copyString, increment); auto newDestGFile = g_file_new_for_path(newDestGFilePath); g_free(newDestGFilePath); g_free(srcBasename); g_object_unref(destGFileParent); return newDestGFile; } static void set_new_nonexisting_dest_gfile(GFile *srcGFile, GFile **destGFile, XferData *xferData) { // Translators: Translate 'Copy' as a noun here auto copyString = C_("Filename suffix", "Copy"); guint increment = 1; auto newDestGFile = get_new_dest_gfile(srcGFile, *destGFile, copyString, increment); while (g_file_query_exists(newDestGFile, nullptr)) { g_object_unref(newDestGFile); increment++; newDestGFile = get_new_dest_gfile(srcGFile, *destGFile, copyString, increment); } // If destGFile is an item of the user-provided list of items // to be transferred, we have to update this list-item also auto oldDestItem = g_list_find(xferData->destGFileList, *destGFile); if (oldDestItem) { oldDestItem->data = newDestGFile; } g_object_unref(*destGFile); *destGFile = newDestGFile; } /** * This function moves directories and files from srcGFile to destGFile. * ... */ gboolean gnome_cmd_move_gfile_recursive (GFile *srcGFile, GFile *destGFile, GFileCopyFlags copyFlags, gpointer xferDataPointer) { auto xferData = (XferData *) xferDataPointer; if (xferData->error || xferData->aborted) return false; g_return_val_if_fail(G_IS_FILE(srcGFile), false); g_return_val_if_fail(G_IS_FILE(destGFile), false); GError *tmpError = nullptr; g_free(xferData->curSrcFileName); xferData->curSrcFileName = get_gfile_attribute_string(srcGFile, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); xferData->currentSrcFileType = g_file_query_file_type(srcGFile, G_FILE_QUERY_INFO_NONE, nullptr); if(!g_file_move(srcGFile, destGFile, copyFlags, nullptr, update_transferred_data, xferDataPointer, &tmpError)) { if (g_error_matches (tmpError, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free(tmpError); return false; } if(g_file_query_file_type(srcGFile, G_FILE_QUERY_INFO_NONE, nullptr) == G_FILE_TYPE_DIRECTORY && g_error_matches(tmpError, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE)) { g_error_free(tmpError); tmpError = nullptr; if (gnome_cmd_copy_gfile_recursive(srcGFile, destGFile, xferData->copyFlags, xferData)) { // Folder was copied, now delete the source for completing the move operation auto gFileInfo = g_file_query_info(srcGFile, "*", G_FILE_QUERY_INFO_NONE, nullptr, &tmpError); if (tmpError) { auto srcFileName = g_file_get_basename(srcGFile); auto msg = g_strdup_printf(_("Source “%s” could not be deleted. Aborting!\n\n%s"), srcFileName, tmpError->message); run_simple_dialog ( *main_win, TRUE, GTK_MESSAGE_ERROR, msg, _("Transfer problem"), -1, _("Abort"), NULL); g_free (srcFileName); g_error_free(tmpError); return false; } auto srcGFileParent = g_file_get_parent(srcGFile); if (!srcGFileParent) { auto srcFileName = g_file_get_basename(srcGFile); auto msg = g_strdup_printf(_("Source “%s” could not be deleted. Aborting!\n\n%s"), srcFileName, tmpError->message); run_simple_dialog ( *main_win, TRUE, GTK_MESSAGE_ERROR, msg, _("Transfer problem"), -1, _("Abort"), NULL); g_free (srcFileName); g_error_free(tmpError); g_object_unref(gFileInfo); return false; } GnomeCmdDir *gnomeCmdDirParent = nullptr; GnomeCmdCon *gnomeCmdCon = nullptr; auto srcUriSchema = g_file_get_uri_scheme(srcGFileParent); if (strcmp(srcUriSchema, "file") && (strcmp(srcUriSchema, "smb"))) { gnomeCmdCon = get_remote_con_for_gfile(srcGFileParent); if (gnomeCmdCon) { auto gFileParentUri = g_file_get_uri(srcGFileParent); auto gUriParent = g_uri_parse(gFileParentUri, G_URI_FLAGS_NONE, nullptr); auto gFileParentPathFromUri = g_uri_get_path(gUriParent); gnomeCmdDirParent = gnome_cmd_dir_new (gnomeCmdCon, new GnomeCmdPlainPath(gFileParentPathFromUri)); g_free(gFileParentUri); } } else if (strcmp(srcUriSchema, "file")) { auto gFileParentPath = g_file_get_path(srcGFileParent); gnomeCmdCon = get_home_con(); gnomeCmdDirParent = gnome_cmd_dir_new (gnomeCmdCon, new GnomeCmdPlainPath(gFileParentPath)); g_free(gFileParentPath); } auto gnomeCmdDir = gnome_cmd_dir_new_from_gfileinfo(gFileInfo, gnomeCmdDirParent); auto deleteData = g_new0 (DeleteData, 1); deleteData->gnomeCmdFiles = g_list_append(nullptr, gnomeCmdDir); deleteData->originAction = DeleteData::OriginAction::MOVE; do_delete (deleteData, false); // false -> do not show progress window g_object_unref(srcGFileParent); g_free(srcUriSchema); } else { if (!xferData->aborted) { auto srcFileName = g_file_get_basename(srcGFile); auto msg = g_strdup_printf(_("Source “%s” could not be copied. Aborting!"), srcFileName); run_simple_dialog ( *main_win, TRUE, GTK_MESSAGE_ERROR, msg, _("Transfer problem"), -1, _("Abort"), NULL); g_free (srcFileName); g_error_free(tmpError); } return false; } } else { // If G_FILE_COPY_OVERWRITE is not specified (i.e. when not 'Overwrite silently' is selected) // and the target exists and is a file, then the error G_IO_ERROR_EXISTS is returned. if(g_error_matches(tmpError, G_IO_ERROR, G_IO_ERROR_EXISTS)) { g_propagate_error(&(xferData->error), tmpError); tmpError = nullptr; xferData->problemSrcGFile = g_file_dup(srcGFile); xferData->problemDestGFile = g_file_dup(destGFile); xfer_progress_update(xferData); guint copyFlagsTemp = copyFlags; switch (xferData->problem_action) { case COPY_ERROR_ACTION_RETRY: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; return gnome_cmd_move_gfile_recursive(srcGFile, destGFile, copyFlags, xferData); case COPY_ERROR_ACTION_REPLACE: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; copyFlagsTemp = copyFlags | G_FILE_COPY_OVERWRITE; gnome_cmd_move_gfile_recursive(srcGFile, destGFile, (GFileCopyFlags) copyFlagsTemp, xferData); break; case COPY_ERROR_ACTION_REPLACE_ALL: copyFlagsTemp = copyFlags | G_FILE_COPY_OVERWRITE; gnome_cmd_move_gfile_recursive(srcGFile, destGFile, (GFileCopyFlags) copyFlagsTemp, xferData); break; case COPY_ERROR_ACTION_RENAME: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; set_new_nonexisting_dest_gfile(srcGFile, &destGFile, xferData); if(g_file_query_file_type(srcGFile, G_FILE_QUERY_INFO_NONE, nullptr) == G_FILE_TYPE_DIRECTORY) gnome_cmd_move_gfile_recursive(srcGFile, destGFile, (GFileCopyFlags) copyFlagsTemp, xferData); else g_file_move(srcGFile, destGFile, copyFlags, xferData->cancellable, update_transferred_data, xferDataPointer, &tmpError); if (tmpError) { if (g_error_matches (tmpError, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_error_free(tmpError); else { g_warning("g_file_move error: %s\n", tmpError->message); g_propagate_error(&(xferData->error), tmpError); xferData->problemSrcGFile = g_file_dup(srcGFile); xferData->problemDestGFile = g_file_dup(destGFile); xfer_progress_update(xferData); } return false; } break; case COPY_ERROR_ACTION_RENAME_ALL: set_new_nonexisting_dest_gfile(srcGFile, &destGFile, xferData); if(g_file_query_file_type(srcGFile, G_FILE_QUERY_INFO_NONE, nullptr) == G_FILE_TYPE_DIRECTORY) gnome_cmd_move_gfile_recursive(srcGFile, destGFile, (GFileCopyFlags) copyFlagsTemp, xferData); else g_file_move(srcGFile, destGFile, copyFlags, xferData->cancellable, update_transferred_data, xferDataPointer, &tmpError); if (tmpError) { if (g_error_matches (tmpError, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_error_free(tmpError); else { g_warning("g_file_move error: %s\n", tmpError->message); g_propagate_error(&(xferData->error), tmpError); xferData->problemSrcGFile = g_file_dup(srcGFile); xferData->problemDestGFile = g_file_dup(destGFile); xfer_progress_update(xferData); } return false; } break; case COPY_ERROR_ACTION_SKIP: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; break; case COPY_ERROR_ACTION_SKIP_ALL: return true; // Copy into can only be selected in case of copying a directory into an // already exising directory, so we are ignoring this here because we deal with // moving files in this if branch. case COPY_ERROR_ACTION_COPY_INTO: case COPY_ERROR_ACTION_ABORT: return false; case COPY_ERROR_ACTION_NO_ACTION_YET: default: break; } } else { g_propagate_error(&(xferData->error), tmpError); xferData->problemSrcGFile = g_file_dup(srcGFile); xferData->problemDestGFile = g_file_dup(destGFile); xfer_progress_update(xferData); } } } else { xferData->bytesCopiedFile = 0; xferData->curFileNumber++; xferData->bytesTotalTransferred += xferData->fileSize; if (xferData->problem_action == COPY_ERROR_ACTION_RETRY) xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; } if(xferData->problem_action == COPY_ERROR_ACTION_RETRY) { xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; gnome_cmd_move_gfile_recursive(srcGFile, destGFile, copyFlags, xferData); } return true; } /** * This function recursively copies directories and files from srcGFile to destGFile. * As the function can call itself multiple times, some logic is build into it to handle the * case when the destination is already existing and the user decides up on that. * * The function does return when it copied a regular file or when something went wrong * unexpectedly. It calls itself if a directory should be copied or if a rename should happen * as the original destination exists already. */ gboolean gnome_cmd_copy_gfile_recursive (GFile *srcGFile, GFile *destGFile, GFileCopyFlags copyFlags, gpointer xferDataPointer) { auto xferData = (XferData *) xferDataPointer; if (xferData->error || xferData->aborted) return false; g_return_val_if_fail(G_IS_FILE(srcGFile), false); g_return_val_if_fail(G_IS_FILE(destGFile), false); GError *tmpError = nullptr; switch (g_file_query_file_type(srcGFile, G_FILE_QUERY_INFO_NONE, nullptr)) { case G_FILE_TYPE_DIRECTORY: { xferData->currentSrcFileType = G_FILE_TYPE_DIRECTORY; switch (xferData->problem_action) { case COPY_ERROR_ACTION_NO_ACTION_YET: // Try to create the dest directory if (!g_file_make_directory (destGFile, nullptr, &tmpError)) { g_warning("g_file_make_directory error: %s\n", tmpError->message); g_propagate_error(&(xferData->error), tmpError); xferData->problemSrcGFile = g_file_dup(srcGFile); xferData->problemDestGFile = g_file_dup(destGFile); xfer_progress_update(xferData); return gnome_cmd_copy_gfile_recursive(srcGFile, destGFile, copyFlags, xferData); } break; case COPY_ERROR_ACTION_RENAME: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; set_new_nonexisting_dest_gfile(srcGFile, &destGFile, xferData); if (!g_file_make_directory (destGFile, nullptr, &tmpError)) { g_warning("g_file_make_directory error: %s\n", tmpError->message); g_propagate_error(&(xferData->error), tmpError); xferData->problemSrcGFile = g_file_dup(srcGFile); xferData->problemDestGFile = g_file_dup(destGFile); xfer_progress_update(xferData); return false; } break; case COPY_ERROR_ACTION_COPY_INTO: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; break; case COPY_ERROR_ACTION_RENAME_ALL: set_new_nonexisting_dest_gfile(srcGFile, &destGFile, xferData); if (!g_file_make_directory (destGFile, nullptr, &tmpError)) { g_warning("g_file_make_directory error: %s\n", tmpError->message); g_propagate_error(&(xferData->error), tmpError); xferData->problemSrcGFile = g_file_dup(srcGFile); xferData->problemDestGFile = g_file_dup(destGFile); xfer_progress_update(xferData); return false; } break; case COPY_ERROR_ACTION_SKIP: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; return true; case COPY_ERROR_ACTION_SKIP_ALL: return true; case COPY_ERROR_ACTION_RETRY: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; return gnome_cmd_copy_gfile_recursive(srcGFile, destGFile, copyFlags, xferData); case COPY_ERROR_ACTION_ABORT: return false; case COPY_ERROR_ACTION_REPLACE: // This is not handled for directories when copying case COPY_ERROR_ACTION_REPLACE_ALL: // This is not handled for directories when copying default: return false; } // After processing the user response above, we are looping through the directory now. auto enumerator = g_file_enumerate_children(srcGFile, "*", G_FILE_QUERY_INFO_NONE, NULL, &tmpError); if (tmpError) { g_warning("g_file_enumerate_children error: %s\n", tmpError->message); g_propagate_error(&(xferData->error), tmpError); xferData->problemSrcGFile = g_file_dup(srcGFile); xferData->problemDestGFile = g_file_dup(destGFile); xfer_progress_update(xferData); return false; } auto gFileInfoChildFile = g_file_enumerator_next_file(enumerator, nullptr, &tmpError); while(gFileInfoChildFile != nullptr && !tmpError) { if (xferData->aborted) { g_object_unref(gFileInfoChildFile); g_file_enumerator_close (enumerator, nullptr, nullptr); return false; } if(g_file_info_get_file_type(gFileInfoChildFile) == G_FILE_TYPE_DIRECTORY) { auto dirNameChildGFile = g_file_info_get_name(gFileInfoChildFile); auto sourceChildGFile = g_file_get_child(g_file_enumerator_get_container(enumerator), dirNameChildGFile); auto targetPath = g_strdup_printf("%s%s%s", g_file_peek_path(destGFile), G_DIR_SEPARATOR_S, dirNameChildGFile); auto targetGFile = g_file_new_for_path(targetPath); gnome_cmd_copy_gfile_recursive(sourceChildGFile, targetGFile, copyFlags, xferData); g_object_unref(sourceChildGFile); g_object_unref(targetGFile); g_free(targetPath); } else if(g_file_info_get_file_type(gFileInfoChildFile) == G_FILE_TYPE_REGULAR) { xferData->currentSrcFileType = G_FILE_TYPE_REGULAR; auto fileNameChildGFile = g_file_info_get_name(gFileInfoChildFile); auto srcChildGFile = g_file_get_child(g_file_enumerator_get_container(enumerator), fileNameChildGFile); auto targetPath = g_strdup_printf("%s%s%s", g_file_peek_path(destGFile), G_DIR_SEPARATOR_S, fileNameChildGFile); auto targetGFile = g_file_new_for_path(targetPath); do_the_copy(srcChildGFile, targetGFile, copyFlags, xferDataPointer); guint copyFlagsTemp = copyFlags; switch (xferData->problem_action) { case COPY_ERROR_ACTION_RETRY: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; return gnome_cmd_copy_gfile_recursive(srcChildGFile, targetGFile, copyFlags, xferData); case COPY_ERROR_ACTION_REPLACE: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; copyFlagsTemp = copyFlags | G_FILE_COPY_OVERWRITE; gnome_cmd_copy_gfile_recursive(srcChildGFile, targetGFile, (GFileCopyFlags) copyFlagsTemp, xferData); break; case COPY_ERROR_ACTION_RENAME: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; set_new_nonexisting_dest_gfile(srcChildGFile, &targetGFile, xferData); gnome_cmd_copy_gfile_recursive(srcChildGFile, targetGFile, copyFlags, xferData); break; case COPY_ERROR_ACTION_SKIP: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; break; case COPY_ERROR_ACTION_REPLACE_ALL: copyFlagsTemp = copyFlags | G_FILE_COPY_OVERWRITE; gnome_cmd_copy_gfile_recursive(srcChildGFile, targetGFile, (GFileCopyFlags) copyFlagsTemp, xferData); break; case COPY_ERROR_ACTION_SKIP_ALL: g_object_unref(srcChildGFile); g_object_unref(targetGFile); g_free(targetPath); g_object_unref(gFileInfoChildFile); g_file_enumerator_close (enumerator, nullptr, nullptr); return true; case COPY_ERROR_ACTION_RENAME_ALL: set_new_nonexisting_dest_gfile(srcChildGFile, &targetGFile, xferData); gnome_cmd_copy_gfile_recursive(srcChildGFile, targetGFile, copyFlags, xferData); break; case COPY_ERROR_ACTION_ABORT: case COPY_ERROR_ACTION_COPY_INTO: g_object_unref(srcChildGFile); g_object_unref(targetGFile); g_free(targetPath); g_object_unref(gFileInfoChildFile); g_file_enumerator_close (enumerator, nullptr, nullptr); return false; case COPY_ERROR_ACTION_NO_ACTION_YET: default: break; } g_object_unref(srcChildGFile); g_object_unref(targetGFile); g_free(targetPath); } g_object_unref(gFileInfoChildFile); gFileInfoChildFile = g_file_enumerator_next_file(enumerator, NULL, &tmpError); } if (tmpError) { printf("g_file_enumerator_next_file: %s\n", tmpError->message); g_propagate_error(&(xferData->error), tmpError); xfer_progress_update(xferData); } g_file_enumerator_close (enumerator, nullptr, nullptr); break; } case G_FILE_TYPE_REGULAR: { xferData->currentSrcFileType = G_FILE_TYPE_REGULAR; do_the_copy(srcGFile, destGFile, copyFlags, xferDataPointer); switch (xferData->problem_action) { case COPY_ERROR_ACTION_RETRY: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; return gnome_cmd_copy_gfile_recursive(srcGFile, destGFile, copyFlags, xferData); case COPY_ERROR_ACTION_REPLACE: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; gnome_cmd_copy_gfile_recursive(srcGFile, destGFile, G_FILE_COPY_OVERWRITE, xferData); break; case COPY_ERROR_ACTION_RENAME: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; set_new_nonexisting_dest_gfile(srcGFile, &destGFile, xferData); do_the_copy(srcGFile, destGFile, copyFlags, xferDataPointer); break; case COPY_ERROR_ACTION_SKIP: xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; break; case COPY_ERROR_ACTION_REPLACE_ALL: if (copyFlags != G_FILE_COPY_OVERWRITE) gnome_cmd_copy_gfile_recursive(srcGFile, destGFile, G_FILE_COPY_OVERWRITE, xferData); break; case COPY_ERROR_ACTION_SKIP_ALL: return true; case COPY_ERROR_ACTION_RENAME_ALL: set_new_nonexisting_dest_gfile(srcGFile, &destGFile, xferData); do_the_copy(srcGFile, destGFile, copyFlags, xferDataPointer); break; case COPY_ERROR_ACTION_COPY_INTO: // This is not handled for files when copying case COPY_ERROR_ACTION_ABORT: return false; case COPY_ERROR_ACTION_NO_ACTION_YET: default: break; } } case G_FILE_TYPE_MOUNTABLE: case G_FILE_TYPE_UNKNOWN: case G_FILE_TYPE_SHORTCUT: case G_FILE_TYPE_SPECIAL: case G_FILE_TYPE_SYMBOLIC_LINK: default: break; } if(xferData->problem_action == COPY_ERROR_ACTION_RETRY) { xferData->problem_action = COPY_ERROR_ACTION_NO_ACTION_YET; gnome_cmd_copy_gfile_recursive(srcGFile, destGFile, copyFlags, xferData); } return true; } void gnome_cmd_copy_start (GList *srcGnomeCmdFileGList, GnomeCmdDir *toGnomeCmdDir, GnomeCmdFileList *srcGnomeCmdFileList, gchar *destFileName, GFileCopyFlags copyFlags, GnomeCmdConfirmOverwriteMode overwriteMode, GtkSignalFunc on_completed_func, gpointer on_completed_data) { g_return_if_fail (srcGnomeCmdFileGList != nullptr); g_return_if_fail (GNOME_CMD_IS_DIR (toGnomeCmdDir)); GList *srcGFileList = gnome_cmd_file_list_to_gfile_list (srcGnomeCmdFileGList); gnome_cmd_copy_gfiles_start (srcGFileList, toGnomeCmdDir, srcGnomeCmdFileList, srcGnomeCmdFileGList, destFileName, copyFlags, overwriteMode, on_completed_func, on_completed_data); } void gnome_cmd_move_start (GList *srcGnomeCmdFileGList, GnomeCmdDir *toGnomeCmdDir, GnomeCmdFileList *srcGnomeCmdFileList, gchar *destFileName, GFileCopyFlags copyFlags, GnomeCmdConfirmOverwriteMode overwriteMode, GtkSignalFunc on_completed_func, gpointer on_completed_data) { g_return_if_fail (srcGnomeCmdFileGList != nullptr); g_return_if_fail (GNOME_CMD_IS_DIR (toGnomeCmdDir)); GList *srcGFileList = gnome_cmd_file_list_to_gfile_list (srcGnomeCmdFileGList); gnome_cmd_move_gfiles_start (srcGFileList, toGnomeCmdDir, srcGnomeCmdFileList, srcGnomeCmdFileGList, destFileName, copyFlags, overwriteMode, on_completed_func, on_completed_data); }