2 Chmod command -- for the Midnight Commander
4 Copyright (C) 1994-2020
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 "midnight.h" /* current_panel */
47 /*** global variables ****************************************************************************/
49 /*** file scope macro definitions ****************************************************************/
54 #define B_MARKED B_USER
55 #define B_SETALL (B_USER + 1)
56 #define B_SETMRK (B_USER + 2)
57 #define B_CLRMRK (B_USER + 3)
60 #define BUTTONS_PERM 12
63 /*** file scope type declarations ****************************************************************/
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
;
146 for (i
= 0; i
< BUTTONS_PERM
; i
++)
147 check_perm
[i
].text
= _(check_perm
[i
].text
);
149 for (i
= 0; i
< LABELS
; i
++)
150 file_info_labels
[i
] = _(file_info_labels
[i
]);
152 for (i
= 0; i
< BUTTONS
; i
++)
153 chmod_but
[i
].text
= _(chmod_but
[i
].text
);
154 #endif /* ENABLE_NLS */
156 for (i
= 0; i
< BUTTONS_PERM
; i
++)
158 len
= str_term_width1 (check_perm
[i
].text
);
159 check_perm_len
= MAX (check_perm_len
, len
);
162 check_perm_len
+= 1 + 3 + 1; /* mark, [x] and space */
164 for (i
= 0; i
< LABELS
; i
++)
166 len
= str_term_width1 (file_info_labels
[i
]) + 2; /* spaces around */
167 file_info_labels_len
= MAX (file_info_labels_len
, len
);
170 for (i
= 0; i
< BUTTONS
; i
++)
172 chmod_but
[i
].len
= str_term_width1 (chmod_but
[i
].text
) + 3; /* [], spaces and w/o & */
173 if (chmod_but
[i
].flags
== DEFPUSH_BUTTON
)
174 chmod_but
[i
].len
+= 2; /* <> */
178 /* --------------------------------------------------------------------------------------------- */
181 chmod_toggle_select (WDialog
* h
, int Id
)
183 tty_setcolor (COLOR_NORMAL
);
184 check_perm
[Id
].selected
= !check_perm
[Id
].selected
;
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_refresh (WDialog
* h
)
196 int y
= WIDGET (file_gb
)->y
+ 1;
197 int x
= WIDGET (file_gb
)->x
+ 2;
199 dlg_default_repaint (h
);
201 tty_setcolor (COLOR_NORMAL
);
204 tty_print_string (file_info_labels
[0]);
205 tty_gotoyx (y
+ 2, x
);
206 tty_print_string (file_info_labels
[1]);
207 tty_gotoyx (y
+ 4, x
);
208 tty_print_string (file_info_labels
[2]);
209 tty_gotoyx (y
+ 6, x
);
210 tty_print_string (file_info_labels
[3]);
213 /* --------------------------------------------------------------------------------------------- */
216 chmod_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
218 WDialog
*h
= DIALOG (w
);
228 /* handle checkboxes */
231 /* whether notification was sent by checkbox? */
232 for (i
= 0; i
< BUTTONS_PERM
; i
++)
233 if (sender
== WIDGET (check_perm
[i
].check
))
236 if (i
< BUTTONS_PERM
)
238 char buffer
[BUF_TINY
];
240 ch_mode
^= check_perm
[i
].mode
;
241 g_snprintf (buffer
, sizeof (buffer
), "%o", (unsigned int) ch_mode
);
242 label_set_text (statl
, buffer
);
243 chmod_toggle_select (h
, i
);
249 return MSG_NOT_HANDLED
;
252 if (parm
== 'T' || parm
== 't' || parm
== KEY_IC
)
257 id
= dlg_get_current_widget_id (h
);
258 for (i
= 0; i
< BUTTONS_PERM
; i
++)
259 if (id
== WIDGET (check_perm
[i
].check
)->id
)
262 if (i
< BUTTONS_PERM
)
264 chmod_toggle_select (h
, i
);
266 group_select_next_widget (GROUP (h
));
270 return MSG_NOT_HANDLED
;
273 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
277 /* --------------------------------------------------------------------------------------------- */
280 chmod_init (const char *fname
, const struct stat
*sf_stat
)
289 const char *c_fname
, *c_fown
, *c_fgrp
;
290 char buffer
[BUF_TINY
];
294 single_set
= (current_panel
->marked
< 2);
295 perm_gb_len
= check_perm_len
+ 2;
296 file_gb_len
= file_info_labels_len
+ 2;
297 cols
= str_term_width1 (fname
) + 2 + 1;
298 file_gb_len
= MAX (file_gb_len
, cols
);
300 lines
= single_set
? 20 : 23;
301 cols
= perm_gb_len
+ file_gb_len
+ 1 + 6;
305 /* shrink the right groupbox */
307 file_gb_len
= cols
- (perm_gb_len
+ 1 + 6);
311 dlg_create (TRUE
, 0, 0, lines
, cols
, WPOS_CENTER
, FALSE
, dialog_colors
,
312 chmod_callback
, NULL
, "[Chmod]", _("Chmod command"));
315 group_add_widget (g
, groupbox_new (PY
, PX
, BUTTONS_PERM
+ 2, perm_gb_len
, _("Permission")));
317 for (i
= 0; i
< BUTTONS_PERM
; i
++)
319 check_perm
[i
].check
= check_new (PY
+ i
+ 1, PX
+ 2, (ch_mode
& check_perm
[i
].mode
) != 0,
321 group_add_widget (g
, check_perm
[i
].check
);
324 file_gb
= groupbox_new (PY
, PX
+ perm_gb_len
+ 1, BUTTONS_PERM
+ 2, file_gb_len
, _("File"));
325 group_add_widget (g
, file_gb
);
329 cols
= PX
+ perm_gb_len
+ 3;
330 c_fname
= str_trunc (fname
, file_gb_len
- 3);
331 group_add_widget (g
, label_new (y
, cols
, c_fname
));
332 g_snprintf (buffer
, sizeof (buffer
), "%o", (unsigned int) ch_mode
);
333 statl
= label_new (y
+ 2, cols
, buffer
);
334 group_add_widget (g
, statl
);
335 c_fown
= str_trunc (get_owner (sf_stat
->st_uid
), file_gb_len
- 3);
336 group_add_widget (g
, label_new (y
+ 4, cols
, c_fown
));
337 c_fgrp
= str_trunc (get_group (sf_stat
->st_gid
), file_gb_len
- 3);
338 group_add_widget (g
, label_new (y
+ 6, cols
, c_fgrp
));
344 group_add_widget (g
, hline_new (lines
- chmod_but
[i
].y
- 1, -1, -1));
346 for (; i
< BUTTONS
- 2; i
++)
348 y
= lines
- chmod_but
[i
].y
;
349 group_add_widget (g
, button_new (y
, WIDGET (ch_dlg
)->cols
/ 2 - chmod_but
[i
].len
,
350 chmod_but
[i
].ret_cmd
, chmod_but
[i
].flags
,
351 chmod_but
[i
].text
, NULL
));
353 group_add_widget (g
, button_new (y
, WIDGET (ch_dlg
)->cols
/ 2 + 1,
354 chmod_but
[i
].ret_cmd
, chmod_but
[i
].flags
,
355 chmod_but
[i
].text
, NULL
));
360 y
= lines
- chmod_but
[i
].y
;
361 group_add_widget (g
, hline_new (y
- 1, -1, -1));
362 group_add_widget (g
, button_new (y
, WIDGET (ch_dlg
)->cols
/ 2 - chmod_but
[i
].len
,
363 chmod_but
[i
].ret_cmd
, chmod_but
[i
].flags
, chmod_but
[i
].text
,
366 group_add_widget (g
, button_new (y
, WIDGET (ch_dlg
)->cols
/ 2 + 1, chmod_but
[i
].ret_cmd
,
367 chmod_but
[i
].flags
, chmod_but
[i
].text
, NULL
));
369 /* select first checkbox */
370 widget_select (WIDGET (check_perm
[0].check
));
375 /* --------------------------------------------------------------------------------------------- */
378 chmod_done (gboolean need_update
)
381 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
385 /* --------------------------------------------------------------------------------------------- */
390 while (!current_panel
->dir
.list
[current_file
].f
.marked
)
393 return current_panel
->dir
.list
[current_file
].fname
;
396 /* --------------------------------------------------------------------------------------------- */
399 try_chmod (const vfs_path_t
* p
, mode_t m
)
401 while (mc_chmod (p
, m
) == -1 && !ignore_all
)
403 int my_errno
= errno
;
408 g_strdup_printf (_("Cannot chmod \"%s\"\n%s"), x_basename (vfs_path_as_str (p
)),
409 unix_error_string (my_errno
));
411 query_dialog (MSG_ERROR
, msg
, D_ERROR
, 4, _("&Ignore"), _("Ignore &all"), _("&Retry"),
427 /* retry this file */
432 /* stop remain files processing */
440 /* --------------------------------------------------------------------------------------------- */
443 do_chmod (const vfs_path_t
* p
, struct stat
*sf
)
447 sf
->st_mode
&= and_mask
;
448 sf
->st_mode
|= or_mask
;
450 ret
= try_chmod (p
, sf
->st_mode
);
452 do_file_mark (current_panel
, current_file
, 0);
457 /* --------------------------------------------------------------------------------------------- */
460 apply_mask (vfs_path_t
* vpath
, struct stat
*sf
)
464 if (!do_chmod (vpath
, sf
))
471 fname
= next_file ();
472 vpath
= vfs_path_from_str (fname
);
473 ok
= (mc_stat (vpath
, sf
) == 0);
477 /* if current file was deleted outside mc -- try next file */
478 /* decrease current_panel->marked */
479 do_file_mark (current_panel
, current_file
, 0);
486 ch_mode
= sf
->st_mode
;
488 ok
= do_chmod (vpath
, sf
);
491 vfs_path_free (vpath
);
493 while (ok
&& current_panel
->marked
!= 0);
496 /* --------------------------------------------------------------------------------------------- */
497 /*** public functions ****************************************************************************/
498 /* --------------------------------------------------------------------------------------------- */
503 gboolean need_update
;
512 { /* do while any files remaining */
524 if (current_panel
->marked
!= 0)
525 fname
= next_file (); /* next marked file */
527 fname
= selection (current_panel
)->fname
; /* single file */
529 vpath
= vfs_path_from_str (fname
);
531 if (mc_stat (vpath
, &sf_stat
) != 0)
533 vfs_path_free (vpath
);
537 ch_mode
= sf_stat
.st_mode
;
539 ch_dlg
= chmod_init (fname
, &sf_stat
);
541 result
= dlg_run (ch_dlg
);
552 if (current_panel
->marked
<= 1)
554 /* single or last file */
555 if (mc_chmod (vpath
, ch_mode
) == -1 && !ignore_all
)
556 message (D_ERROR
, MSG_ERROR
, _("Cannot chmod \"%s\"\n%s"), fname
,
557 unix_error_string (errno
));
560 else if (!try_chmod (vpath
, ch_mode
))
562 /* stop multiple files processing */
573 and_mask
= or_mask
= 0;
574 and_mask
= ~and_mask
;
576 for (i
= 0; i
< BUTTONS_PERM
; i
++)
577 if (check_perm
[i
].selected
|| result
== B_SETALL
)
579 if (check_perm
[i
].check
->state
)
580 or_mask
|= check_perm
[i
].mode
;
582 and_mask
&= ~check_perm
[i
].mode
;
585 apply_mask (vpath
, &sf_stat
);
591 and_mask
= or_mask
= 0;
592 and_mask
= ~and_mask
;
594 for (i
= 0; i
< BUTTONS_PERM
; i
++)
595 if (check_perm
[i
].selected
)
596 or_mask
|= check_perm
[i
].mode
;
598 apply_mask (vpath
, &sf_stat
);
604 and_mask
= or_mask
= 0;
605 and_mask
= ~and_mask
;
607 for (i
= 0; i
< BUTTONS_PERM
; i
++)
608 if (check_perm
[i
].selected
)
609 and_mask
&= ~check_perm
[i
].mode
;
611 apply_mask (vpath
, &sf_stat
);
620 if (current_panel
->marked
!= 0 && result
!= B_CANCEL
)
622 do_file_mark (current_panel
, current_file
, 0);
626 vfs_path_free (vpath
);
628 dlg_destroy (ch_dlg
);
630 while (current_panel
->marked
!= 0 && !end_chmod
);
632 chmod_done (need_update
);
635 /* --------------------------------------------------------------------------------------------- */