From 34cbab7c2a3977029fcc956756a43169920f1ba4 Mon Sep 17 00:00:00 2001 From: Andrew Borodin Date: Sun, 20 Aug 2017 18:35:08 +0300 Subject: [PATCH] Implement cancel of chmod of many files. What steps will reproduce the problem? 1. Selected many files (12000 in my case). 2. Ctrl+x c (chmod) - changed mode to desired one. 3. MC started to change file modes but failed, it shows error popup "Cannot chmod ... Operation not permitted (1)" for every file and waits for a keypress. MC shows error for every file in a list (12000 times), there is no chance to cancel the whole operation, only to press any key 12000 times or to kill MC. The solution: implement error message with follow buttons: "Ignore" -- ignore current error; "Ignore all" -- ignore current error, try chmod of other selected files, and ignore all following errors; "Retry" -- retry chmod operation; "Cancel" -- cancel chmod operation immediately. Signed-off-by: Andrew Borodin --- src/filemanager/chmod.c | 97 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 13 deletions(-) diff --git a/src/filemanager/chmod.c b/src/filemanager/chmod.c index 9fe3725c9..997d79feb 100644 --- a/src/filemanager/chmod.c +++ b/src/filemanager/chmod.c @@ -120,6 +120,7 @@ static struct static gboolean mode_change; static int current_file; +static gboolean ignore_all; static mode_t and_mask, or_mask, ch_mode; @@ -393,21 +394,66 @@ next_file (void) /* --------------------------------------------------------------------------------------------- */ -static void +static gboolean +try_chmod (const vfs_path_t * p, mode_t m) +{ + while (mc_chmod (p, m) == -1 && !ignore_all) + { + int my_errno = errno; + int result; + char *msg; + + msg = + g_strdup_printf (_("Cannot chmod \"%s\"\n%s"), x_basename (vfs_path_as_str (p)), + unix_error_string (my_errno)); + result = + query_dialog (MSG_ERROR, msg, D_ERROR, 4, _("&Ignore"), _("Ignore &all"), _("&Retry"), + _("&Cancel")); + g_free (msg); + + switch (result) + { + case 0: + /* try next file */ + return TRUE; + + case 1: + ignore_all = TRUE; + /* try next file */ + return TRUE; + + case 2: + /* retry this file */ + break; + + case 3: + default: + /* stop remain files processing */ + return FALSE; + } + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean do_chmod (struct stat *sf) { + gboolean ret; vfs_path_t *vpath; sf->st_mode &= and_mask; sf->st_mode |= or_mask; vpath = vfs_path_from_str (current_panel->dir.list[current_file].fname); - if (mc_chmod (vpath, sf->st_mode) == -1) - message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"), - current_panel->dir.list[current_file].fname, unix_error_string (errno)); - + ret = try_chmod (vpath, sf->st_mode); vfs_path_free (vpath); + do_file_mark (current_panel, current_file, 0); + + return ret; } /* --------------------------------------------------------------------------------------------- */ @@ -415,7 +461,8 @@ do_chmod (struct stat *sf) static void apply_mask (struct stat *sf) { - do_chmod (sf); + if (!do_chmod (sf)) + return; do { @@ -427,12 +474,20 @@ apply_mask (struct stat *sf) vpath = vfs_path_from_str (fname); ok = (mc_stat (vpath, sf) == 0); vfs_path_free (vpath); - if (!ok) - return; - ch_mode = sf->st_mode; + if (!ok) + { + /* if current file was deleted outside mc -- try next file */ + /* decrease current_panel->marked */ + do_file_mark (current_panel, current_file, 0); + } + else + { + ch_mode = sf->st_mode; - do_chmod (sf); + if (!do_chmod (sf)) + return; + } } while (current_panel->marked != 0); } @@ -450,6 +505,7 @@ chmod_cmd (void) chmod_i18n (); current_file = 0; + ignore_all = FALSE; do { /* do while any files remaining */ @@ -490,9 +546,24 @@ chmod_cmd (void) break; case B_ENTER: - if (mode_change && mc_chmod (vpath, ch_mode) == -1) - message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"), - fname, unix_error_string (errno)); + if (mode_change) + { + if (current_panel->marked <= 1) + { + /* single or last file */ + if (mc_chmod (vpath, ch_mode) == -1 && !ignore_all) + message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"), fname, + unix_error_string (errno)); + end_chmod = TRUE; + } + else if (!try_chmod (vpath, ch_mode)) + { + /* stop multiple files processing */ + result = B_CANCEL; + end_chmod = TRUE; + } + } + need_update = TRUE; break; -- 2.11.4.GIT