/* Chown-advanced command -- for the Midnight Commander Copyright (C) 1994-2023 Free Software Foundation, Inc. This file is part of the Midnight Commander. The Midnight Commander 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 3 of the License, or (at your option) any later version. The Midnight Commander 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. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** \file achown.c * \brief Source: Contains functions for advanced chowning */ #include #include #include #include #include #include #include #include #include #include "lib/global.h" #include "lib/tty/tty.h" #include "lib/tty/key.h" /* XCTRL and ALT macros */ #include "lib/skin.h" #include "lib/vfs/vfs.h" #include "lib/strutil.h" #include "lib/util.h" #include "lib/widget.h" #include "cmd.h" /* advanced_chown_cmd() */ /*** global variables ****************************************************************************/ /*** file scope macro definitions ****************************************************************/ #define BX 5 #define BY 5 #define BUTTONS 9 #define BUTTONS_PERM 5 #define B_SETALL B_USER #define B_SKIP (B_USER + 1) /*** file scope type declarations ****************************************************************/ /*** forward declarations (file scope functions) *************************************************/ /*** file scope variables ************************************************************************/ static struct { unsigned long id; int ret_cmd; button_flags_t flags; int x; int len; const char *text; } advanced_chown_but[BUTTONS] = { /* *INDENT-OFF* */ { 0, B_ENTER, NARROW_BUTTON, 3, 0, " " }, { 0, B_ENTER, NARROW_BUTTON, 11, 0, " " }, { 0, B_ENTER, NARROW_BUTTON, 19, 0, " " }, { 0, B_ENTER, NARROW_BUTTON, 29, 0, "" }, { 0, B_ENTER, NARROW_BUTTON, 47, 0, "" }, { 0, B_SETALL, NORMAL_BUTTON, 0, 0, N_("Set &all") }, { 0, B_SKIP, NORMAL_BUTTON, 0, 0, N_("S&kip") }, { 0, B_ENTER, DEFPUSH_BUTTON, 0, 0, N_("&Set") }, { 0, B_CANCEL, NORMAL_BUTTON, 0, 0, N_("&Cancel") } /* *INDENT-ON* */ }; static int current_file; static gboolean ignore_all; static WButton *b_att[3]; /* permission */ static WButton *b_user, *b_group; /* owner */ static WLabel *l_filename; static WLabel *l_mode; static int flag_pos; static int x_toggle; static char ch_flags[11]; static const char ch_perm[] = "rwx"; static mode_t ch_cmode; static struct stat sf_stat; /* --------------------------------------------------------------------------------------------- */ /*** file scope functions ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ static void advanced_chown_init (void) { static gboolean i18n = FALSE; int i; if (i18n) return; i18n = TRUE; for (i = BUTTONS_PERM; i < BUTTONS; i++) { #ifdef ENABLE_NLS advanced_chown_but[i].text = _(advanced_chown_but[i].text); #endif /* ENABLE_NLS */ advanced_chown_but[i].len = str_term_width1 (advanced_chown_but[i].text) + 3; if (advanced_chown_but[i].flags == DEFPUSH_BUTTON) advanced_chown_but[i].len += 2; /* "<>" */ } } /* --------------------------------------------------------------------------------------------- */ static cb_ret_t inc_flag_pos (void) { if (flag_pos == 10) { flag_pos = 0; return MSG_NOT_HANDLED; } flag_pos++; return flag_pos % 3 == 0 ? MSG_NOT_HANDLED : MSG_HANDLED; } /* --------------------------------------------------------------------------------------------- */ static cb_ret_t dec_flag_pos (void) { if (flag_pos == 0) { flag_pos = 10; return MSG_NOT_HANDLED; } flag_pos--; return (flag_pos + 1) % 3 == 0 ? MSG_NOT_HANDLED : MSG_HANDLED; } /* --------------------------------------------------------------------------------------------- */ static void set_perm_by_flags (char *s, int f_p) { int i; for (i = 0; i < 3; i++) { if (ch_flags[f_p + i] == '+') s[i] = ch_perm[i]; else if (ch_flags[f_p + i] == '-') s[i] = '-'; else s[i] = (ch_cmode & (1 << (8 - f_p - i))) != 0 ? ch_perm[i] : '-'; } } /* --------------------------------------------------------------------------------------------- */ static mode_t get_perm (char *s, int base) { mode_t m = 0; m |= (s[0] == '-') ? 0 : ((s[0] == '+') ? (mode_t) (1 << (base + 2)) : (1 << (base + 2)) & ch_cmode); m |= (s[1] == '-') ? 0 : ((s[1] == '+') ? (mode_t) (1 << (base + 1)) : (1 << (base + 1)) & ch_cmode); m |= (s[2] == '-') ? 0 : ((s[2] == '+') ? (mode_t) (1 << base) : (1 << base) & ch_cmode); return m; } /* --------------------------------------------------------------------------------------------- */ static mode_t get_mode (void) { mode_t m; m = ch_cmode ^ (ch_cmode & 0777); m |= get_perm (ch_flags, 6); m |= get_perm (ch_flags + 3, 3); m |= get_perm (ch_flags + 6, 0); return m; } /* --------------------------------------------------------------------------------------------- */ static void update_permissions (void) { set_perm_by_flags (b_att[0]->text.start, 0); set_perm_by_flags (b_att[1]->text.start, 3); set_perm_by_flags (b_att[2]->text.start, 6); } /* --------------------------------------------------------------------------------------------- */ static void update_ownership (void) { button_set_text (b_user, get_owner (sf_stat.st_uid)); button_set_text (b_group, get_group (sf_stat.st_gid)); } /* --------------------------------------------------------------------------------------------- */ static void print_flags (const WDialog * h) { int i; tty_setcolor (COLOR_NORMAL); for (i = 0; i < 3; i++) { widget_gotoyx (h, BY + 1, advanced_chown_but[0].x + 6 + i); tty_print_char (ch_flags[i]); } for (i = 0; i < 3; i++) { widget_gotoyx (h, BY + 1, advanced_chown_but[1].x + 6 + i); tty_print_char (ch_flags[i + 3]); } for (i = 0; i < 3; i++) { widget_gotoyx (h, BY + 1, advanced_chown_but[2].x + 6 + i); tty_print_char (ch_flags[i + 6]); } update_permissions (); for (i = 0; i < 15; i++) { widget_gotoyx (h, BY + 1, advanced_chown_but[3].x + 6 + i); tty_print_char (ch_flags[9]); } for (i = 0; i < 15; i++) { widget_gotoyx (h, BY + 1, advanced_chown_but[4].x + 6 + i); tty_print_char (ch_flags[10]); } } /* --------------------------------------------------------------------------------------------- */ static void advanced_chown_refresh (const WDialog * h) { tty_setcolor (COLOR_NORMAL); widget_gotoyx (h, BY - 1, advanced_chown_but[0].x + 5); tty_print_string (_("owner")); widget_gotoyx (h, BY - 1, advanced_chown_but[1].x + 5); tty_print_string (_("group")); widget_gotoyx (h, BY - 1, advanced_chown_but[2].x + 5); tty_print_string (_("other")); widget_gotoyx (h, BY - 1, advanced_chown_but[3].x + 5); tty_print_string (_("owner")); widget_gotoyx (h, BY - 1, advanced_chown_but[4].x + 5); tty_print_string (_("group")); widget_gotoyx (h, BY + 1, 3); tty_print_string (_("Flag")); print_flags (h); } /* --------------------------------------------------------------------------------------------- */ static void advanced_chown_info_update (void) { /* mode */ label_set_textv (l_mode, _("Permissions (octal): %o"), get_mode ()); /* permissions */ update_permissions (); } /* --------------------------------------------------------------------------------------------- */ static void update_mode (WGroup * g) { print_flags (DIALOG (g)); advanced_chown_info_update (); widget_set_state (WIDGET (g->current->data), WST_FOCUSED, TRUE); } /* --------------------------------------------------------------------------------------------- */ static cb_ret_t perm_button_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) { WButton *b = BUTTON (w); WGroup *g = w->owner; int i = 0; int f_pos; /* one of permission buttons */ if (b == b_att[0]) f_pos = 0; else if (b == b_att[1]) f_pos = 1; else /* if (w == b_att [1] */ f_pos = 2; switch (msg) { case MSG_FOCUS: if (b->hotpos == -1) b->hotpos = 0; flag_pos = f_pos * 3 + b->hotpos; return MSG_HANDLED; case MSG_KEY: switch (parm) { case '*': parm = '='; MC_FALLTHROUGH; case '-': case '=': case '+': flag_pos = f_pos * 3 + b->hotpos; ch_flags[flag_pos] = parm; update_mode (g); send_message (w, NULL, MSG_KEY, KEY_RIGHT, NULL); if (b->hotpos == 2) group_select_next_widget (g); break; case XCTRL ('f'): case KEY_RIGHT: { cb_ret_t ret; ret = inc_flag_pos (); b->hotpos = flag_pos % 3; return ret; } case XCTRL ('b'): case KEY_LEFT: { cb_ret_t ret; ret = dec_flag_pos (); b->hotpos = flag_pos % 3; return ret; } case 'x': i++; MC_FALLTHROUGH; case 'w': i++; MC_FALLTHROUGH; case 'r': b->hotpos = i; MC_FALLTHROUGH; case ' ': i = b->hotpos; flag_pos = f_pos * 3 + i; if (b->text.start[flag_pos % 3] == '-') ch_flags[flag_pos] = '+'; else ch_flags[flag_pos] = '-'; update_mode (w->owner); break; case '4': i++; MC_FALLTHROUGH; case '2': i++; MC_FALLTHROUGH; case '1': b->hotpos = i; flag_pos = f_pos * 3 + i; ch_flags[flag_pos] = '='; update_mode (g); break; default: break; } /* continue key handling in the dialog level */ return MSG_NOT_HANDLED; default: return button_default_callback (w, sender, msg, parm, data); } } /* --------------------------------------------------------------------------------------------- */ static void perm_button_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) { switch (msg) { case MSG_MOUSE_DOWN: /* place cursor on flag that is being modified */ BUTTON (w)->hotpos = CLAMP (event->x - 1, 0, 2); MC_FALLTHROUGH; default: button_mouse_default_callback (w, msg, event); break; } } /* --------------------------------------------------------------------------------------------- */ static WButton * perm_button_new (int y, int x, int action, button_flags_t flags, const char *text, bcback_fn callback) { WButton *b; Widget *w; /* create base button using native API */ b = button_new (y, x, action, flags, text, callback); w = WIDGET (b); /* we don't want HOTKEY */ widget_want_hotkey (w, FALSE); w->callback = perm_button_callback; w->mouse_callback = perm_button_mouse_callback; return b; } /* --------------------------------------------------------------------------------------------- */ static cb_ret_t chl_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) { switch (msg) { case MSG_KEY: switch (parm) { case KEY_LEFT: case KEY_RIGHT: { WDialog *h = DIALOG (w); h->ret_value = parm; dlg_close (h); } break; default: break; } MC_FALLTHROUGH; default: return dlg_default_callback (w, sender, msg, parm, data); } } /* --------------------------------------------------------------------------------------------- */ static int user_group_button_cb (WButton * button, int action) { Widget *w = WIDGET (button); int f_pos; gboolean chl_end; (void) action; if (button == b_user) f_pos = BUTTONS_PERM - 2; else if (button == b_group) f_pos = BUTTONS_PERM - 1; else return 0; /* do nothing */ do { WGroup *g = w->owner; WDialog *h = DIALOG (g); Widget *wh = WIDGET (h); gboolean is_owner = (f_pos == BUTTONS_PERM - 2); const char *title; int lxx, b_current; WDialog *chl_dlg; WListbox *chl_list; int result; int fe; struct passwd *chl_pass; struct group *chl_grp; chl_end = FALSE; if (is_owner) { title = _("owner"); lxx = WIDGET (b_user)->rect.x + 1; } else { title = _("group"); lxx = WIDGET (b_group)->rect.x + 1; } chl_dlg = dlg_create (TRUE, wh->rect.y - 1, lxx, wh->rect.lines + 2, 17, WPOS_KEEP_DEFAULT, TRUE, dialog_colors, chl_callback, NULL, "[Advanced Chown]", title); /* get new listboxes */ chl_list = listbox_new (1, 1, WIDGET (chl_dlg)->rect.lines - 2, WIDGET (chl_dlg)->rect.cols - 2, FALSE, NULL); listbox_add_item (chl_list, LISTBOX_APPEND_AT_END, 0, "", NULL, FALSE); if (is_owner) { /* get and put user names in the listbox */ setpwent (); while ((chl_pass = getpwent ()) != NULL) listbox_add_item (chl_list, LISTBOX_APPEND_SORTED, 0, chl_pass->pw_name, NULL, FALSE); endpwent (); fe = listbox_search_text (chl_list, get_owner (sf_stat.st_uid)); } else { /* get and put group names in the listbox */ setgrent (); while ((chl_grp = getgrent ()) != NULL) listbox_add_item (chl_list, LISTBOX_APPEND_SORTED, 0, chl_grp->gr_name, NULL, FALSE); endgrent (); fe = listbox_search_text (chl_list, get_group (sf_stat.st_gid)); } listbox_set_current (chl_list, fe); b_current = chl_list->current; group_add_widget (GROUP (chl_dlg), chl_list); result = dlg_run (chl_dlg); if (result != B_CANCEL) { if (b_current != chl_list->current) { gboolean ok = FALSE; char *text; listbox_get_current (chl_list, &text, NULL); if (is_owner) { chl_pass = getpwnam (text); if (chl_pass != NULL) { sf_stat.st_uid = chl_pass->pw_uid; ok = TRUE; } } else { chl_grp = getgrnam (text); if (chl_grp != NULL) { sf_stat.st_gid = chl_grp->gr_gid; ok = TRUE; } } if (!ok) group_select_current_widget (g); else { ch_flags[f_pos + 6] = '+'; update_ownership (); group_select_current_widget (g); print_flags (h); } } if (result == KEY_LEFT) { if (!is_owner) chl_end = TRUE; group_select_prev_widget (g); f_pos--; } else if (result == KEY_RIGHT) { if (is_owner) chl_end = TRUE; group_select_next_widget (g); f_pos++; } } /* Here we used to redraw the window */ widget_destroy (WIDGET (chl_dlg)); } while (chl_end); return 0; } /* --------------------------------------------------------------------------------------------- */ static cb_ret_t advanced_chown_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) { switch (msg) { case MSG_DRAW: frame_callback (w, NULL, MSG_DRAW, 0, NULL); advanced_chown_refresh (DIALOG (w->owner)); advanced_chown_info_update (); return MSG_HANDLED; default: return frame_callback (w, sender, msg, parm, data); } } /* --------------------------------------------------------------------------------------------- */ static cb_ret_t advanced_chown_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) { WGroup *g = GROUP (w); int i = 0; switch (msg) { case MSG_KEY: switch (parm) { case ALT ('x'): i++; MC_FALLTHROUGH; case ALT ('w'): i++; MC_FALLTHROUGH; case ALT ('r'): parm = i + 3; for (i = 0; i < 3; i++) ch_flags[i * 3 + parm - 3] = (x_toggle & (1 << parm)) ? '-' : '+'; x_toggle ^= (1 << parm); update_mode (g); widget_draw (w); break; case XCTRL ('x'): i++; MC_FALLTHROUGH; case XCTRL ('w'): i++; MC_FALLTHROUGH; case XCTRL ('r'): parm = i; for (i = 0; i < 3; i++) ch_flags[i * 3 + parm] = (x_toggle & (1 << parm)) ? '-' : '+'; x_toggle ^= (1 << parm); update_mode (g); widget_draw (w); break; default: break; } return MSG_NOT_HANDLED; default: return dlg_default_callback (w, sender, msg, parm, data); } } /* --------------------------------------------------------------------------------------------- */ static WDialog * advanced_chown_dlg_create (WPanel * panel) { gboolean single_set; WDialog *ch_dlg; WGroup *ch_grp; int lines = 12; int cols = 74; int i; int y; memset (ch_flags, '=', 11); flag_pos = 0; x_toggle = 070; single_set = (panel->marked < 2); if (!single_set) lines += 2; ch_dlg = dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, advanced_chown_callback, NULL, "[Advanced Chown]", _("Chown advanced command")); ch_grp = GROUP (ch_dlg); /* draw background */ ch_dlg->bg->callback = advanced_chown_bg_callback; l_filename = label_new (2, 3, NULL); group_add_widget (ch_grp, l_filename); group_add_widget (ch_grp, hline_new (3, -1, -1)); #define XTRACT(i,y,cb) y, BX+advanced_chown_but[i].x, \ advanced_chown_but[i].ret_cmd, advanced_chown_but[i].flags, \ (advanced_chown_but[i].text), cb b_att[0] = perm_button_new (XTRACT (0, BY, NULL)); advanced_chown_but[0].id = group_add_widget (ch_grp, b_att[0]); b_att[1] = perm_button_new (XTRACT (1, BY, NULL)); advanced_chown_but[1].id = group_add_widget (ch_grp, b_att[1]); b_att[2] = perm_button_new (XTRACT (2, BY, NULL)); advanced_chown_but[2].id = group_add_widget (ch_grp, b_att[2]); b_user = button_new (XTRACT (3, BY, user_group_button_cb)); advanced_chown_but[3].id = group_add_widget (ch_grp, b_user); b_group = button_new (XTRACT (4, BY, user_group_button_cb)); advanced_chown_but[4].id = group_add_widget (ch_grp, b_group); l_mode = label_new (BY + 2, 3, NULL); group_add_widget (ch_grp, l_mode); y = BY + 3; if (!single_set) { i = BUTTONS_PERM; group_add_widget (ch_grp, hline_new (y++, -1, -1)); advanced_chown_but[i].id = group_add_widget (ch_grp, button_new (y, WIDGET (ch_dlg)->rect.cols / 2 - advanced_chown_but[i].len, advanced_chown_but[i].ret_cmd, advanced_chown_but[i].flags, advanced_chown_but[i].text, NULL)); i++; advanced_chown_but[i].id = group_add_widget (ch_grp, button_new (y, WIDGET (ch_dlg)->rect.cols / 2 + 1, advanced_chown_but[i].ret_cmd, advanced_chown_but[i].flags, advanced_chown_but[i].text, NULL)); y++; } i = BUTTONS_PERM + 2; group_add_widget (ch_grp, hline_new (y++, -1, -1)); advanced_chown_but[i].id = group_add_widget (ch_grp, button_new (y, WIDGET (ch_dlg)->rect.cols / 2 - advanced_chown_but[i].len, advanced_chown_but[i].ret_cmd, advanced_chown_but[i].flags, advanced_chown_but[i].text, NULL)); i++; advanced_chown_but[i].id = group_add_widget (ch_grp, button_new (y, WIDGET (ch_dlg)->rect.cols / 2 + 1, advanced_chown_but[i].ret_cmd, advanced_chown_but[i].flags, advanced_chown_but[i].text, NULL)); widget_select (WIDGET (b_att[0])); return ch_dlg; } /* --------------------------------------------------------------------------------------------- */ static void advanced_chown_done (gboolean need_update) { if (need_update) update_panels (UP_OPTIMIZE, UP_KEEPSEL); repaint_screen (); } /* --------------------------------------------------------------------------------------------- */ static const GString * next_file (const WPanel * panel) { while (panel->dir.list[current_file].f.marked == 0) current_file++; return panel->dir.list[current_file].fname; } /* --------------------------------------------------------------------------------------------- */ static gboolean try_advanced_chown (const vfs_path_t * p, mode_t m, uid_t u, gid_t g) { int chmod_result; const char *fname = NULL; while ((chmod_result = mc_chmod (p, m)) == -1 && !ignore_all) { int my_errno = errno; int result; char *msg; if (fname == NULL) fname = x_basename (vfs_path_as_str (p)); msg = g_strdup_printf (_("Cannot chmod \"%s\"\n%s"), fname, 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: /* call mc_chown() only, if mc_chmod() didn't fail */ return TRUE; case 1: ignore_all = TRUE; /* call mc_chown() only, if mc_chmod() didn't fail */ return TRUE; case 2: /* retry chmod of this file */ break; case 3: default: /* stop remain files processing */ return FALSE; } } /* call mc_chown() only, if mc_chmod didn't fail */ while (chmod_result != -1 && mc_chown (p, u, g) == -1 && !ignore_all) { int my_errno = errno; int result; char *msg; if (fname == NULL) fname = x_basename (vfs_path_as_str (p)); msg = g_strdup_printf (_("Cannot chown \"%s\"\n%s"), fname, 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 chown of this file */ break; case 3: default: /* stop remain files processing */ return FALSE; } } return TRUE; } /* --------------------------------------------------------------------------------------------- */ static gboolean do_advanced_chown (WPanel * panel, const vfs_path_t * p, mode_t m, uid_t u, gid_t g) { gboolean ret; ret = try_advanced_chown (p, m, u, g); do_file_mark (panel, current_file, 0); return ret; } /* --------------------------------------------------------------------------------------------- */ static void apply_advanced_chowns (WPanel * panel, vfs_path_t * vpath, struct stat *sf) { gid_t a_gid = sf->st_gid; uid_t a_uid = sf->st_uid; gboolean ok; if (!do_advanced_chown (panel, vpath, get_mode (), (ch_flags[9] == '+') ? a_uid : (uid_t) (-1), (ch_flags[10] == '+') ? a_gid : (gid_t) (-1))) return; do { const GString *fname; fname = next_file (panel); vpath = vfs_path_from_str (fname->str); ok = (mc_stat (vpath, sf) == 0); if (!ok) { /* if current file was deleted outside mc -- try next file */ /* decrease panel->marked */ do_file_mark (panel, current_file, 0); /* try next file */ ok = TRUE; } else { ch_cmode = sf->st_mode; ok = do_advanced_chown (panel, vpath, get_mode (), (ch_flags[9] == '+') ? a_uid : (uid_t) (-1), (ch_flags[10] == '+') ? a_gid : (gid_t) (-1)); } vfs_path_free (vpath, TRUE); } while (ok && panel->marked != 0); } /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ void advanced_chown_cmd (WPanel * panel) { gboolean need_update; gboolean end_chown; /* Number of files at startup */ int files_on_begin; files_on_begin = MAX (1, panel->marked); advanced_chown_init (); current_file = 0; ignore_all = FALSE; do { /* do while any files remaining */ vfs_path_t *vpath; WDialog *ch_dlg; const GString *fname; int result; int file_idx; do_refresh (); need_update = FALSE; end_chown = FALSE; if (panel->marked != 0) fname = next_file (panel); /* next marked file */ else fname = panel_current_entry (panel)->fname; /* single file */ vpath = vfs_path_from_str (fname->str); if (mc_stat (vpath, &sf_stat) != 0) { vfs_path_free (vpath, TRUE); break; } ch_cmode = sf_stat.st_mode; ch_dlg = advanced_chown_dlg_create (panel); file_idx = files_on_begin == 1 ? 1 : (files_on_begin - panel->marked + 1); label_set_textv (l_filename, "%s (%d/%d)", str_fit_to_term (fname->str, WIDGET (ch_dlg)->rect.cols - 20, J_LEFT_FIT), file_idx, files_on_begin); update_ownership (); result = dlg_run (ch_dlg); switch (result) { case B_CANCEL: end_chown = TRUE; break; case B_ENTER: { uid_t uid = ch_flags[9] == '+' ? sf_stat.st_uid : (uid_t) (-1); gid_t gid = ch_flags[10] == '+' ? sf_stat.st_gid : (gid_t) (-1); if (panel->marked <= 1) { /* single or last file */ if (mc_chmod (vpath, get_mode ()) == -1) message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"), fname->str, unix_error_string (errno)); /* call mc_chown only, if mc_chmod didn't fail */ else if (mc_chown (vpath, uid, gid) == -1) message (D_ERROR, MSG_ERROR, _("Cannot chown \"%s\"\n%s"), fname->str, unix_error_string (errno)); end_chown = TRUE; } else if (!try_advanced_chown (vpath, get_mode (), uid, gid)) { /* stop multiple files processing */ result = B_CANCEL; end_chown = TRUE; } need_update = TRUE; break; } case B_SETALL: apply_advanced_chowns (panel, vpath, &sf_stat); need_update = TRUE; end_chown = TRUE; break; case B_SKIP: default: break; } if (panel->marked != 0 && result != B_CANCEL) { do_file_mark (panel, current_file, 0); need_update = TRUE; } vfs_path_free (vpath, TRUE); widget_destroy (WIDGET (ch_dlg)); } while (panel->marked != 0 && !end_chown); advanced_chown_done (need_update); } /* --------------------------------------------------------------------------------------------- */