Merge branch '4524_cleanup'
[midnight-commander.git] / src / filemanager / achown.c
blob717a64562cd8a6180da7720cab9b9f0f82c16b5e
1 /*
2 Chown-advanced command -- for the Midnight Commander
4 Copyright (C) 1994-2024
5 Free Software Foundation, Inc.
7 This file is part of the Midnight Commander.
9 The Midnight Commander is free software: you can redistribute it
10 and/or modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation, either version 3 of the License,
12 or (at your option) any later version.
14 The Midnight Commander is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 /** \file achown.c
24 * \brief Source: Contains functions for advanced chowning
27 #include <config.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
38 #include "lib/global.h"
40 #include "lib/tty/tty.h"
41 #include "lib/tty/key.h" /* XCTRL and ALT macros */
42 #include "lib/skin.h"
43 #include "lib/vfs/vfs.h"
44 #include "lib/strutil.h"
45 #include "lib/util.h"
46 #include "lib/widget.h"
48 #include "cmd.h" /* advanced_chown_cmd() */
50 /*** global variables ****************************************************************************/
52 /*** file scope macro definitions ****************************************************************/
54 #define BX 5
55 #define BY 5
57 #define BUTTONS 9
58 #define BUTTONS_PERM 5
60 #define B_SETALL B_USER
61 #define B_SKIP (B_USER + 1)
63 /*** file scope type declarations ****************************************************************/
65 /*** forward declarations (file scope functions) *************************************************/
67 /*** file scope variables ************************************************************************/
69 static struct
71 unsigned long id;
72 int ret_cmd;
73 button_flags_t flags;
74 int x;
75 int len;
76 const char *text;
77 } advanced_chown_but[BUTTONS] =
79 /* *INDENT-OFF* */
80 { 0, B_ENTER, NARROW_BUTTON, 3, 0, " " },
81 { 0, B_ENTER, NARROW_BUTTON, 11, 0, " " },
82 { 0, B_ENTER, NARROW_BUTTON, 19, 0, " " },
83 { 0, B_ENTER, NARROW_BUTTON, 29, 0, "" },
84 { 0, B_ENTER, NARROW_BUTTON, 47, 0, "" },
86 { 0, B_SETALL, NORMAL_BUTTON, 0, 0, N_("Set &all") },
87 { 0, B_SKIP, NORMAL_BUTTON, 0, 0, N_("S&kip") },
88 { 0, B_ENTER, DEFPUSH_BUTTON, 0, 0, N_("&Set") },
89 { 0, B_CANCEL, NORMAL_BUTTON, 0, 0, N_("&Cancel") }
90 /* *INDENT-ON* */
93 static int current_file;
94 static gboolean ignore_all;
96 static WButton *b_att[3]; /* permission */
97 static WButton *b_user, *b_group; /* owner */
98 static WLabel *l_filename;
99 static WLabel *l_mode;
101 static int flag_pos;
102 static int x_toggle;
103 static char ch_flags[11];
104 static const char ch_perm[] = "rwx";
105 static mode_t ch_cmode;
106 static struct stat sf_stat;
108 /* --------------------------------------------------------------------------------------------- */
109 /*** file scope functions ************************************************************************/
110 /* --------------------------------------------------------------------------------------------- */
112 static void
113 advanced_chown_init (void)
115 static gboolean i18n = FALSE;
116 int i;
118 if (i18n)
119 return;
121 i18n = TRUE;
123 for (i = BUTTONS_PERM; i < BUTTONS; i++)
125 #ifdef ENABLE_NLS
126 advanced_chown_but[i].text = _(advanced_chown_but[i].text);
127 #endif /* ENABLE_NLS */
129 advanced_chown_but[i].len = str_term_width1 (advanced_chown_but[i].text) + 3;
130 if (advanced_chown_but[i].flags == DEFPUSH_BUTTON)
131 advanced_chown_but[i].len += 2; /* "<>" */
136 /* --------------------------------------------------------------------------------------------- */
138 static cb_ret_t
139 inc_flag_pos (void)
141 if (flag_pos == 10)
143 flag_pos = 0;
144 return MSG_NOT_HANDLED;
147 flag_pos++;
149 return flag_pos % 3 == 0 ? MSG_NOT_HANDLED : MSG_HANDLED;
152 /* --------------------------------------------------------------------------------------------- */
154 static cb_ret_t
155 dec_flag_pos (void)
157 if (flag_pos == 0)
159 flag_pos = 10;
160 return MSG_NOT_HANDLED;
163 flag_pos--;
165 return (flag_pos + 1) % 3 == 0 ? MSG_NOT_HANDLED : MSG_HANDLED;
168 /* --------------------------------------------------------------------------------------------- */
170 static void
171 set_perm_by_flags (char *s, int f_p)
173 int i;
175 for (i = 0; i < 3; i++)
177 if (ch_flags[f_p + i] == '+')
178 s[i] = ch_perm[i];
179 else if (ch_flags[f_p + i] == '-')
180 s[i] = '-';
181 else
182 s[i] = (ch_cmode & (1 << (8 - f_p - i))) != 0 ? ch_perm[i] : '-';
186 /* --------------------------------------------------------------------------------------------- */
188 static mode_t
189 get_perm (char *s, int base)
191 mode_t m = 0;
193 m |= (s[0] == '-') ? 0 :
194 ((s[0] == '+') ? (mode_t) (1 << (base + 2)) : (1 << (base + 2)) & ch_cmode);
196 m |= (s[1] == '-') ? 0 :
197 ((s[1] == '+') ? (mode_t) (1 << (base + 1)) : (1 << (base + 1)) & ch_cmode);
199 m |= (s[2] == '-') ? 0 : ((s[2] == '+') ? (mode_t) (1 << base) : (1 << base) & ch_cmode);
201 return m;
204 /* --------------------------------------------------------------------------------------------- */
206 static mode_t
207 get_mode (void)
209 mode_t m;
211 m = ch_cmode ^ (ch_cmode & 0777);
212 m |= get_perm (ch_flags, 6);
213 m |= get_perm (ch_flags + 3, 3);
214 m |= get_perm (ch_flags + 6, 0);
216 return m;
219 /* --------------------------------------------------------------------------------------------- */
221 static void
222 update_permissions (void)
224 set_perm_by_flags (b_att[0]->text.start, 0);
225 set_perm_by_flags (b_att[1]->text.start, 3);
226 set_perm_by_flags (b_att[2]->text.start, 6);
229 /* --------------------------------------------------------------------------------------------- */
231 static void
232 update_ownership (void)
234 button_set_text (b_user, get_owner (sf_stat.st_uid));
235 button_set_text (b_group, get_group (sf_stat.st_gid));
238 /* --------------------------------------------------------------------------------------------- */
240 static void
241 print_flags (const WDialog * h)
243 int i;
245 tty_setcolor (COLOR_NORMAL);
247 for (i = 0; i < 3; i++)
249 widget_gotoyx (h, BY + 1, advanced_chown_but[0].x + 6 + i);
250 tty_print_char (ch_flags[i]);
253 for (i = 0; i < 3; i++)
255 widget_gotoyx (h, BY + 1, advanced_chown_but[1].x + 6 + i);
256 tty_print_char (ch_flags[i + 3]);
259 for (i = 0; i < 3; i++)
261 widget_gotoyx (h, BY + 1, advanced_chown_but[2].x + 6 + i);
262 tty_print_char (ch_flags[i + 6]);
265 update_permissions ();
267 for (i = 0; i < 15; i++)
269 widget_gotoyx (h, BY + 1, advanced_chown_but[3].x + 6 + i);
270 tty_print_char (ch_flags[9]);
272 for (i = 0; i < 15; i++)
274 widget_gotoyx (h, BY + 1, advanced_chown_but[4].x + 6 + i);
275 tty_print_char (ch_flags[10]);
279 /* --------------------------------------------------------------------------------------------- */
281 static void
282 advanced_chown_refresh (const WDialog * h)
284 tty_setcolor (COLOR_NORMAL);
286 widget_gotoyx (h, BY - 1, advanced_chown_but[0].x + 5);
287 tty_print_string (_("owner"));
288 widget_gotoyx (h, BY - 1, advanced_chown_but[1].x + 5);
289 tty_print_string (_("group"));
290 widget_gotoyx (h, BY - 1, advanced_chown_but[2].x + 5);
291 tty_print_string (_("other"));
293 widget_gotoyx (h, BY - 1, advanced_chown_but[3].x + 5);
294 tty_print_string (_("owner"));
295 widget_gotoyx (h, BY - 1, advanced_chown_but[4].x + 5);
296 tty_print_string (_("group"));
298 widget_gotoyx (h, BY + 1, 3);
299 tty_print_string (_("Flag"));
300 print_flags (h);
303 /* --------------------------------------------------------------------------------------------- */
305 static void
306 advanced_chown_info_update (void)
308 /* mode */
309 label_set_textv (l_mode, _("Permissions (octal): %o"), get_mode ());
311 /* permissions */
312 update_permissions ();
315 /* --------------------------------------------------------------------------------------------- */
317 static void
318 update_mode (WGroup * g)
320 print_flags (DIALOG (g));
321 advanced_chown_info_update ();
322 widget_set_state (WIDGET (g->current->data), WST_FOCUSED, TRUE);
325 /* --------------------------------------------------------------------------------------------- */
327 static cb_ret_t
328 perm_button_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
330 WButton *b = BUTTON (w);
331 WGroup *g = w->owner;
332 int i = 0;
333 int f_pos;
335 /* one of permission buttons */
336 if (b == b_att[0])
337 f_pos = 0;
338 else if (b == b_att[1])
339 f_pos = 1;
340 else /* if (w == b_att [1] */
341 f_pos = 2;
343 switch (msg)
345 case MSG_FOCUS:
346 if (b->hotpos == -1)
347 b->hotpos = 0;
349 flag_pos = f_pos * 3 + b->hotpos;
350 return MSG_HANDLED;
352 case MSG_KEY:
353 switch (parm)
355 case '*':
356 parm = '=';
357 MC_FALLTHROUGH;
359 case '-':
360 case '=':
361 case '+':
362 flag_pos = f_pos * 3 + b->hotpos;
363 ch_flags[flag_pos] = parm;
364 update_mode (g);
365 send_message (w, NULL, MSG_KEY, KEY_RIGHT, NULL);
366 if (b->hotpos == 2)
367 group_select_next_widget (g);
368 break;
370 case XCTRL ('f'):
371 case KEY_RIGHT:
373 cb_ret_t ret;
375 ret = inc_flag_pos ();
376 b->hotpos = flag_pos % 3;
377 return ret;
380 case XCTRL ('b'):
381 case KEY_LEFT:
383 cb_ret_t ret;
385 ret = dec_flag_pos ();
386 b->hotpos = flag_pos % 3;
387 return ret;
390 case 'x':
391 i++;
392 MC_FALLTHROUGH;
394 case 'w':
395 i++;
396 MC_FALLTHROUGH;
398 case 'r':
399 b->hotpos = i;
400 MC_FALLTHROUGH;
402 case ' ':
403 i = b->hotpos;
405 flag_pos = f_pos * 3 + i;
406 if (b->text.start[flag_pos % 3] == '-')
407 ch_flags[flag_pos] = '+';
408 else
409 ch_flags[flag_pos] = '-';
410 update_mode (w->owner);
411 break;
413 case '4':
414 i++;
415 MC_FALLTHROUGH;
417 case '2':
418 i++;
419 MC_FALLTHROUGH;
421 case '1':
422 b->hotpos = i;
423 flag_pos = f_pos * 3 + i;
424 ch_flags[flag_pos] = '=';
425 update_mode (g);
426 break;
428 default:
429 break;
431 /* continue key handling in the dialog level */
432 return MSG_NOT_HANDLED;
434 default:
435 return button_default_callback (w, sender, msg, parm, data);
439 /* --------------------------------------------------------------------------------------------- */
441 static void
442 perm_button_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
444 switch (msg)
446 case MSG_MOUSE_DOWN:
447 /* place cursor on flag that is being modified */
448 BUTTON (w)->hotpos = CLAMP (event->x - 1, 0, 2);
449 MC_FALLTHROUGH;
451 default:
452 button_mouse_default_callback (w, msg, event);
453 break;
457 /* --------------------------------------------------------------------------------------------- */
459 static WButton *
460 perm_button_new (int y, int x, int action, button_flags_t flags, const char *text,
461 bcback_fn callback)
463 WButton *b;
464 Widget *w;
466 /* create base button using native API */
467 b = button_new (y, x, action, flags, text, callback);
468 w = WIDGET (b);
470 /* we don't want HOTKEY */
471 widget_want_hotkey (w, FALSE);
473 w->callback = perm_button_callback;
474 w->mouse_callback = perm_button_mouse_callback;
476 return b;
479 /* --------------------------------------------------------------------------------------------- */
481 static cb_ret_t
482 chl_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
484 switch (msg)
486 case MSG_KEY:
487 switch (parm)
489 case KEY_LEFT:
490 case KEY_RIGHT:
492 WDialog *h = DIALOG (w);
494 h->ret_value = parm;
495 dlg_close (h);
497 break;
498 default:
499 break;
501 MC_FALLTHROUGH;
503 default:
504 return dlg_default_callback (w, sender, msg, parm, data);
508 /* --------------------------------------------------------------------------------------------- */
510 static int
511 user_group_button_cb (WButton * button, int action)
513 Widget *w = WIDGET (button);
514 int f_pos;
515 gboolean chl_end;
517 (void) action;
519 if (button == b_user)
520 f_pos = BUTTONS_PERM - 2;
521 else if (button == b_group)
522 f_pos = BUTTONS_PERM - 1;
523 else
524 return 0; /* do nothing */
528 WGroup *g = w->owner;
529 WDialog *h = DIALOG (g);
530 Widget *wh = WIDGET (h);
532 gboolean is_owner = (f_pos == BUTTONS_PERM - 2);
533 const char *title;
534 int lxx, b_current;
535 WDialog *chl_dlg;
536 WListbox *chl_list;
537 int result;
538 int fe;
539 struct passwd *chl_pass;
540 struct group *chl_grp;
542 chl_end = FALSE;
544 if (is_owner)
546 title = _("owner");
547 lxx = WIDGET (b_user)->rect.x + 1;
549 else
551 title = _("group");
552 lxx = WIDGET (b_group)->rect.x + 1;
555 chl_dlg =
556 dlg_create (TRUE, wh->rect.y - 1, lxx, wh->rect.lines + 2, 17, WPOS_KEEP_DEFAULT, TRUE,
557 dialog_colors, chl_callback, NULL, "[Advanced Chown]", title);
559 /* get new listboxes */
560 chl_list =
561 listbox_new (1, 1, WIDGET (chl_dlg)->rect.lines - 2, WIDGET (chl_dlg)->rect.cols - 2,
562 FALSE, NULL);
563 listbox_add_item (chl_list, LISTBOX_APPEND_AT_END, 0, "<Unknown>", NULL, FALSE);
564 if (is_owner)
566 /* get and put user names in the listbox */
567 setpwent ();
568 while ((chl_pass = getpwent ()) != NULL)
569 listbox_add_item (chl_list, LISTBOX_APPEND_SORTED, 0, chl_pass->pw_name, NULL,
570 FALSE);
571 endpwent ();
572 fe = listbox_search_text (chl_list, get_owner (sf_stat.st_uid));
574 else
576 /* get and put group names in the listbox */
577 setgrent ();
578 while ((chl_grp = getgrent ()) != NULL)
579 listbox_add_item (chl_list, LISTBOX_APPEND_SORTED, 0, chl_grp->gr_name, NULL,
580 FALSE);
581 endgrent ();
582 fe = listbox_search_text (chl_list, get_group (sf_stat.st_gid));
585 listbox_set_current (chl_list, fe);
587 b_current = chl_list->current;
588 group_add_widget (GROUP (chl_dlg), chl_list);
590 result = dlg_run (chl_dlg);
592 if (result != B_CANCEL)
594 if (b_current != chl_list->current)
596 gboolean ok = FALSE;
597 char *text;
599 listbox_get_current (chl_list, &text, NULL);
600 if (is_owner)
602 chl_pass = getpwnam (text);
603 if (chl_pass != NULL)
605 sf_stat.st_uid = chl_pass->pw_uid;
606 ok = TRUE;
609 else
611 chl_grp = getgrnam (text);
612 if (chl_grp != NULL)
614 sf_stat.st_gid = chl_grp->gr_gid;
615 ok = TRUE;
619 if (!ok)
620 group_select_current_widget (g);
621 else
623 ch_flags[f_pos + 6] = '+';
624 update_ownership ();
625 group_select_current_widget (g);
626 print_flags (h);
630 if (result == KEY_LEFT)
632 if (!is_owner)
633 chl_end = TRUE;
634 group_select_prev_widget (g);
635 f_pos--;
637 else if (result == KEY_RIGHT)
639 if (is_owner)
640 chl_end = TRUE;
641 group_select_next_widget (g);
642 f_pos++;
646 /* Here we used to redraw the window */
647 widget_destroy (WIDGET (chl_dlg));
649 while (chl_end);
651 return 0;
654 /* --------------------------------------------------------------------------------------------- */
656 static cb_ret_t
657 advanced_chown_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
659 switch (msg)
661 case MSG_DRAW:
662 frame_callback (w, NULL, MSG_DRAW, 0, NULL);
663 advanced_chown_refresh (DIALOG (w->owner));
664 advanced_chown_info_update ();
665 return MSG_HANDLED;
667 default:
668 return frame_callback (w, sender, msg, parm, data);
672 /* --------------------------------------------------------------------------------------------- */
674 static cb_ret_t
675 advanced_chown_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
677 WGroup *g = GROUP (w);
678 int i = 0;
680 switch (msg)
682 case MSG_KEY:
683 switch (parm)
685 case ALT ('x'):
686 i++;
687 MC_FALLTHROUGH;
689 case ALT ('w'):
690 i++;
691 MC_FALLTHROUGH;
693 case ALT ('r'):
694 parm = i + 3;
695 for (i = 0; i < 3; i++)
696 ch_flags[i * 3 + parm - 3] = (x_toggle & (1 << parm)) ? '-' : '+';
697 x_toggle ^= (1 << parm);
698 update_mode (g);
699 widget_draw (w);
700 break;
702 case XCTRL ('x'):
703 i++;
704 MC_FALLTHROUGH;
706 case XCTRL ('w'):
707 i++;
708 MC_FALLTHROUGH;
710 case XCTRL ('r'):
711 parm = i;
712 for (i = 0; i < 3; i++)
713 ch_flags[i * 3 + parm] = (x_toggle & (1 << parm)) ? '-' : '+';
714 x_toggle ^= (1 << parm);
715 update_mode (g);
716 widget_draw (w);
717 break;
719 default:
720 break;
722 return MSG_NOT_HANDLED;
724 default:
725 return dlg_default_callback (w, sender, msg, parm, data);
729 /* --------------------------------------------------------------------------------------------- */
731 static WDialog *
732 advanced_chown_dlg_create (WPanel * panel)
734 gboolean single_set;
735 WDialog *ch_dlg;
736 WGroup *ch_grp;
737 int lines = 12;
738 int cols = 74;
739 int i;
740 int y;
742 memset (ch_flags, '=', 11);
743 flag_pos = 0;
744 x_toggle = 070;
746 single_set = (panel->marked < 2);
747 if (!single_set)
748 lines += 2;
750 ch_dlg =
751 dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors,
752 advanced_chown_callback, NULL, "[Advanced Chown]", _("Chown advanced command"));
753 ch_grp = GROUP (ch_dlg);
755 /* draw background */
756 ch_dlg->bg->callback = advanced_chown_bg_callback;
758 l_filename = label_new (2, 3, NULL);
759 group_add_widget (ch_grp, l_filename);
761 group_add_widget (ch_grp, hline_new (3, -1, -1));
763 #define XTRACT(i,y,cb) y, BX+advanced_chown_but[i].x, \
764 advanced_chown_but[i].ret_cmd, advanced_chown_but[i].flags, \
765 (advanced_chown_but[i].text), cb
766 b_att[0] = perm_button_new (XTRACT (0, BY, NULL));
767 advanced_chown_but[0].id = group_add_widget (ch_grp, b_att[0]);
768 b_att[1] = perm_button_new (XTRACT (1, BY, NULL));
769 advanced_chown_but[1].id = group_add_widget (ch_grp, b_att[1]);
770 b_att[2] = perm_button_new (XTRACT (2, BY, NULL));
771 advanced_chown_but[2].id = group_add_widget (ch_grp, b_att[2]);
772 b_user = button_new (XTRACT (3, BY, user_group_button_cb));
773 advanced_chown_but[3].id = group_add_widget (ch_grp, b_user);
774 b_group = button_new (XTRACT (4, BY, user_group_button_cb));
775 advanced_chown_but[4].id = group_add_widget (ch_grp, b_group);
777 l_mode = label_new (BY + 2, 3, NULL);
778 group_add_widget (ch_grp, l_mode);
780 y = BY + 3;
781 if (!single_set)
783 i = BUTTONS_PERM;
784 group_add_widget (ch_grp, hline_new (y++, -1, -1));
785 advanced_chown_but[i].id = group_add_widget (ch_grp,
786 button_new (y,
787 WIDGET (ch_dlg)->rect.cols / 2 -
788 advanced_chown_but[i].len,
789 advanced_chown_but[i].ret_cmd,
790 advanced_chown_but[i].flags,
791 advanced_chown_but[i].text, NULL));
792 i++;
793 advanced_chown_but[i].id = group_add_widget (ch_grp,
794 button_new (y,
795 WIDGET (ch_dlg)->rect.cols / 2 + 1,
796 advanced_chown_but[i].ret_cmd,
797 advanced_chown_but[i].flags,
798 advanced_chown_but[i].text, NULL));
799 y++;
802 i = BUTTONS_PERM + 2;
803 group_add_widget (ch_grp, hline_new (y++, -1, -1));
804 advanced_chown_but[i].id = group_add_widget (ch_grp,
805 button_new (y,
806 WIDGET (ch_dlg)->rect.cols / 2 -
807 advanced_chown_but[i].len,
808 advanced_chown_but[i].ret_cmd,
809 advanced_chown_but[i].flags,
810 advanced_chown_but[i].text, NULL));
811 i++;
812 advanced_chown_but[i].id = group_add_widget (ch_grp,
813 button_new (y, WIDGET (ch_dlg)->rect.cols / 2 + 1,
814 advanced_chown_but[i].ret_cmd,
815 advanced_chown_but[i].flags,
816 advanced_chown_but[i].text, NULL));
818 widget_select (WIDGET (b_att[0]));
820 return ch_dlg;
823 /* --------------------------------------------------------------------------------------------- */
825 static void
826 advanced_chown_done (gboolean need_update)
828 if (need_update)
829 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
830 repaint_screen ();
833 /* --------------------------------------------------------------------------------------------- */
835 static const GString *
836 next_file (const WPanel * panel)
838 while (panel->dir.list[current_file].f.marked == 0)
839 current_file++;
841 return panel->dir.list[current_file].fname;
844 /* --------------------------------------------------------------------------------------------- */
846 static gboolean
847 try_advanced_chown (const vfs_path_t * p, mode_t m, uid_t u, gid_t g)
849 int chmod_result;
850 const char *fname = NULL;
852 while ((chmod_result = mc_chmod (p, m)) == -1 && !ignore_all)
854 int my_errno = errno;
855 int result;
856 char *msg;
858 if (fname == NULL)
859 fname = x_basename (vfs_path_as_str (p));
860 msg = g_strdup_printf (_("Cannot chmod \"%s\"\n%s"), fname, unix_error_string (my_errno));
861 result =
862 query_dialog (MSG_ERROR, msg, D_ERROR, 4, _("&Ignore"), _("Ignore &all"), _("&Retry"),
863 _("&Cancel"));
864 g_free (msg);
866 switch (result)
868 case 0:
869 /* call mc_chown() only, if mc_chmod() didn't fail */
870 return TRUE;
872 case 1:
873 ignore_all = TRUE;
874 /* call mc_chown() only, if mc_chmod() didn't fail */
875 return TRUE;
877 case 2:
878 /* retry chmod of this file */
879 break;
881 case 3:
882 default:
883 /* stop remain files processing */
884 return FALSE;
888 /* call mc_chown() only, if mc_chmod didn't fail */
889 while (chmod_result != -1 && mc_chown (p, u, g) == -1 && !ignore_all)
891 int my_errno = errno;
892 int result;
893 char *msg;
895 if (fname == NULL)
896 fname = x_basename (vfs_path_as_str (p));
897 msg = g_strdup_printf (_("Cannot chown \"%s\"\n%s"), fname, unix_error_string (my_errno));
898 result =
899 query_dialog (MSG_ERROR, msg, D_ERROR, 4, _("&Ignore"), _("Ignore &all"), _("&Retry"),
900 _("&Cancel"));
901 g_free (msg);
903 switch (result)
905 case 0:
906 /* try next file */
907 return TRUE;
909 case 1:
910 ignore_all = TRUE;
911 /* try next file */
912 return TRUE;
914 case 2:
915 /* retry chown of this file */
916 break;
918 case 3:
919 default:
920 /* stop remain files processing */
921 return FALSE;
925 return TRUE;
929 /* --------------------------------------------------------------------------------------------- */
931 static gboolean
932 do_advanced_chown (WPanel * panel, const vfs_path_t * p, mode_t m, uid_t u, gid_t g)
934 gboolean ret;
936 ret = try_advanced_chown (p, m, u, g);
938 do_file_mark (panel, current_file, 0);
940 return ret;
943 /* --------------------------------------------------------------------------------------------- */
945 static void
946 apply_advanced_chowns (WPanel * panel, vfs_path_t * vpath, struct stat *sf)
948 gid_t a_gid = sf->st_gid;
949 uid_t a_uid = sf->st_uid;
950 gboolean ok;
952 if (!do_advanced_chown (panel, vpath, get_mode (),
953 (ch_flags[9] == '+') ? a_uid : (uid_t) (-1),
954 (ch_flags[10] == '+') ? a_gid : (gid_t) (-1)))
955 return;
959 const GString *fname;
961 fname = next_file (panel);
962 vpath = vfs_path_from_str (fname->str);
963 ok = (mc_stat (vpath, sf) == 0);
965 if (!ok)
967 /* if current file was deleted outside mc -- try next file */
968 /* decrease panel->marked */
969 do_file_mark (panel, current_file, 0);
971 /* try next file */
972 ok = TRUE;
974 else
976 ch_cmode = sf->st_mode;
978 ok = do_advanced_chown (panel, vpath, get_mode (),
979 (ch_flags[9] == '+') ? a_uid : (uid_t) (-1),
980 (ch_flags[10] == '+') ? a_gid : (gid_t) (-1));
983 vfs_path_free (vpath, TRUE);
985 while (ok && panel->marked != 0);
988 /* --------------------------------------------------------------------------------------------- */
989 /*** public functions ****************************************************************************/
990 /* --------------------------------------------------------------------------------------------- */
992 void
993 advanced_chown_cmd (WPanel * panel)
995 gboolean need_update;
996 gboolean end_chown;
998 /* Number of files at startup */
999 int files_on_begin;
1001 files_on_begin = MAX (1, panel->marked);
1003 advanced_chown_init ();
1005 current_file = 0;
1006 ignore_all = FALSE;
1009 { /* do while any files remaining */
1010 vfs_path_t *vpath;
1011 WDialog *ch_dlg;
1012 const GString *fname;
1013 int result;
1014 int file_idx;
1016 do_refresh ();
1018 need_update = FALSE;
1019 end_chown = FALSE;
1021 if (panel->marked != 0)
1022 fname = next_file (panel); /* next marked file */
1023 else
1024 fname = panel_current_entry (panel)->fname; /* single file */
1026 vpath = vfs_path_from_str (fname->str);
1028 if (mc_stat (vpath, &sf_stat) != 0)
1030 vfs_path_free (vpath, TRUE);
1031 break;
1034 ch_cmode = sf_stat.st_mode;
1036 ch_dlg = advanced_chown_dlg_create (panel);
1038 file_idx = files_on_begin == 1 ? 1 : (files_on_begin - panel->marked + 1);
1039 label_set_textv (l_filename, "%s (%d/%d)",
1040 str_fit_to_term (fname->str, WIDGET (ch_dlg)->rect.cols - 20, J_LEFT_FIT),
1041 file_idx, files_on_begin);
1042 update_ownership ();
1044 result = dlg_run (ch_dlg);
1046 switch (result)
1048 case B_CANCEL:
1049 end_chown = TRUE;
1050 break;
1052 case B_ENTER:
1054 uid_t uid = ch_flags[9] == '+' ? sf_stat.st_uid : (uid_t) (-1);
1055 gid_t gid = ch_flags[10] == '+' ? sf_stat.st_gid : (gid_t) (-1);
1057 if (panel->marked <= 1)
1059 /* single or last file */
1060 if (mc_chmod (vpath, get_mode ()) == -1)
1061 message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"),
1062 fname->str, unix_error_string (errno));
1063 /* call mc_chown only, if mc_chmod didn't fail */
1064 else if (mc_chown (vpath, uid, gid) == -1)
1065 message (D_ERROR, MSG_ERROR, _("Cannot chown \"%s\"\n%s"), fname->str,
1066 unix_error_string (errno));
1068 end_chown = TRUE;
1070 else if (!try_advanced_chown (vpath, get_mode (), uid, gid))
1072 /* stop multiple files processing */
1073 result = B_CANCEL;
1074 end_chown = TRUE;
1077 need_update = TRUE;
1078 break;
1081 case B_SETALL:
1082 apply_advanced_chowns (panel, vpath, &sf_stat);
1083 need_update = TRUE;
1084 end_chown = TRUE;
1085 break;
1087 case B_SKIP:
1088 default:
1089 break;
1092 if (panel->marked != 0 && result != B_CANCEL)
1094 do_file_mark (panel, current_file, 0);
1095 need_update = TRUE;
1098 vfs_path_free (vpath, TRUE);
1100 widget_destroy (WIDGET (ch_dlg));
1102 while (panel->marked != 0 && !end_chown);
1104 advanced_chown_done (need_update);
1107 /* --------------------------------------------------------------------------------------------- */