Rename Dlg_head to WDialog.
[midnight-commander.git] / src / filemanager / chmod.c
blobcea3b0022c39be4a5bc1c16ba52e4f803fe768f5
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 (WDialog * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
221 switch (msg)
223 case DLG_ACTION:
225 /* handle checkboxes */
226 unsigned int i;
228 /* close dialog due to SIGINT (ctrl-g) */
229 if (sender == NULL && parm == CK_Cancel)
230 return MSG_NOT_HANDLED;
232 /* whether action was sent by checkbox? */
233 for (i = 0; i < check_perm_num; i++)
234 if (sender == WIDGET (check_perm[i].check))
235 break;
237 if (i < check_perm_num)
239 char buffer[BUF_TINY];
241 c_stat ^= check_perm[i].mode;
242 g_snprintf (buffer, sizeof (buffer), "%o", (unsigned int) c_stat);
243 label_set_text (statl, buffer);
244 chmod_toggle_select (h, i);
245 mode_change = TRUE;
246 return MSG_HANDLED;
250 return MSG_NOT_HANDLED;
252 case DLG_KEY:
253 if (parm == 'T' || parm == 't' || parm == KEY_IC)
255 unsigned int i;
256 unsigned long id;
258 id = dlg_get_current_widget_id (h);
259 for (i = 0; i < check_perm_num; i++)
260 if (id == WIDGET (check_perm[i].check)->id)
261 break;
263 if (i < check_perm_num)
265 chmod_toggle_select (h, i);
266 if (parm == KEY_IC)
267 dlg_one_down (h);
268 return MSG_HANDLED;
271 return MSG_NOT_HANDLED;
273 case DLG_DRAW:
274 chmod_refresh (h);
275 return MSG_HANDLED;
277 default:
278 return dlg_default_callback (h, sender, msg, parm, data);
282 /* --------------------------------------------------------------------------------------------- */
284 static WDialog *
285 init_chmod (const char *fname, const struct stat *sf_stat)
287 WDialog *ch_dlg;
288 int lines, cols;
289 int y;
290 int perm_gb_len;
291 int file_gb_len;
292 unsigned int i;
293 const char *c_fname, *c_fown, *c_fgrp;
294 char buffer[BUF_TINY];
296 single_set = (current_panel->marked < 2);
297 perm_gb_len = check_perm_len + 2;
298 file_gb_len = file_info_labels_len + 2;
299 cols = str_term_width1 (fname) + 2 + 1;
300 file_gb_len = max (file_gb_len, cols);
302 lines = single_set ? 20 : 23;
303 cols = perm_gb_len + file_gb_len + 1 + 6;
305 if (cols > COLS)
307 /* shrink the right groupbox */
308 cols = COLS;
309 file_gb_len = cols - (perm_gb_len + 1 + 6);
312 ch_dlg =
313 create_dlg (TRUE, 0, 0, lines, cols, dialog_colors,
314 chmod_callback, NULL, "[Chmod]", _("Chmod command"), DLG_CENTER);
316 add_widget (ch_dlg, groupbox_new (PY, PX, check_perm_num + 2, perm_gb_len, _("Permission")));
318 for (i = 0; i < check_perm_num; i++)
320 check_perm[i].check = check_new (PY + i + 1, PX + 2,
321 (c_stat & check_perm[i].mode) != 0 ? 1 : 0,
322 check_perm[i].text);
323 add_widget (ch_dlg, check_perm[i].check);
326 file_gb = groupbox_new (PY, PX + perm_gb_len + 1, check_perm_num + 2, file_gb_len, _("File"));
327 add_widget (ch_dlg, file_gb);
329 /* Set the labels */
330 y = PY + 2;
331 cols = PX + perm_gb_len + 3;
332 c_fname = str_trunc (fname, file_gb_len - 3);
333 add_widget (ch_dlg, label_new (y, cols, c_fname));
334 g_snprintf (buffer, sizeof (buffer), "%o", (unsigned int) c_stat);
335 statl = label_new (y + 2, cols, buffer);
336 add_widget (ch_dlg, statl);
337 c_fown = str_trunc (get_owner (sf_stat->st_uid), file_gb_len - 3);
338 add_widget (ch_dlg, label_new (y + 4, cols, c_fown));
339 c_fgrp = str_trunc (get_group (sf_stat->st_gid), file_gb_len - 3);
340 add_widget (ch_dlg, label_new (y + 6, cols, c_fgrp));
342 if (!single_set)
344 i = 0;
345 add_widget (ch_dlg, hline_new (lines - chmod_but[i].y - 1, -1, -1));
346 for (; i < chmod_but_num - 2; i++)
348 y = lines - chmod_but[i].y;
349 add_widget (ch_dlg,
350 button_new (y, WIDGET (ch_dlg)->cols / 2 - chmod_but[i].len,
351 chmod_but[i].ret_cmd, chmod_but[i].flags, chmod_but[i].text,
352 NULL));
353 i++;
354 add_widget (ch_dlg,
355 button_new (y, WIDGET (ch_dlg)->cols / 2 + 1,
356 chmod_but[i].ret_cmd, chmod_but[i].flags, chmod_but[i].text,
357 NULL));
361 i = chmod_but_num - 2;
362 y = lines - chmod_but[i].y;
363 add_widget (ch_dlg, hline_new (y - 1, -1, -1));
364 add_widget (ch_dlg,
365 button_new (y, WIDGET (ch_dlg)->cols / 2 - chmod_but[i].len, chmod_but[i].ret_cmd,
366 chmod_but[i].flags, chmod_but[i].text, NULL));
367 i++;
368 add_widget (ch_dlg,
369 button_new (y, WIDGET (ch_dlg)->cols / 2 + 1, chmod_but[i].ret_cmd,
370 chmod_but[i].flags, chmod_but[i].text, NULL));
372 /* select first checkbox */
373 dlg_select_widget (check_perm[0].check);
375 return ch_dlg;
378 /* --------------------------------------------------------------------------------------------- */
380 static void
381 chmod_done (void)
383 if (need_update)
384 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
385 repaint_screen ();
388 /* --------------------------------------------------------------------------------------------- */
390 static char *
391 next_file (void)
393 while (!current_panel->dir.list[c_file].f.marked)
394 c_file++;
396 return current_panel->dir.list[c_file].fname;
399 /* --------------------------------------------------------------------------------------------- */
401 static void
402 do_chmod (struct stat *sf)
404 vfs_path_t *vpath;
405 sf->st_mode &= and_mask;
406 sf->st_mode |= or_mask;
408 vpath = vfs_path_from_str (current_panel->dir.list[c_file].fname);
409 if (mc_chmod (vpath, sf->st_mode) == -1)
410 message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"),
411 current_panel->dir.list[c_file].fname, unix_error_string (errno));
413 vfs_path_free (vpath);
414 do_file_mark (current_panel, c_file, 0);
417 /* --------------------------------------------------------------------------------------------- */
419 static void
420 apply_mask (struct stat *sf)
422 need_update = TRUE;
423 end_chmod = TRUE;
425 do_chmod (sf);
429 char *fname;
430 vfs_path_t *vpath;
431 gboolean ok;
433 fname = next_file ();
434 vpath = vfs_path_from_str (fname);
435 ok = (mc_stat (vpath, sf) == 0);
436 vfs_path_free (vpath);
437 if (!ok)
438 return;
440 c_stat = sf->st_mode;
442 do_chmod (sf);
444 while (current_panel->marked != 0);
447 /* --------------------------------------------------------------------------------------------- */
448 /*** public functions ****************************************************************************/
449 /* --------------------------------------------------------------------------------------------- */
451 void
452 chmod_cmd (void)
454 chmod_i18n ();
457 { /* do while any files remaining */
458 vfs_path_t *vpath;
459 WDialog *ch_dlg;
460 struct stat sf_stat;
461 char *fname;
462 int result;
463 unsigned int i;
465 do_refresh ();
467 mode_change = FALSE;
468 need_update = FALSE;
469 end_chmod = FALSE;
470 c_file = 0;
472 if (current_panel->marked != 0)
473 fname = next_file (); /* next marked file */
474 else
475 fname = selection (current_panel)->fname; /* single file */
477 vpath = vfs_path_from_str (fname);
479 if (mc_stat (vpath, &sf_stat) != 0)
481 vfs_path_free (vpath);
482 break;
485 c_stat = sf_stat.st_mode;
487 ch_dlg = init_chmod (fname, &sf_stat);
489 /* do action */
490 result = run_dlg (ch_dlg);
492 switch (result)
494 case B_ENTER:
495 if (mode_change && mc_chmod (vpath, c_stat) == -1)
496 message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"),
497 fname, unix_error_string (errno));
498 need_update = TRUE;
499 break;
501 case B_CANCEL:
502 end_chmod = TRUE;
503 break;
505 case B_ALL:
506 case B_MARKED:
507 and_mask = or_mask = 0;
508 and_mask = ~and_mask;
510 for (i = 0; i < check_perm_num; i++)
511 if (check_perm[i].selected || result == B_ALL)
513 if (check_perm[i].check->state & C_BOOL)
514 or_mask |= check_perm[i].mode;
515 else
516 and_mask &= ~check_perm[i].mode;
519 apply_mask (&sf_stat);
520 break;
522 case B_SETMRK:
523 and_mask = or_mask = 0;
524 and_mask = ~and_mask;
526 for (i = 0; i < check_perm_num; i++)
527 if (check_perm[i].selected)
528 or_mask |= check_perm[i].mode;
530 apply_mask (&sf_stat);
531 break;
533 case B_CLRMRK:
534 and_mask = or_mask = 0;
535 and_mask = ~and_mask;
537 for (i = 0; i < check_perm_num; i++)
538 if (check_perm[i].selected)
539 and_mask &= ~check_perm[i].mode;
541 apply_mask (&sf_stat);
542 break;
545 if (current_panel->marked != 0 && result != B_CANCEL)
547 do_file_mark (current_panel, c_file, 0);
548 need_update = TRUE;
551 vfs_path_free (vpath);
553 destroy_dlg (ch_dlg);
555 while (current_panel->marked != 0 && !end_chmod);
557 chmod_done ();
560 /* --------------------------------------------------------------------------------------------- */