2 Chmod 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/>.
24 * \brief Source: chmod command
30 #include <sys/types.h>
34 #include "lib/global.h"
36 #include "lib/tty/tty.h"
38 #include "lib/vfs/vfs.h"
39 #include "lib/strutil.h"
41 #include "lib/widget.h"
43 #include "cmd.h" /* chmod_cmd() */
45 /*** global variables ****************************************************************************/
47 /*** file scope macro definitions ****************************************************************/
52 #define B_MARKED B_USER
53 #define B_SETALL (B_USER + 1)
54 #define B_SETMRK (B_USER + 2)
55 #define B_CLRMRK (B_USER + 3)
58 #define BUTTONS_PERM 12
61 /*** file scope type declarations ****************************************************************/
63 /*** forward declarations (file scope functions) *************************************************/
65 /*** file scope variables ************************************************************************/
73 } check_perm
[BUTTONS_PERM
] =
76 { S_ISUID
, N_("set &user ID on execution"), FALSE
, NULL
},
77 { S_ISGID
, N_("set &group ID on execution"), FALSE
, NULL
},
78 { S_ISVTX
, N_("stick&y bit"), FALSE
, NULL
},
79 { S_IRUSR
, N_("&read by owner"), FALSE
, NULL
},
80 { S_IWUSR
, N_("&write by owner"), FALSE
, NULL
},
81 { S_IXUSR
, N_("e&xecute/search by owner"), FALSE
, NULL
},
82 { S_IRGRP
, N_("rea&d by group"), FALSE
, NULL
},
83 { S_IWGRP
, N_("write by grou&p"), FALSE
, NULL
},
84 { S_IXGRP
, N_("execu&te/search by group"), FALSE
, NULL
},
85 { S_IROTH
, N_("read &by others"), FALSE
, NULL
},
86 { S_IWOTH
, N_("wr&ite by others"), FALSE
, NULL
},
87 { S_IXOTH
, N_("execute/searc&h by others"), FALSE
, NULL
}
91 static int check_perm_len
= 0;
93 static const char *file_info_labels
[LABELS
] = {
95 N_("Permissions (octal):"),
100 static int file_info_labels_len
= 0;
105 button_flags_t flags
;
106 int y
; /* vertical position relatively to dialog bottom boundary */
109 } chmod_but
[BUTTONS
] =
112 { B_SETALL
, NORMAL_BUTTON
, 6, 0, N_("Set &all") },
113 { B_MARKED
, NORMAL_BUTTON
, 6, 0, N_("&Marked all") },
114 { B_SETMRK
, NORMAL_BUTTON
, 5, 0, N_("S&et marked") },
115 { B_CLRMRK
, NORMAL_BUTTON
, 5, 0, N_("C&lear marked") },
116 { B_ENTER
, DEFPUSH_BUTTON
, 3, 0, N_("&Set") },
117 { B_CANCEL
, NORMAL_BUTTON
, 3, 0, N_("&Cancel") }
121 static gboolean mode_change
;
122 static int current_file
;
123 static gboolean ignore_all
;
125 static mode_t and_mask
, or_mask
, ch_mode
;
127 static WLabel
*statl
;
128 static WGroupbox
*file_gb
;
130 /* --------------------------------------------------------------------------------------------- */
131 /*** file scope functions ************************************************************************/
132 /* --------------------------------------------------------------------------------------------- */
137 static gboolean i18n
= FALSE
;
140 for (i
= 0; i
< BUTTONS_PERM
; i
++)
141 check_perm
[i
].selected
= FALSE
;
149 for (i
= 0; i
< BUTTONS_PERM
; i
++)
150 check_perm
[i
].text
= _(check_perm
[i
].text
);
152 for (i
= 0; i
< LABELS
; i
++)
153 file_info_labels
[i
] = _(file_info_labels
[i
]);
155 for (i
= 0; i
< BUTTONS
; i
++)
156 chmod_but
[i
].text
= _(chmod_but
[i
].text
);
157 #endif /* ENABLE_NLS */
159 for (i
= 0; i
< BUTTONS_PERM
; i
++)
161 len
= str_term_width1 (check_perm
[i
].text
);
162 check_perm_len
= MAX (check_perm_len
, len
);
165 check_perm_len
+= 1 + 3 + 1; /* mark, [x] and space */
167 for (i
= 0; i
< LABELS
; i
++)
169 len
= str_term_width1 (file_info_labels
[i
]) + 2; /* spaces around */
170 file_info_labels_len
= MAX (file_info_labels_len
, len
);
173 for (i
= 0; i
< BUTTONS
; i
++)
175 chmod_but
[i
].len
= str_term_width1 (chmod_but
[i
].text
) + 3; /* [], spaces and w/o & */
176 if (chmod_but
[i
].flags
== DEFPUSH_BUTTON
)
177 chmod_but
[i
].len
+= 2; /* <> */
181 /* --------------------------------------------------------------------------------------------- */
184 chmod_draw_select (const WDialog
* h
, int Id
)
186 widget_gotoyx (h
, PY
+ Id
+ 1, PX
+ 1);
187 tty_print_char (check_perm
[Id
].selected
? '*' : ' ');
188 widget_gotoyx (h
, PY
+ Id
+ 1, PX
+ 3);
191 /* --------------------------------------------------------------------------------------------- */
194 chmod_toggle_select (const WDialog
* h
, int Id
)
196 check_perm
[Id
].selected
= !check_perm
[Id
].selected
;
197 tty_setcolor (COLOR_NORMAL
);
198 chmod_draw_select (h
, Id
);
201 /* --------------------------------------------------------------------------------------------- */
204 chmod_refresh (const WDialog
* h
)
209 tty_setcolor (COLOR_NORMAL
);
211 for (i
= 0; i
< BUTTONS_PERM
; i
++)
212 chmod_draw_select (h
, i
);
214 y
= WIDGET (file_gb
)->rect
.y
+ 1;
215 x
= WIDGET (file_gb
)->rect
.x
+ 2;
218 tty_print_string (file_info_labels
[0]);
219 tty_gotoyx (y
+ 2, x
);
220 tty_print_string (file_info_labels
[1]);
221 tty_gotoyx (y
+ 4, x
);
222 tty_print_string (file_info_labels
[2]);
223 tty_gotoyx (y
+ 6, x
);
224 tty_print_string (file_info_labels
[3]);
227 /* --------------------------------------------------------------------------------------------- */
230 chmod_bg_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
235 frame_callback (w
, NULL
, MSG_DRAW
, 0, NULL
);
236 chmod_refresh (CONST_DIALOG (w
->owner
));
240 return frame_callback (w
, sender
, msg
, parm
, data
);
244 /* --------------------------------------------------------------------------------------------- */
247 chmod_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
249 WGroup
*g
= GROUP (w
);
250 WDialog
*h
= DIALOG (w
);
256 /* handle checkboxes */
259 /* whether notification was sent by checkbox? */
260 for (i
= 0; i
< BUTTONS_PERM
; i
++)
261 if (sender
== WIDGET (check_perm
[i
].check
))
264 if (i
< BUTTONS_PERM
)
266 ch_mode
^= check_perm
[i
].mode
;
267 label_set_textv (statl
, "%o", (unsigned int) ch_mode
);
268 chmod_toggle_select (h
, i
);
274 return MSG_NOT_HANDLED
;
277 if (parm
== 'T' || parm
== 't' || parm
== KEY_IC
)
282 id
= group_get_current_widget_id (g
);
283 for (i
= 0; i
< BUTTONS_PERM
; i
++)
284 if (id
== WIDGET (check_perm
[i
].check
)->id
)
287 if (i
< BUTTONS_PERM
)
289 chmod_toggle_select (h
, i
);
291 group_select_next_widget (g
);
295 return MSG_NOT_HANDLED
;
298 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
302 /* --------------------------------------------------------------------------------------------- */
305 chmod_dlg_create (WPanel
* panel
, const char *fname
, const struct stat
*sf_stat
)
314 const char *c_fname
, *c_fown
, *c_fgrp
;
315 char buffer
[BUF_TINY
];
319 single_set
= (panel
->marked
< 2);
320 perm_gb_len
= check_perm_len
+ 2;
321 file_gb_len
= file_info_labels_len
+ 2;
322 cols
= str_term_width1 (fname
) + 2 + 1;
323 file_gb_len
= MAX (file_gb_len
, cols
);
325 lines
= single_set
? 20 : 23;
326 cols
= perm_gb_len
+ file_gb_len
+ 1 + 6;
330 /* shrink the right groupbox */
332 file_gb_len
= cols
- (perm_gb_len
+ 1 + 6);
336 dlg_create (TRUE
, 0, 0, lines
, cols
, WPOS_CENTER
, FALSE
, dialog_colors
,
337 chmod_callback
, NULL
, "[Chmod]", _("Chmod command"));
340 /* draw background */
341 ch_dlg
->bg
->callback
= chmod_bg_callback
;
343 group_add_widget (g
, groupbox_new (PY
, PX
, BUTTONS_PERM
+ 2, perm_gb_len
, _("Permission")));
345 for (i
= 0; i
< BUTTONS_PERM
; i
++)
347 check_perm
[i
].check
= check_new (PY
+ i
+ 1, PX
+ 2, (ch_mode
& check_perm
[i
].mode
) != 0,
349 group_add_widget (g
, check_perm
[i
].check
);
352 file_gb
= groupbox_new (PY
, PX
+ perm_gb_len
+ 1, BUTTONS_PERM
+ 2, file_gb_len
, _("File"));
353 group_add_widget (g
, file_gb
);
357 cols
= PX
+ perm_gb_len
+ 3;
358 c_fname
= str_trunc (fname
, file_gb_len
- 3);
359 group_add_widget (g
, label_new (y
, cols
, c_fname
));
360 g_snprintf (buffer
, sizeof (buffer
), "%o", (unsigned int) ch_mode
);
361 statl
= label_new (y
+ 2, cols
, buffer
);
362 group_add_widget (g
, statl
);
363 c_fown
= str_trunc (get_owner (sf_stat
->st_uid
), file_gb_len
- 3);
364 group_add_widget (g
, label_new (y
+ 4, cols
, c_fown
));
365 c_fgrp
= str_trunc (get_group (sf_stat
->st_gid
), file_gb_len
- 3);
366 group_add_widget (g
, label_new (y
+ 6, cols
, c_fgrp
));
372 group_add_widget (g
, hline_new (lines
- chmod_but
[i
].y
- 1, -1, -1));
374 for (; i
< BUTTONS
- 2; i
++)
376 y
= lines
- chmod_but
[i
].y
;
377 group_add_widget (g
, button_new (y
, WIDGET (ch_dlg
)->rect
.cols
/ 2 - chmod_but
[i
].len
,
378 chmod_but
[i
].ret_cmd
, chmod_but
[i
].flags
,
379 chmod_but
[i
].text
, NULL
));
381 group_add_widget (g
, button_new (y
, WIDGET (ch_dlg
)->rect
.cols
/ 2 + 1,
382 chmod_but
[i
].ret_cmd
, chmod_but
[i
].flags
,
383 chmod_but
[i
].text
, NULL
));
388 y
= lines
- chmod_but
[i
].y
;
389 group_add_widget (g
, hline_new (y
- 1, -1, -1));
390 group_add_widget (g
, button_new (y
, WIDGET (ch_dlg
)->rect
.cols
/ 2 - chmod_but
[i
].len
,
391 chmod_but
[i
].ret_cmd
, chmod_but
[i
].flags
, chmod_but
[i
].text
,
394 group_add_widget (g
, button_new (y
, WIDGET (ch_dlg
)->rect
.cols
/ 2 + 1, chmod_but
[i
].ret_cmd
,
395 chmod_but
[i
].flags
, chmod_but
[i
].text
, NULL
));
397 /* select first checkbox */
398 widget_select (WIDGET (check_perm
[0].check
));
403 /* --------------------------------------------------------------------------------------------- */
406 chmod_done (gboolean need_update
)
409 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
413 /* --------------------------------------------------------------------------------------------- */
415 static const GString
*
416 next_file (const WPanel
* panel
)
418 while (panel
->dir
.list
[current_file
].f
.marked
== 0)
421 return panel
->dir
.list
[current_file
].fname
;
424 /* --------------------------------------------------------------------------------------------- */
427 try_chmod (const vfs_path_t
* p
, mode_t m
)
429 const char *fname
= NULL
;
431 while (mc_chmod (p
, m
) == -1 && !ignore_all
)
433 int my_errno
= errno
;
438 fname
= x_basename (vfs_path_as_str (p
));
439 msg
= g_strdup_printf (_("Cannot chmod \"%s\"\n%s"), fname
, unix_error_string (my_errno
));
441 query_dialog (MSG_ERROR
, msg
, D_ERROR
, 4, _("&Ignore"), _("Ignore &all"), _("&Retry"),
457 /* retry this file */
462 /* stop remain files processing */
470 /* --------------------------------------------------------------------------------------------- */
473 do_chmod (WPanel
* panel
, const vfs_path_t
* p
, struct stat
*sf
)
477 sf
->st_mode
&= and_mask
;
478 sf
->st_mode
|= or_mask
;
480 ret
= try_chmod (p
, sf
->st_mode
);
482 do_file_mark (panel
, current_file
, 0);
487 /* --------------------------------------------------------------------------------------------- */
490 apply_mask (WPanel
* panel
, vfs_path_t
* vpath
, struct stat
*sf
)
494 if (!do_chmod (panel
, vpath
, sf
))
499 const GString
*fname
;
501 fname
= next_file (panel
);
502 vpath
= vfs_path_from_str (fname
->str
);
503 ok
= (mc_stat (vpath
, sf
) == 0);
507 /* if current file was deleted outside mc -- try next file */
508 /* decrease panel->marked */
509 do_file_mark (panel
, current_file
, 0);
516 ch_mode
= sf
->st_mode
;
518 ok
= do_chmod (panel
, vpath
, sf
);
521 vfs_path_free (vpath
, TRUE
);
523 while (ok
&& panel
->marked
!= 0);
526 /* --------------------------------------------------------------------------------------------- */
527 /*** public functions ****************************************************************************/
528 /* --------------------------------------------------------------------------------------------- */
531 chmod_cmd (WPanel
* panel
)
533 gboolean need_update
;
542 { /* do while any files remaining */
546 const GString
*fname
;
554 if (panel
->marked
!= 0)
555 fname
= next_file (panel
); /* next marked file */
557 fname
= panel_current_entry (panel
)->fname
; /* single file */
559 vpath
= vfs_path_from_str (fname
->str
);
561 if (mc_stat (vpath
, &sf_stat
) != 0)
563 vfs_path_free (vpath
, TRUE
);
567 ch_mode
= sf_stat
.st_mode
;
569 ch_dlg
= chmod_dlg_create (panel
, fname
->str
, &sf_stat
);
570 result
= dlg_run (ch_dlg
);
581 if (panel
->marked
<= 1)
583 /* single or last file */
584 if (mc_chmod (vpath
, ch_mode
) == -1 && !ignore_all
)
585 message (D_ERROR
, MSG_ERROR
, _("Cannot chmod \"%s\"\n%s"), fname
->str
,
586 unix_error_string (errno
));
589 else if (!try_chmod (vpath
, ch_mode
))
591 /* stop multiple files processing */
602 and_mask
= or_mask
= 0;
603 and_mask
= ~and_mask
;
605 for (i
= 0; i
< BUTTONS_PERM
; i
++)
606 if (check_perm
[i
].selected
|| result
== B_SETALL
)
608 if (check_perm
[i
].check
->state
)
609 or_mask
|= check_perm
[i
].mode
;
611 and_mask
&= ~check_perm
[i
].mode
;
614 apply_mask (panel
, vpath
, &sf_stat
);
620 and_mask
= or_mask
= 0;
621 and_mask
= ~and_mask
;
623 for (i
= 0; i
< BUTTONS_PERM
; i
++)
624 if (check_perm
[i
].selected
)
625 or_mask
|= check_perm
[i
].mode
;
627 apply_mask (panel
, vpath
, &sf_stat
);
633 and_mask
= or_mask
= 0;
634 and_mask
= ~and_mask
;
636 for (i
= 0; i
< BUTTONS_PERM
; i
++)
637 if (check_perm
[i
].selected
)
638 and_mask
&= ~check_perm
[i
].mode
;
640 apply_mask (panel
, vpath
, &sf_stat
);
649 if (panel
->marked
!= 0 && result
!= B_CANCEL
)
651 do_file_mark (panel
, current_file
, 0);
655 vfs_path_free (vpath
, TRUE
);
657 widget_destroy (WIDGET (ch_dlg
));
659 while (panel
->marked
!= 0 && !end_chmod
);
661 chmod_done (need_update
);
664 /* --------------------------------------------------------------------------------------------- */