Clarify text of screen_msg message.
[midnight-commander.git] / src / filemanager / chmod.c
blob715e457800e74710a583e6973c0708e21a78586c
1 /*
2 Chmod command -- for the Midnight Commander
4 Copyright (C) 1994-2016
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 chmod.c
24 * \brief Source: chmod command
27 #include <config.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
34 #include "lib/global.h"
36 #include "lib/tty/tty.h"
37 #include "lib/skin.h"
38 #include "lib/vfs/vfs.h"
39 #include "lib/strutil.h"
40 #include "lib/util.h"
41 #include "lib/widget.h"
42 #include "lib/keybind.h" /* CK_Cancel */
44 #include "midnight.h" /* current_panel */
45 #include "chmod.h"
47 /*** global variables ****************************************************************************/
49 /*** file scope macro definitions ****************************************************************/
51 #define PX 3
52 #define PY 2
54 #define B_MARKED B_USER
55 #define B_ALL (B_USER + 1)
56 #define B_SETMRK (B_USER + 2)
57 #define B_CLRMRK (B_USER + 3)
59 /*** file scope type declarations ****************************************************************/
61 /*** file scope variables ************************************************************************/
63 static gboolean single_set;
65 static gboolean mode_change, need_update, end_chmod;
66 static int c_file;
68 static mode_t and_mask, or_mask, c_stat;
70 static WLabel *statl;
71 static WGroupbox *file_gb;
73 static struct
75 mode_t mode;
76 const char *text;
77 gboolean selected;
78 WCheck *check;
79 } check_perm[] =
81 /* *INDENT-OFF* */
82 { S_ISUID, N_("set &user ID on execution"), FALSE, NULL },
83 { S_ISGID, N_("set &group ID on execution"), FALSE, NULL },
84 { S_ISVTX, N_("stick&y bit"), FALSE, NULL },
85 { S_IRUSR, N_("&read by owner"), FALSE, NULL },
86 { S_IWUSR, N_("&write by owner"), FALSE, NULL },
87 { S_IXUSR, N_("e&xecute/search by owner"), FALSE, NULL },
88 { S_IRGRP, N_("rea&d by group"), FALSE, NULL },
89 { S_IWGRP, N_("write by grou&p"), FALSE, NULL },
90 { S_IXGRP, N_("execu&te/search by group"), FALSE, NULL },
91 { S_IROTH, N_("read &by others"), FALSE, NULL },
92 { S_IWOTH, N_("wr&ite by others"), FALSE, NULL },
93 { S_IXOTH, N_("execute/searc&h by others"), FALSE, NULL }
94 /* *INDENT-ON* */
97 static const int check_perm_num = G_N_ELEMENTS (check_perm);
98 static int check_perm_len = 0;
100 static const char *file_info_labels[] = {
101 N_("Name:"),
102 N_("Permissions (octal):"),
103 N_("Owner name:"),
104 N_("Group name:")
107 static const int file_info_labels_num = G_N_ELEMENTS (file_info_labels);
108 static int file_info_labels_len = 0;
110 static struct
112 int ret_cmd;
113 button_flags_t flags;
114 int y; /* vertical position relatively to dialog bottom boundary */
115 int len;
116 const char *text;
117 } chmod_but[] =
119 /* *INDENT-OFF* */
120 { B_ALL, NORMAL_BUTTON, 6, 0, N_("Set &all") },
121 { B_MARKED, NORMAL_BUTTON, 6, 0, N_("&Marked all") },
122 { B_SETMRK, NORMAL_BUTTON, 5, 0, N_("S&et marked") },
123 { B_CLRMRK, NORMAL_BUTTON, 5, 0, N_("C&lear marked") },
124 { B_ENTER, DEFPUSH_BUTTON, 3, 0, N_("&Set") },
125 { B_CANCEL, NORMAL_BUTTON, 3, 0, N_("&Cancel") }
126 /* *INDENT-ON* */
129 static const int chmod_but_num = G_N_ELEMENTS (chmod_but);
131 /* --------------------------------------------------------------------------------------------- */
132 /*** file scope functions ************************************************************************/
133 /* --------------------------------------------------------------------------------------------- */
135 static void
136 chmod_i18n (void)
138 static gboolean i18n = FALSE;
139 int i, len;
141 if (i18n)
142 return;
144 i18n = TRUE;
146 #ifdef ENABLE_NLS
147 for (i = 0; i < check_perm_num; i++)
148 check_perm[i].text = _(check_perm[i].text);
150 for (i = 0; i < file_info_labels_num; i++)
151 file_info_labels[i] = _(file_info_labels[i]);
153 for (i = 0; i < chmod_but_num; i++)
154 chmod_but[i].text = _(chmod_but[i].text);
155 #endif /* ENABLE_NLS */
157 for (i = 0; i < check_perm_num; i++)
159 len = str_term_width1 (check_perm[i].text);
160 check_perm_len = max (check_perm_len, len);
163 check_perm_len += 1 + 3 + 1; /* mark, [x] and space */
165 for (i = 0; i < file_info_labels_num; i++)
167 len = str_term_width1 (file_info_labels[i]) + 2; /* spaces around */
168 file_info_labels_len = max (file_info_labels_len, len);
171 for (i = 0; i < chmod_but_num; i++)
173 chmod_but[i].len = str_term_width1 (chmod_but[i].text) + 3; /* [], spaces and w/o & */
174 if (chmod_but[i].flags == DEFPUSH_BUTTON)
175 chmod_but[i].len += 2; /* <> */
179 /* --------------------------------------------------------------------------------------------- */
181 static void
182 chmod_toggle_select (WDialog * h, int Id)
184 tty_setcolor (COLOR_NORMAL);
185 check_perm[Id].selected = !check_perm[Id].selected;
187 widget_move (h, PY + Id + 1, PX + 1);
188 tty_print_char (check_perm[Id].selected ? '*' : ' ');
189 widget_move (h, PY + Id + 1, PX + 3);
192 /* --------------------------------------------------------------------------------------------- */
194 static void
195 chmod_refresh (WDialog * h)
197 int y = WIDGET (file_gb)->y + 1;
198 int x = WIDGET (file_gb)->x + 2;
200 dlg_default_repaint (h);
202 tty_setcolor (COLOR_NORMAL);
204 tty_gotoyx (y, x);
205 tty_print_string (file_info_labels[0]);
206 tty_gotoyx (y + 2, x);
207 tty_print_string (file_info_labels[1]);
208 tty_gotoyx (y + 4, x);
209 tty_print_string (file_info_labels[2]);
210 tty_gotoyx (y + 6, x);
211 tty_print_string (file_info_labels[3]);
214 /* --------------------------------------------------------------------------------------------- */
216 static cb_ret_t
217 chmod_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
219 WDialog *h = DIALOG (w);
221 switch (msg)
223 case MSG_NOTIFY:
225 /* handle checkboxes */
226 int i;
228 /* whether notification was sent by checkbox? */
229 for (i = 0; i < check_perm_num; i++)
230 if (sender == WIDGET (check_perm[i].check))
231 break;
233 if (i < check_perm_num)
235 char buffer[BUF_TINY];
237 c_stat ^= check_perm[i].mode;
238 g_snprintf (buffer, sizeof (buffer), "%o", (unsigned int) c_stat);
239 label_set_text (statl, buffer);
240 chmod_toggle_select (h, i);
241 mode_change = TRUE;
242 return MSG_HANDLED;
246 return MSG_NOT_HANDLED;
248 case MSG_KEY:
249 if (parm == 'T' || parm == 't' || parm == KEY_IC)
251 int i;
252 unsigned long id;
254 id = dlg_get_current_widget_id (h);
255 for (i = 0; i < check_perm_num; i++)
256 if (id == WIDGET (check_perm[i].check)->id)
257 break;
259 if (i < check_perm_num)
261 chmod_toggle_select (h, i);
262 if (parm == KEY_IC)
263 dlg_one_down (h);
264 return MSG_HANDLED;
267 return MSG_NOT_HANDLED;
269 case MSG_DRAW:
270 chmod_refresh (h);
271 return MSG_HANDLED;
273 default:
274 return dlg_default_callback (w, sender, msg, parm, data);
278 /* --------------------------------------------------------------------------------------------- */
280 static WDialog *
281 init_chmod (const char *fname, const struct stat *sf_stat)
283 WDialog *ch_dlg;
284 int lines, cols;
285 int i, y;
286 int perm_gb_len;
287 int file_gb_len;
288 const char *c_fname, *c_fown, *c_fgrp;
289 char buffer[BUF_TINY];
291 single_set = (current_panel->marked < 2);
292 perm_gb_len = check_perm_len + 2;
293 file_gb_len = file_info_labels_len + 2;
294 cols = str_term_width1 (fname) + 2 + 1;
295 file_gb_len = max (file_gb_len, cols);
297 lines = single_set ? 20 : 23;
298 cols = perm_gb_len + file_gb_len + 1 + 6;
300 if (cols > COLS)
302 /* shrink the right groupbox */
303 cols = COLS;
304 file_gb_len = cols - (perm_gb_len + 1 + 6);
307 ch_dlg =
308 dlg_create (TRUE, 0, 0, lines, cols, dialog_colors,
309 chmod_callback, NULL, "[Chmod]", _("Chmod command"), DLG_CENTER);
311 add_widget (ch_dlg, groupbox_new (PY, PX, check_perm_num + 2, perm_gb_len, _("Permission")));
313 for (i = 0; i < check_perm_num; i++)
315 check_perm[i].check = check_new (PY + i + 1, PX + 2,
316 (c_stat & check_perm[i].mode) != 0 ? 1 : 0,
317 check_perm[i].text);
318 add_widget (ch_dlg, check_perm[i].check);
321 file_gb = groupbox_new (PY, PX + perm_gb_len + 1, check_perm_num + 2, file_gb_len, _("File"));
322 add_widget (ch_dlg, file_gb);
324 /* Set the labels */
325 y = PY + 2;
326 cols = PX + perm_gb_len + 3;
327 c_fname = str_trunc (fname, file_gb_len - 3);
328 add_widget (ch_dlg, label_new (y, cols, c_fname));
329 g_snprintf (buffer, sizeof (buffer), "%o", (unsigned int) c_stat);
330 statl = label_new (y + 2, cols, buffer);
331 add_widget (ch_dlg, statl);
332 c_fown = str_trunc (get_owner (sf_stat->st_uid), file_gb_len - 3);
333 add_widget (ch_dlg, label_new (y + 4, cols, c_fown));
334 c_fgrp = str_trunc (get_group (sf_stat->st_gid), file_gb_len - 3);
335 add_widget (ch_dlg, label_new (y + 6, cols, c_fgrp));
337 if (!single_set)
339 i = 0;
340 add_widget (ch_dlg, hline_new (lines - chmod_but[i].y - 1, -1, -1));
341 for (; i < chmod_but_num - 2; i++)
343 y = lines - chmod_but[i].y;
344 add_widget (ch_dlg,
345 button_new (y, WIDGET (ch_dlg)->cols / 2 - chmod_but[i].len,
346 chmod_but[i].ret_cmd, chmod_but[i].flags, chmod_but[i].text,
347 NULL));
348 i++;
349 add_widget (ch_dlg,
350 button_new (y, WIDGET (ch_dlg)->cols / 2 + 1,
351 chmod_but[i].ret_cmd, chmod_but[i].flags, chmod_but[i].text,
352 NULL));
356 i = chmod_but_num - 2;
357 y = lines - chmod_but[i].y;
358 add_widget (ch_dlg, hline_new (y - 1, -1, -1));
359 add_widget (ch_dlg,
360 button_new (y, WIDGET (ch_dlg)->cols / 2 - chmod_but[i].len, chmod_but[i].ret_cmd,
361 chmod_but[i].flags, chmod_but[i].text, NULL));
362 i++;
363 add_widget (ch_dlg,
364 button_new (y, WIDGET (ch_dlg)->cols / 2 + 1, chmod_but[i].ret_cmd,
365 chmod_but[i].flags, chmod_but[i].text, NULL));
367 /* select first checkbox */
368 dlg_select_widget (check_perm[0].check);
370 return ch_dlg;
373 /* --------------------------------------------------------------------------------------------- */
375 static void
376 chmod_done (void)
378 if (need_update)
379 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
380 repaint_screen ();
383 /* --------------------------------------------------------------------------------------------- */
385 static char *
386 next_file (void)
388 while (!current_panel->dir.list[c_file].f.marked)
389 c_file++;
391 return current_panel->dir.list[c_file].fname;
394 /* --------------------------------------------------------------------------------------------- */
396 static void
397 do_chmod (struct stat *sf)
399 vfs_path_t *vpath;
400 sf->st_mode &= and_mask;
401 sf->st_mode |= or_mask;
403 vpath = vfs_path_from_str (current_panel->dir.list[c_file].fname);
404 if (mc_chmod (vpath, sf->st_mode) == -1)
405 message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"),
406 current_panel->dir.list[c_file].fname, unix_error_string (errno));
408 vfs_path_free (vpath);
409 do_file_mark (current_panel, c_file, 0);
412 /* --------------------------------------------------------------------------------------------- */
414 static void
415 apply_mask (struct stat *sf)
417 need_update = TRUE;
418 end_chmod = TRUE;
420 do_chmod (sf);
424 char *fname;
425 vfs_path_t *vpath;
426 gboolean ok;
428 fname = next_file ();
429 vpath = vfs_path_from_str (fname);
430 ok = (mc_stat (vpath, sf) == 0);
431 vfs_path_free (vpath);
432 if (!ok)
433 return;
435 c_stat = sf->st_mode;
437 do_chmod (sf);
439 while (current_panel->marked != 0);
442 /* --------------------------------------------------------------------------------------------- */
443 /*** public functions ****************************************************************************/
444 /* --------------------------------------------------------------------------------------------- */
446 void
447 chmod_cmd (void)
449 chmod_i18n ();
452 { /* do while any files remaining */
453 vfs_path_t *vpath;
454 WDialog *ch_dlg;
455 struct stat sf_stat;
456 char *fname;
457 int i, result;
459 do_refresh ();
461 mode_change = FALSE;
462 need_update = FALSE;
463 end_chmod = FALSE;
464 c_file = 0;
466 if (current_panel->marked != 0)
467 fname = next_file (); /* next marked file */
468 else
469 fname = selection (current_panel)->fname; /* single file */
471 vpath = vfs_path_from_str (fname);
473 if (mc_stat (vpath, &sf_stat) != 0)
475 vfs_path_free (vpath);
476 break;
479 c_stat = sf_stat.st_mode;
481 ch_dlg = init_chmod (fname, &sf_stat);
483 /* do action */
484 result = dlg_run (ch_dlg);
486 switch (result)
488 case B_ENTER:
489 if (mode_change && mc_chmod (vpath, c_stat) == -1)
490 message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"),
491 fname, unix_error_string (errno));
492 need_update = TRUE;
493 break;
495 case B_CANCEL:
496 end_chmod = TRUE;
497 break;
499 case B_ALL:
500 case B_MARKED:
501 and_mask = or_mask = 0;
502 and_mask = ~and_mask;
504 for (i = 0; i < check_perm_num; i++)
505 if (check_perm[i].selected || result == B_ALL)
507 if (check_perm[i].check->state & C_BOOL)
508 or_mask |= check_perm[i].mode;
509 else
510 and_mask &= ~check_perm[i].mode;
513 apply_mask (&sf_stat);
514 break;
516 case B_SETMRK:
517 and_mask = or_mask = 0;
518 and_mask = ~and_mask;
520 for (i = 0; i < check_perm_num; i++)
521 if (check_perm[i].selected)
522 or_mask |= check_perm[i].mode;
524 apply_mask (&sf_stat);
525 break;
527 case B_CLRMRK:
528 and_mask = or_mask = 0;
529 and_mask = ~and_mask;
531 for (i = 0; i < check_perm_num; i++)
532 if (check_perm[i].selected)
533 and_mask &= ~check_perm[i].mode;
535 apply_mask (&sf_stat);
536 break;
538 default:
539 break;
542 if (current_panel->marked != 0 && result != B_CANCEL)
544 do_file_mark (current_panel, c_file, 0);
545 need_update = TRUE;
548 vfs_path_free (vpath);
550 dlg_destroy (ch_dlg);
552 while (current_panel->marked != 0 && !end_chmod);
554 chmod_done ();
557 /* --------------------------------------------------------------------------------------------- */