Merge branch '4562_mcedit_macros_paste'
[midnight-commander.git] / src / filemanager / chown.c
blob174b930492e5d6df00b0b3db4edef21cfe3dbbe9
1 /*
2 Chown 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/>.
23 /** \file chown.c
24 * \brief Source: chown command
27 #include <config.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
38 #include "lib/global.h"
40 #include "lib/tty/tty.h"
41 #include "lib/skin.h"
42 #include "lib/vfs/vfs.h"
43 #include "lib/strutil.h"
44 #include "lib/util.h"
45 #include "lib/widget.h"
47 #include "src/setup.h" /* panels_options */
49 #include "cmd.h" /* chown_cmd() */
51 /*** global variables ****************************************************************************/
53 /*** file scope macro definitions ****************************************************************/
55 #define GH 12
56 #define GW 21
58 #define BUTTONS 5
60 #define B_SETALL B_USER
61 #define B_SETUSR (B_USER + 1)
62 #define B_SETGRP (B_USER + 2)
64 #define LABELS 5
66 #define chown_label(n,txt) label_set_text (chown_label [n].l, txt)
68 /*** file scope type declarations ****************************************************************/
70 /*** forward declarations (file scope functions) *************************************************/
72 /*** file scope variables ************************************************************************/
74 static struct
76 int ret_cmd;
77 button_flags_t flags;
78 int y;
79 int len;
80 const char *text;
81 } chown_but[BUTTONS] = {
82 /* *INDENT-OFF* */
83 { B_SETALL, NORMAL_BUTTON, 5, 0, N_("Set &all") },
84 { B_SETGRP, NORMAL_BUTTON, 5, 0, N_("Set &groups") },
85 { B_SETUSR, NORMAL_BUTTON, 5, 0, N_("Set &users") },
86 { B_ENTER, DEFPUSH_BUTTON, 3, 0, N_("&Set") },
87 { B_CANCEL, NORMAL_BUTTON, 3, 0, N_("&Cancel") }
88 /* *INDENT-ON* */
91 /* summary length of three buttons */
92 static int blen = 0;
94 static struct
96 int y;
97 WLabel *l;
98 } chown_label[LABELS] = {
99 /* *INDENT-OFF* */
100 { 4, NULL },
101 { 6, NULL },
102 { 8, NULL },
103 { 10, NULL },
104 { 12, NULL }
105 /* *INDENT-ON* */
108 static int current_file;
109 static gboolean ignore_all;
111 static WListbox *l_user, *l_group;
113 /* --------------------------------------------------------------------------------------------- */
114 /*** file scope functions ************************************************************************/
115 /* --------------------------------------------------------------------------------------------- */
117 static void
118 chown_init (void)
120 static gboolean i18n = FALSE;
121 int i;
123 if (i18n)
124 return;
126 i18n = TRUE;
128 #ifdef ENABLE_NLS
129 for (i = 0; i < BUTTONS; i++)
130 chown_but[i].text = _(chown_but[i].text);
131 #endif /* ENABLE_NLS */
133 for (i = 0; i < BUTTONS; i++)
135 chown_but[i].len = str_term_width1 (chown_but[i].text) + 3; /* [], spaces and w/o & */
136 if (chown_but[i].flags == DEFPUSH_BUTTON)
137 chown_but[i].len += 2; /* <> */
139 if (i < BUTTONS - 2)
140 blen += chown_but[i].len;
143 blen += 2;
146 /* --------------------------------------------------------------------------------------------- */
148 static void
149 chown_refresh (const Widget *h)
151 int y = 3;
152 int x = 7 + GW * 2;
154 tty_setcolor (COLOR_NORMAL);
156 widget_gotoyx (h, y + 0, x);
157 tty_print_string (_("Name"));
158 widget_gotoyx (h, y + 2, x);
159 tty_print_string (_("Owner name"));
160 widget_gotoyx (h, y + 4, x);
161 tty_print_string (_("Group name"));
162 widget_gotoyx (h, y + 6, x);
163 tty_print_string (_("Size"));
164 widget_gotoyx (h, y + 8, x);
165 tty_print_string (_("Permission"));
168 /* --------------------------------------------------------------------------------------------- */
170 static cb_ret_t
171 chown_bg_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
173 switch (msg)
175 case MSG_DRAW:
176 frame_callback (w, NULL, MSG_DRAW, 0, NULL);
177 chown_refresh (WIDGET (w->owner));
178 return MSG_HANDLED;
180 default:
181 return frame_callback (w, sender, msg, parm, data);
185 /* --------------------------------------------------------------------------------------------- */
187 static WDialog *
188 chown_dlg_create (WPanel *panel)
190 int single_set;
191 WDialog *ch_dlg;
192 WGroup *g;
193 int lines, cols;
194 int i, y;
195 struct passwd *l_pass;
196 struct group *l_grp;
198 single_set = (panel->marked < 2) ? 3 : 0;
199 lines = GH + 4 + (single_set != 0 ? 2 : 4);
200 cols = GW * 3 + 2 + 6;
202 ch_dlg =
203 dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, NULL, NULL,
204 "[Chown]", _("Chown command"));
205 g = GROUP (ch_dlg);
207 /* draw background */
208 ch_dlg->bg->callback = chown_bg_callback;
210 group_add_widget (g, groupbox_new (2, 3, GH, GW, _("User name")));
211 l_user = listbox_new (3, 4, GH - 2, GW - 2, FALSE, NULL);
212 group_add_widget (g, l_user);
213 /* add field for unknown names (numbers) */
214 listbox_add_item (l_user, LISTBOX_APPEND_AT_END, 0, _("<Unknown user>"), NULL, FALSE);
215 /* get and put user names in the listbox */
216 setpwent ();
217 while ((l_pass = getpwent ()) != NULL)
218 listbox_add_item (l_user, LISTBOX_APPEND_SORTED, 0, l_pass->pw_name, NULL, FALSE);
219 endpwent ();
221 group_add_widget (g, groupbox_new (2, 4 + GW, GH, GW, _("Group name")));
222 l_group = listbox_new (3, 5 + GW, GH - 2, GW - 2, FALSE, NULL);
223 group_add_widget (g, l_group);
224 /* add field for unknown names (numbers) */
225 listbox_add_item (l_group, LISTBOX_APPEND_AT_END, 0, _("<Unknown group>"), NULL, FALSE);
226 /* get and put group names in the listbox */
227 setgrent ();
228 while ((l_grp = getgrent ()) != NULL)
229 listbox_add_item (l_group, LISTBOX_APPEND_SORTED, 0, l_grp->gr_name, NULL, FALSE);
230 endgrent ();
232 group_add_widget (g, groupbox_new (2, 5 + GW * 2, GH, GW, _("File")));
233 /* add widgets for the file information */
234 for (i = 0; i < LABELS; i++)
236 chown_label[i].l = label_new (chown_label[i].y, 7 + GW * 2, NULL);
237 group_add_widget (g, chown_label[i].l);
240 if (single_set == 0)
242 int x;
244 group_add_widget (g, hline_new (lines - chown_but[0].y - 1, -1, -1));
246 y = lines - chown_but[0].y;
247 x = (cols - blen) / 2;
249 for (i = 0; i < BUTTONS - 2; i++)
251 group_add_widget (g, button_new (y, x, chown_but[i].ret_cmd, chown_but[i].flags,
252 chown_but[i].text, NULL));
253 x += chown_but[i].len + 1;
257 i = BUTTONS - 2;
258 y = lines - chown_but[i].y;
259 group_add_widget (g, hline_new (y - 1, -1, -1));
260 group_add_widget (g, button_new (y, WIDGET (ch_dlg)->rect.cols / 2 - chown_but[i].len,
261 chown_but[i].ret_cmd, chown_but[i].flags, chown_but[i].text,
262 NULL));
263 i++;
264 group_add_widget (g, button_new (y, WIDGET (ch_dlg)->rect.cols / 2 + 1, chown_but[i].ret_cmd,
265 chown_but[i].flags, chown_but[i].text, NULL));
267 /* select first listbox */
268 widget_select (WIDGET (l_user));
270 return ch_dlg;
273 /* --------------------------------------------------------------------------------------------- */
275 static void
276 chown_done (gboolean need_update)
278 if (need_update)
279 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
280 repaint_screen ();
283 /* --------------------------------------------------------------------------------------------- */
285 static const GString *
286 next_file (const WPanel *panel)
288 while (panel->dir.list[current_file].f.marked == 0)
289 current_file++;
291 return panel->dir.list[current_file].fname;
294 /* --------------------------------------------------------------------------------------------- */
296 static gboolean
297 try_chown (const vfs_path_t *p, uid_t u, gid_t g)
299 const char *fname = NULL;
301 while (mc_chown (p, u, g) == -1 && !ignore_all)
303 int my_errno = errno;
304 int result;
305 char *msg;
307 if (fname == NULL)
308 fname = x_basename (vfs_path_as_str (p));
309 msg = g_strdup_printf (_("Cannot chown \"%s\"\n%s"), fname, unix_error_string (my_errno));
310 result =
311 query_dialog (MSG_ERROR, msg, D_ERROR, 4, _("&Ignore"), _("Ignore &all"), _("&Retry"),
312 _("&Cancel"));
313 g_free (msg);
315 switch (result)
317 case 0:
318 /* try next file */
319 return TRUE;
321 case 1:
322 ignore_all = TRUE;
323 /* try next file */
324 return TRUE;
326 case 2:
327 /* retry this file */
328 break;
330 case 3:
331 default:
332 /* stop remain files processing */
333 return FALSE;
337 return TRUE;
340 /* --------------------------------------------------------------------------------------------- */
342 static gboolean
343 do_chown (WPanel *panel, const vfs_path_t *p, uid_t u, gid_t g)
345 gboolean ret;
347 ret = try_chown (p, u, g);
349 do_file_mark (panel, current_file, 0);
351 return ret;
354 /* --------------------------------------------------------------------------------------------- */
356 static void
357 apply_chowns (WPanel *panel, vfs_path_t *vpath, uid_t u, gid_t g)
359 gboolean ok;
361 if (!do_chown (panel, vpath, u, g))
362 return;
366 const GString *fname;
367 struct stat sf;
369 fname = next_file (panel);
370 vpath = vfs_path_from_str (fname->str);
371 ok = (mc_stat (vpath, &sf) == 0);
373 if (!ok)
375 /* if current file was deleted outside mc -- try next file */
376 /* decrease panel->marked */
377 do_file_mark (panel, current_file, 0);
379 /* try next file */
380 ok = TRUE;
382 else
383 ok = do_chown (panel, vpath, u, g);
385 vfs_path_free (vpath, TRUE);
387 while (ok && panel->marked != 0);
390 /* --------------------------------------------------------------------------------------------- */
391 /*** public functions ****************************************************************************/
392 /* --------------------------------------------------------------------------------------------- */
394 void
395 chown_cmd (WPanel *panel)
397 gboolean need_update;
398 gboolean end_chown;
400 chown_init ();
402 current_file = 0;
403 ignore_all = FALSE;
406 { /* do while any files remaining */
407 vfs_path_t *vpath;
408 WDialog *ch_dlg;
409 struct stat sf_stat;
410 const GString *fname;
411 int result;
412 char buffer[BUF_TINY];
413 uid_t new_user = (uid_t) (-1);
414 gid_t new_group = (gid_t) (-1);
416 do_refresh ();
418 need_update = FALSE;
419 end_chown = FALSE;
421 if (panel->marked != 0)
422 fname = next_file (panel); /* next marked file */
423 else
424 fname = panel_current_entry (panel)->fname; /* single file */
426 vpath = vfs_path_from_str (fname->str);
428 if (mc_stat (vpath, &sf_stat) != 0)
430 vfs_path_free (vpath, TRUE);
431 break;
434 ch_dlg = chown_dlg_create (panel);
436 /* select in listboxes */
437 listbox_set_current (l_user, listbox_search_text (l_user, get_owner (sf_stat.st_uid)));
438 listbox_set_current (l_group, listbox_search_text (l_group, get_group (sf_stat.st_gid)));
440 chown_label (0, str_trunc (fname->str, GW - 4));
441 chown_label (1, str_trunc (get_owner (sf_stat.st_uid), GW - 4));
442 chown_label (2, str_trunc (get_group (sf_stat.st_gid), GW - 4));
443 size_trunc_len (buffer, GW - 4, sf_stat.st_size, 0, panels_options.kilobyte_si);
444 chown_label (3, buffer);
445 chown_label (4, string_perm (sf_stat.st_mode));
447 result = dlg_run (ch_dlg);
449 switch (result)
451 case B_CANCEL:
452 end_chown = TRUE;
453 break;
455 case B_ENTER:
456 case B_SETALL:
458 struct group *grp;
459 struct passwd *user;
460 char *text;
462 listbox_get_current (l_group, &text, NULL);
463 grp = getgrnam (text);
464 if (grp != NULL)
465 new_group = grp->gr_gid;
466 listbox_get_current (l_user, &text, NULL);
467 user = getpwnam (text);
468 if (user != NULL)
469 new_user = user->pw_uid;
470 if (result == B_ENTER)
472 if (panel->marked <= 1)
474 /* single or last file */
475 if (mc_chown (vpath, new_user, new_group) == -1)
476 message (D_ERROR, MSG_ERROR, _("Cannot chown \"%s\"\n%s"),
477 fname->str, unix_error_string (errno));
478 end_chown = TRUE;
480 else if (!try_chown (vpath, new_user, new_group))
482 /* stop multiple files processing */
483 result = B_CANCEL;
484 end_chown = TRUE;
487 else
489 apply_chowns (panel, vpath, new_user, new_group);
490 end_chown = TRUE;
493 need_update = TRUE;
494 break;
497 case B_SETUSR:
499 struct passwd *user;
500 char *text;
502 listbox_get_current (l_user, &text, NULL);
503 user = getpwnam (text);
504 if (user != NULL)
506 new_user = user->pw_uid;
507 apply_chowns (panel, vpath, new_user, new_group);
508 need_update = TRUE;
509 end_chown = TRUE;
511 break;
514 case B_SETGRP:
516 struct group *grp;
517 char *text;
519 listbox_get_current (l_group, &text, NULL);
520 grp = getgrnam (text);
521 if (grp != NULL)
523 new_group = grp->gr_gid;
524 apply_chowns (panel, vpath, new_user, new_group);
525 need_update = TRUE;
526 end_chown = TRUE;
528 break;
531 default:
532 break;
535 if (panel->marked != 0 && result != B_CANCEL)
537 do_file_mark (panel, current_file, 0);
538 need_update = TRUE;
541 vfs_path_free (vpath, TRUE);
543 widget_destroy (WIDGET (ch_dlg));
545 while (panel->marked != 0 && !end_chown);
547 chown_done (need_update);
550 /* --------------------------------------------------------------------------------------------- */