Internal menu structures are opaque now.
[midnight-commander.git] / src / filemanager / chmod.c
blobb823d13a96347d613ad1a96f87960880754be18d
1 /*
2 Chmod command -- for the Midnight Commander
4 Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007,
5 2008, 2009, 2010, 2011
6 The Free Software Foundation, Inc.
8 This file is part of the Midnight Commander.
10 The Midnight Commander is free software: you can redistribute it
11 and/or modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation, either version 3 of the License,
13 or (at your option) any later version.
15 The Midnight Commander is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 /** \file chmod.c
25 * \brief Source: chmod command
28 #include <config.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
35 #include "lib/global.h"
37 #include "lib/tty/tty.h"
38 #include "lib/skin.h"
39 #include "lib/vfs/vfs.h"
40 #include "lib/strutil.h"
41 #include "lib/util.h"
42 #include "lib/widget.h"
43 #include "lib/keybind.h" /* CK_Cancel */
45 #include "midnight.h" /* current_panel */
46 #include "chmod.h"
48 /*** global variables ****************************************************************************/
50 /*** file scope macro definitions ****************************************************************/
52 #define PX 3
53 #define PY 2
55 #define B_MARKED B_USER
56 #define B_ALL (B_USER + 1)
57 #define B_SETMRK (B_USER + 2)
58 #define B_CLRMRK (B_USER + 3)
60 /*** file scope type declarations ****************************************************************/
62 /*** file scope variables ************************************************************************/
64 static gboolean single_set;
66 static gboolean mode_change, need_update, end_chmod;
67 static int c_file;
69 static mode_t and_mask, or_mask, c_stat;
71 static WLabel *statl;
72 static WGroupbox *file_gb;
74 static struct
76 mode_t mode;
77 const char *text;
78 gboolean selected;
79 WCheck *check;
80 } check_perm[] =
82 /* *INDENT-OFF* */
83 { S_ISUID, N_("set &user ID on execution"), FALSE, NULL },
84 { S_ISGID, N_("set &group ID on execution"), FALSE, NULL },
85 { S_ISVTX, N_("stick&y bit"), FALSE, NULL },
86 { S_IRUSR, N_("&read by owner"), FALSE, NULL },
87 { S_IWUSR, N_("&write by owner"), FALSE, NULL },
88 { S_IXUSR, N_("e&xecute/search by owner"), FALSE, NULL },
89 { S_IRGRP, N_("rea&d by group"), FALSE, NULL },
90 { S_IWGRP, N_("write by grou&p"), FALSE, NULL },
91 { S_IXGRP, N_("execu&te/search by group"), FALSE, NULL },
92 { S_IROTH, N_("read &by others"), FALSE, NULL },
93 { S_IWOTH, N_("wr&ite by others"), FALSE, NULL },
94 { S_IXOTH, N_("execute/searc&h by others"), FALSE, NULL }
95 /* *INDENT-ON* */
98 static const unsigned int check_perm_num = G_N_ELEMENTS (check_perm);
99 static int check_perm_len = 0;
101 static const char *file_info_labels[] = {
102 N_("Name:"),
103 N_("Permissions (octal):"),
104 N_("Owner name:"),
105 N_("Group name:")
108 static const unsigned int file_info_labels_num = G_N_ELEMENTS (file_info_labels);
109 static int file_info_labels_len = 0;
111 static struct
113 int ret_cmd;
114 int flags;
115 int y; /* vertical position relatively to dialog bottom boundary */
116 int len;
117 const char *text;
118 } chmod_but[] =
120 /* *INDENT-OFF* */
121 { B_ALL, NORMAL_BUTTON, 6, 0, N_("Set &all") },
122 { B_MARKED, NORMAL_BUTTON, 6, 0, N_("&Marked all") },
123 { B_SETMRK, NORMAL_BUTTON, 5, 0, N_("S&et marked") },
124 { B_CLRMRK, NORMAL_BUTTON, 5, 0, N_("C&lear marked") },
125 { B_ENTER, DEFPUSH_BUTTON, 3, 0, N_("&Set") },
126 { B_CANCEL, NORMAL_BUTTON, 3, 0, N_("&Cancel") }
127 /* *INDENT-ON* */
130 static const unsigned int chmod_but_num = G_N_ELEMENTS (chmod_but);
132 /* --------------------------------------------------------------------------------------------- */
133 /*** file scope functions ************************************************************************/
134 /* --------------------------------------------------------------------------------------------- */
136 static void
137 chmod_i18n (void)
139 static gboolean i18n = FALSE;
140 unsigned int i;
141 int len;
143 if (i18n)
144 return;
146 i18n = TRUE;
148 #ifdef ENABLE_NLS
149 for (i = 0; i < check_perm_num; i++)
150 check_perm[i].text = _(check_perm[i].text);
152 for (i = 0; i < file_info_labels_num; i++)
153 file_info_labels[i] = _(file_info_labels[i]);
155 for (i = 0; i < chmod_but_num; i++)
156 chmod_but[i].text = _(chmod_but[i].text);
157 #endif /* ENABLE_NLS */
159 for (i = 0; i < check_perm_num; 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 < file_info_labels_num; 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 < chmod_but_num; 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 /* --------------------------------------------------------------------------------------------- */
183 static void
184 chmod_toggle_select (WDialog * h, int Id)
186 tty_setcolor (COLOR_NORMAL);
187 check_perm[Id].selected = !check_perm[Id].selected;
189 widget_move (h, PY + Id + 1, PX + 1);
190 tty_print_char (check_perm[Id].selected ? '*' : ' ');
191 widget_move (h, PY + Id + 1, PX + 3);
194 /* --------------------------------------------------------------------------------------------- */
196 static void
197 chmod_refresh (WDialog * h)
199 int y = WIDGET (file_gb)->y + 1;
200 int x = WIDGET (file_gb)->x + 2;
202 dlg_default_repaint (h);
204 tty_setcolor (COLOR_NORMAL);
206 tty_gotoyx (y, x);
207 tty_print_string (file_info_labels[0]);
208 tty_gotoyx (y + 2, x);
209 tty_print_string (file_info_labels[1]);
210 tty_gotoyx (y + 4, x);
211 tty_print_string (file_info_labels[2]);
212 tty_gotoyx (y + 6, x);
213 tty_print_string (file_info_labels[3]);
216 /* --------------------------------------------------------------------------------------------- */
218 static cb_ret_t
219 chmod_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
221 WDialog *h = DIALOG (w);
223 switch (msg)
225 case MSG_ACTION:
227 /* handle checkboxes */
228 unsigned int i;
230 /* close dialog due to SIGINT (ctrl-g) */
231 if (sender == NULL && parm == CK_Cancel)
232 return MSG_NOT_HANDLED;
234 /* whether action was sent by checkbox? */
235 for (i = 0; i < check_perm_num; i++)
236 if (sender == WIDGET (check_perm[i].check))
237 break;
239 if (i < check_perm_num)
241 char buffer[BUF_TINY];
243 c_stat ^= check_perm[i].mode;
244 g_snprintf (buffer, sizeof (buffer), "%o", (unsigned int) c_stat);
245 label_set_text (statl, buffer);
246 chmod_toggle_select (h, i);
247 mode_change = TRUE;
248 return MSG_HANDLED;
252 return MSG_NOT_HANDLED;
254 case MSG_KEY:
255 if (parm == 'T' || parm == 't' || parm == KEY_IC)
257 unsigned int i;
258 unsigned long id;
260 id = dlg_get_current_widget_id (h);
261 for (i = 0; i < check_perm_num; i++)
262 if (id == WIDGET (check_perm[i].check)->id)
263 break;
265 if (i < check_perm_num)
267 chmod_toggle_select (h, i);
268 if (parm == KEY_IC)
269 dlg_one_down (h);
270 return MSG_HANDLED;
273 return MSG_NOT_HANDLED;
275 case MSG_DRAW:
276 chmod_refresh (h);
277 return MSG_HANDLED;
279 default:
280 return dlg_default_callback (w, sender, msg, parm, data);
284 /* --------------------------------------------------------------------------------------------- */
286 static WDialog *
287 init_chmod (const char *fname, const struct stat *sf_stat)
289 WDialog *ch_dlg;
290 int lines, cols;
291 int y;
292 int perm_gb_len;
293 int file_gb_len;
294 unsigned int i;
295 const char *c_fname, *c_fown, *c_fgrp;
296 char buffer[BUF_TINY];
298 single_set = (current_panel->marked < 2);
299 perm_gb_len = check_perm_len + 2;
300 file_gb_len = file_info_labels_len + 2;
301 cols = str_term_width1 (fname) + 2 + 1;
302 file_gb_len = max (file_gb_len, cols);
304 lines = single_set ? 20 : 23;
305 cols = perm_gb_len + file_gb_len + 1 + 6;
307 if (cols > COLS)
309 /* shrink the right groupbox */
310 cols = COLS;
311 file_gb_len = cols - (perm_gb_len + 1 + 6);
314 ch_dlg =
315 create_dlg (TRUE, 0, 0, lines, cols, dialog_colors,
316 chmod_callback, NULL, "[Chmod]", _("Chmod command"), DLG_CENTER);
318 add_widget (ch_dlg, groupbox_new (PY, PX, check_perm_num + 2, perm_gb_len, _("Permission")));
320 for (i = 0; i < check_perm_num; i++)
322 check_perm[i].check = check_new (PY + i + 1, PX + 2,
323 (c_stat & check_perm[i].mode) != 0 ? 1 : 0,
324 check_perm[i].text);
325 add_widget (ch_dlg, check_perm[i].check);
328 file_gb = groupbox_new (PY, PX + perm_gb_len + 1, check_perm_num + 2, file_gb_len, _("File"));
329 add_widget (ch_dlg, file_gb);
331 /* Set the labels */
332 y = PY + 2;
333 cols = PX + perm_gb_len + 3;
334 c_fname = str_trunc (fname, file_gb_len - 3);
335 add_widget (ch_dlg, label_new (y, cols, c_fname));
336 g_snprintf (buffer, sizeof (buffer), "%o", (unsigned int) c_stat);
337 statl = label_new (y + 2, cols, buffer);
338 add_widget (ch_dlg, statl);
339 c_fown = str_trunc (get_owner (sf_stat->st_uid), file_gb_len - 3);
340 add_widget (ch_dlg, label_new (y + 4, cols, c_fown));
341 c_fgrp = str_trunc (get_group (sf_stat->st_gid), file_gb_len - 3);
342 add_widget (ch_dlg, label_new (y + 6, cols, c_fgrp));
344 if (!single_set)
346 i = 0;
347 add_widget (ch_dlg, hline_new (lines - chmod_but[i].y - 1, -1, -1));
348 for (; i < chmod_but_num - 2; i++)
350 y = lines - chmod_but[i].y;
351 add_widget (ch_dlg,
352 button_new (y, WIDGET (ch_dlg)->cols / 2 - chmod_but[i].len,
353 chmod_but[i].ret_cmd, chmod_but[i].flags, chmod_but[i].text,
354 NULL));
355 i++;
356 add_widget (ch_dlg,
357 button_new (y, WIDGET (ch_dlg)->cols / 2 + 1,
358 chmod_but[i].ret_cmd, chmod_but[i].flags, chmod_but[i].text,
359 NULL));
363 i = chmod_but_num - 2;
364 y = lines - chmod_but[i].y;
365 add_widget (ch_dlg, hline_new (y - 1, -1, -1));
366 add_widget (ch_dlg,
367 button_new (y, WIDGET (ch_dlg)->cols / 2 - chmod_but[i].len, chmod_but[i].ret_cmd,
368 chmod_but[i].flags, chmod_but[i].text, NULL));
369 i++;
370 add_widget (ch_dlg,
371 button_new (y, WIDGET (ch_dlg)->cols / 2 + 1, chmod_but[i].ret_cmd,
372 chmod_but[i].flags, chmod_but[i].text, NULL));
374 /* select first checkbox */
375 dlg_select_widget (check_perm[0].check);
377 return ch_dlg;
380 /* --------------------------------------------------------------------------------------------- */
382 static void
383 chmod_done (void)
385 if (need_update)
386 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
387 repaint_screen ();
390 /* --------------------------------------------------------------------------------------------- */
392 static char *
393 next_file (void)
395 while (!current_panel->dir.list[c_file].f.marked)
396 c_file++;
398 return current_panel->dir.list[c_file].fname;
401 /* --------------------------------------------------------------------------------------------- */
403 static void
404 do_chmod (struct stat *sf)
406 vfs_path_t *vpath;
407 sf->st_mode &= and_mask;
408 sf->st_mode |= or_mask;
410 vpath = vfs_path_from_str (current_panel->dir.list[c_file].fname);
411 if (mc_chmod (vpath, sf->st_mode) == -1)
412 message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"),
413 current_panel->dir.list[c_file].fname, unix_error_string (errno));
415 vfs_path_free (vpath);
416 do_file_mark (current_panel, c_file, 0);
419 /* --------------------------------------------------------------------------------------------- */
421 static void
422 apply_mask (struct stat *sf)
424 need_update = TRUE;
425 end_chmod = TRUE;
427 do_chmod (sf);
431 char *fname;
432 vfs_path_t *vpath;
433 gboolean ok;
435 fname = next_file ();
436 vpath = vfs_path_from_str (fname);
437 ok = (mc_stat (vpath, sf) == 0);
438 vfs_path_free (vpath);
439 if (!ok)
440 return;
442 c_stat = sf->st_mode;
444 do_chmod (sf);
446 while (current_panel->marked != 0);
449 /* --------------------------------------------------------------------------------------------- */
450 /*** public functions ****************************************************************************/
451 /* --------------------------------------------------------------------------------------------- */
453 void
454 chmod_cmd (void)
456 chmod_i18n ();
459 { /* do while any files remaining */
460 vfs_path_t *vpath;
461 WDialog *ch_dlg;
462 struct stat sf_stat;
463 char *fname;
464 int result;
465 unsigned int i;
467 do_refresh ();
469 mode_change = FALSE;
470 need_update = FALSE;
471 end_chmod = FALSE;
472 c_file = 0;
474 if (current_panel->marked != 0)
475 fname = next_file (); /* next marked file */
476 else
477 fname = selection (current_panel)->fname; /* single file */
479 vpath = vfs_path_from_str (fname);
481 if (mc_stat (vpath, &sf_stat) != 0)
483 vfs_path_free (vpath);
484 break;
487 c_stat = sf_stat.st_mode;
489 ch_dlg = init_chmod (fname, &sf_stat);
491 /* do action */
492 result = run_dlg (ch_dlg);
494 switch (result)
496 case B_ENTER:
497 if (mode_change && mc_chmod (vpath, c_stat) == -1)
498 message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"),
499 fname, unix_error_string (errno));
500 need_update = TRUE;
501 break;
503 case B_CANCEL:
504 end_chmod = TRUE;
505 break;
507 case B_ALL:
508 case B_MARKED:
509 and_mask = or_mask = 0;
510 and_mask = ~and_mask;
512 for (i = 0; i < check_perm_num; i++)
513 if (check_perm[i].selected || result == B_ALL)
515 if (check_perm[i].check->state & C_BOOL)
516 or_mask |= check_perm[i].mode;
517 else
518 and_mask &= ~check_perm[i].mode;
521 apply_mask (&sf_stat);
522 break;
524 case B_SETMRK:
525 and_mask = or_mask = 0;
526 and_mask = ~and_mask;
528 for (i = 0; i < check_perm_num; i++)
529 if (check_perm[i].selected)
530 or_mask |= check_perm[i].mode;
532 apply_mask (&sf_stat);
533 break;
535 case B_CLRMRK:
536 and_mask = or_mask = 0;
537 and_mask = ~and_mask;
539 for (i = 0; i < check_perm_num; i++)
540 if (check_perm[i].selected)
541 and_mask &= ~check_perm[i].mode;
543 apply_mask (&sf_stat);
544 break;
547 if (current_panel->marked != 0 && result != B_CANCEL)
549 do_file_mark (current_panel, c_file, 0);
550 need_update = TRUE;
553 vfs_path_free (vpath);
555 destroy_dlg (ch_dlg);
557 while (current_panel->marked != 0 && !end_chmod);
559 chmod_done ();
562 /* --------------------------------------------------------------------------------------------- */