root/src/filemanager/chmod.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. chmod_i18n
  2. chmod_toggle_select
  3. chmod_refresh
  4. chmod_bg_callback
  5. chmod_callback
  6. chmod_init
  7. chmod_done
  8. next_file
  9. try_chmod
  10. do_chmod
  11. apply_mask
  12. chmod_cmd

   1 /*
   2    Chmod command -- for the Midnight Commander
   3 
   4    Copyright (C) 1994-2020
   5    Free Software Foundation, Inc.
   6 
   7    This file is part of the Midnight Commander.
   8 
   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.
  13 
  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.
  18 
  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/>.
  21  */
  22 
  23 /** \file chmod.c
  24  *  \brief Source: chmod command
  25  */
  26 
  27 #include <config.h>
  28 
  29 #include <errno.h>
  30 #include <sys/types.h>
  31 #include <sys/stat.h>
  32 #include <unistd.h>
  33 
  34 #include "lib/global.h"
  35 
  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 
  43 #include "midnight.h"           /* current_panel */
  44 
  45 #include "chmod.h"
  46 
  47 /*** global variables ****************************************************************************/
  48 
  49 /*** file scope macro definitions ****************************************************************/
  50 
  51 #define PX 3
  52 #define PY 2
  53 
  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)
  58 
  59 #define BUTTONS      6
  60 #define BUTTONS_PERM 12
  61 #define LABELS       4
  62 
  63 /*** file scope type declarations ****************************************************************/
  64 
  65 /*** file scope variables ************************************************************************/
  66 
  67 static struct
  68 {
  69     mode_t mode;
  70     const char *text;
  71     gboolean selected;
  72     WCheck *check;
  73 } check_perm[BUTTONS_PERM] =
  74 {
  75     /* *INDENT-OFF* */
  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 }
  88     /* *INDENT-ON* */
  89 };
  90 
  91 static int check_perm_len = 0;
  92 
  93 static const char *file_info_labels[LABELS] = {
  94     N_("Name:"),
  95     N_("Permissions (octal):"),
  96     N_("Owner name:"),
  97     N_("Group name:")
  98 };
  99 
 100 static int file_info_labels_len = 0;
 101 
 102 static struct
 103 {
 104     int ret_cmd;
 105     button_flags_t flags;
 106     int y;                      /* vertical position relatively to dialog bottom boundary */
 107     int len;
 108     const char *text;
 109 } chmod_but[BUTTONS] =
 110 {
 111     /* *INDENT-OFF* */
 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")       }
 118     /* *INDENT-ON* */
 119 };
 120 
 121 static gboolean mode_change;
 122 static int current_file;
 123 static gboolean ignore_all;
 124 
 125 static mode_t and_mask, or_mask, ch_mode;
 126 
 127 static WLabel *statl;
 128 static WGroupbox *file_gb;
 129 
 130 /* --------------------------------------------------------------------------------------------- */
 131 /*** file scope functions ************************************************************************/
 132 /* --------------------------------------------------------------------------------------------- */
 133 
 134 static void
 135 chmod_i18n (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 136 {
 137     static gboolean i18n = FALSE;
 138     int i, len;
 139 
 140     if (i18n)
 141         return;
 142 
 143     i18n = TRUE;
 144 
 145 #ifdef ENABLE_NLS
 146     for (i = 0; i < BUTTONS_PERM; i++)
 147         check_perm[i].text = _(check_perm[i].text);
 148 
 149     for (i = 0; i < LABELS; i++)
 150         file_info_labels[i] = _(file_info_labels[i]);
 151 
 152     for (i = 0; i < BUTTONS; i++)
 153         chmod_but[i].text = _(chmod_but[i].text);
 154 #endif /* ENABLE_NLS */
 155 
 156     for (i = 0; i < BUTTONS_PERM; i++)
 157     {
 158         len = str_term_width1 (check_perm[i].text);
 159         check_perm_len = MAX (check_perm_len, len);
 160     }
 161 
 162     check_perm_len += 1 + 3 + 1;        /* mark, [x] and space */
 163 
 164     for (i = 0; i < LABELS; i++)
 165     {
 166         len = str_term_width1 (file_info_labels[i]) + 2;        /* spaces around */
 167         file_info_labels_len = MAX (file_info_labels_len, len);
 168     }
 169 
 170     for (i = 0; i < BUTTONS; i++)
 171     {
 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;      /* <> */
 175     }
 176 }
 177 
 178 /* --------------------------------------------------------------------------------------------- */
 179 
 180 static void
 181 chmod_toggle_select (WDialog * h, int Id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 182 {
 183     tty_setcolor (COLOR_NORMAL);
 184     check_perm[Id].selected = !check_perm[Id].selected;
 185 
 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);
 189 }
 190 
 191 /* --------------------------------------------------------------------------------------------- */
 192 
 193 static void
 194 chmod_refresh (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 195 {
 196     int y = WIDGET (file_gb)->y + 1;
 197     int x = WIDGET (file_gb)->x + 2;
 198 
 199     tty_setcolor (COLOR_NORMAL);
 200 
 201     tty_gotoyx (y, x);
 202     tty_print_string (file_info_labels[0]);
 203     tty_gotoyx (y + 2, x);
 204     tty_print_string (file_info_labels[1]);
 205     tty_gotoyx (y + 4, x);
 206     tty_print_string (file_info_labels[2]);
 207     tty_gotoyx (y + 6, x);
 208     tty_print_string (file_info_labels[3]);
 209 }
 210 
 211 /* --------------------------------------------------------------------------------------------- */
 212 
 213 static cb_ret_t
 214 chmod_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 215 {
 216     switch (msg)
 217     {
 218     case MSG_DRAW:
 219         frame_callback (w, NULL, MSG_DRAW, 0, NULL);
 220         chmod_refresh ();
 221         return MSG_HANDLED;
 222 
 223     default:
 224         return frame_callback (w, sender, msg, parm, data);
 225     }
 226 }
 227 
 228 /* --------------------------------------------------------------------------------------------- */
 229 
 230 static cb_ret_t
 231 chmod_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 232 {
 233     WGroup *g = GROUP (w);
 234     WDialog *h = DIALOG (w);
 235 
 236     switch (msg)
 237     {
 238     case MSG_NOTIFY:
 239         {
 240             /* handle checkboxes */
 241             int i;
 242 
 243             /* whether notification was sent by checkbox? */
 244             for (i = 0; i < BUTTONS_PERM; i++)
 245                 if (sender == WIDGET (check_perm[i].check))
 246                     break;
 247 
 248             if (i < BUTTONS_PERM)
 249             {
 250                 char buffer[BUF_TINY];
 251 
 252                 ch_mode ^= check_perm[i].mode;
 253                 g_snprintf (buffer, sizeof (buffer), "%o", (unsigned int) ch_mode);
 254                 label_set_text (statl, buffer);
 255                 chmod_toggle_select (h, i);
 256                 mode_change = TRUE;
 257                 return MSG_HANDLED;
 258             }
 259         }
 260 
 261         return MSG_NOT_HANDLED;
 262 
 263     case MSG_KEY:
 264         if (parm == 'T' || parm == 't' || parm == KEY_IC)
 265         {
 266             int i;
 267             unsigned long id;
 268 
 269             id = group_get_current_widget_id (g);
 270             for (i = 0; i < BUTTONS_PERM; i++)
 271                 if (id == WIDGET (check_perm[i].check)->id)
 272                     break;
 273 
 274             if (i < BUTTONS_PERM)
 275             {
 276                 chmod_toggle_select (h, i);
 277                 if (parm == KEY_IC)
 278                     group_select_next_widget (g);
 279                 return MSG_HANDLED;
 280             }
 281         }
 282         return MSG_NOT_HANDLED;
 283 
 284     default:
 285         return dlg_default_callback (w, sender, msg, parm, data);
 286     }
 287 }
 288 
 289 /* --------------------------------------------------------------------------------------------- */
 290 
 291 static WDialog *
 292 chmod_init (const char *fname, const struct stat *sf_stat)
     /* [previous][next][first][last][top][bottom][index][help]  */
 293 {
 294     gboolean single_set;
 295     WDialog *ch_dlg;
 296     WGroup *g;
 297     int lines, cols;
 298     int i, y;
 299     int perm_gb_len;
 300     int file_gb_len;
 301     const char *c_fname, *c_fown, *c_fgrp;
 302     char buffer[BUF_TINY];
 303 
 304     mode_change = FALSE;
 305 
 306     single_set = (current_panel->marked < 2);
 307     perm_gb_len = check_perm_len + 2;
 308     file_gb_len = file_info_labels_len + 2;
 309     cols = str_term_width1 (fname) + 2 + 1;
 310     file_gb_len = MAX (file_gb_len, cols);
 311 
 312     lines = single_set ? 20 : 23;
 313     cols = perm_gb_len + file_gb_len + 1 + 6;
 314 
 315     if (cols > COLS)
 316     {
 317         /* shrink the right groupbox */
 318         cols = COLS;
 319         file_gb_len = cols - (perm_gb_len + 1 + 6);
 320     }
 321 
 322     ch_dlg =
 323         dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors,
 324                     chmod_callback, NULL, "[Chmod]", _("Chmod command"));
 325     g = GROUP (ch_dlg);
 326 
 327     /* draw background */
 328     ch_dlg->bg->callback = chmod_bg_callback;
 329 
 330     group_add_widget (g, groupbox_new (PY, PX, BUTTONS_PERM + 2, perm_gb_len, _("Permission")));
 331 
 332     for (i = 0; i < BUTTONS_PERM; i++)
 333     {
 334         check_perm[i].check = check_new (PY + i + 1, PX + 2, (ch_mode & check_perm[i].mode) != 0,
 335                                          check_perm[i].text);
 336         group_add_widget (g, check_perm[i].check);
 337     }
 338 
 339     file_gb = groupbox_new (PY, PX + perm_gb_len + 1, BUTTONS_PERM + 2, file_gb_len, _("File"));
 340     group_add_widget (g, file_gb);
 341 
 342     /* Set the labels */
 343     y = PY + 2;
 344     cols = PX + perm_gb_len + 3;
 345     c_fname = str_trunc (fname, file_gb_len - 3);
 346     group_add_widget (g, label_new (y, cols, c_fname));
 347     g_snprintf (buffer, sizeof (buffer), "%o", (unsigned int) ch_mode);
 348     statl = label_new (y + 2, cols, buffer);
 349     group_add_widget (g, statl);
 350     c_fown = str_trunc (get_owner (sf_stat->st_uid), file_gb_len - 3);
 351     group_add_widget (g, label_new (y + 4, cols, c_fown));
 352     c_fgrp = str_trunc (get_group (sf_stat->st_gid), file_gb_len - 3);
 353     group_add_widget (g, label_new (y + 6, cols, c_fgrp));
 354 
 355     if (!single_set)
 356     {
 357         i = 0;
 358 
 359         group_add_widget (g, hline_new (lines - chmod_but[i].y - 1, -1, -1));
 360 
 361         for (; i < BUTTONS - 2; i++)
 362         {
 363             y = lines - chmod_but[i].y;
 364             group_add_widget (g, button_new (y, WIDGET (ch_dlg)->cols / 2 - chmod_but[i].len,
 365                                              chmod_but[i].ret_cmd, chmod_but[i].flags,
 366                                              chmod_but[i].text, NULL));
 367             i++;
 368             group_add_widget (g, button_new (y, WIDGET (ch_dlg)->cols / 2 + 1,
 369                                              chmod_but[i].ret_cmd, chmod_but[i].flags,
 370                                              chmod_but[i].text, NULL));
 371         }
 372     }
 373 
 374     i = BUTTONS - 2;
 375     y = lines - chmod_but[i].y;
 376     group_add_widget (g, hline_new (y - 1, -1, -1));
 377     group_add_widget (g, button_new (y, WIDGET (ch_dlg)->cols / 2 - chmod_but[i].len,
 378                                      chmod_but[i].ret_cmd, chmod_but[i].flags, chmod_but[i].text,
 379                                      NULL));
 380     i++;
 381     group_add_widget (g, button_new (y, WIDGET (ch_dlg)->cols / 2 + 1, chmod_but[i].ret_cmd,
 382                                      chmod_but[i].flags, chmod_but[i].text, NULL));
 383 
 384     /* select first checkbox */
 385     widget_select (WIDGET (check_perm[0].check));
 386 
 387     return ch_dlg;
 388 }
 389 
 390 /* --------------------------------------------------------------------------------------------- */
 391 
 392 static void
 393 chmod_done (gboolean need_update)
     /* [previous][next][first][last][top][bottom][index][help]  */
 394 {
 395     if (need_update)
 396         update_panels (UP_OPTIMIZE, UP_KEEPSEL);
 397     repaint_screen ();
 398 }
 399 
 400 /* --------------------------------------------------------------------------------------------- */
 401 
 402 static const char *
 403 next_file (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 404 {
 405     while (!current_panel->dir.list[current_file].f.marked)
 406         current_file++;
 407 
 408     return current_panel->dir.list[current_file].fname;
 409 }
 410 
 411 /* --------------------------------------------------------------------------------------------- */
 412 
 413 static gboolean
 414 try_chmod (const vfs_path_t * p, mode_t m)
     /* [previous][next][first][last][top][bottom][index][help]  */
 415 {
 416     while (mc_chmod (p, m) == -1 && !ignore_all)
 417     {
 418         int my_errno = errno;
 419         int result;
 420         char *msg;
 421 
 422         msg =
 423             g_strdup_printf (_("Cannot chmod \"%s\"\n%s"), x_basename (vfs_path_as_str (p)),
 424                              unix_error_string (my_errno));
 425         result =
 426             query_dialog (MSG_ERROR, msg, D_ERROR, 4, _("&Ignore"), _("Ignore &all"), _("&Retry"),
 427                           _("&Cancel"));
 428         g_free (msg);
 429 
 430         switch (result)
 431         {
 432         case 0:
 433             /* try next file */
 434             return TRUE;
 435 
 436         case 1:
 437             ignore_all = TRUE;
 438             /* try next file */
 439             return TRUE;
 440 
 441         case 2:
 442             /* retry this file */
 443             break;
 444 
 445         case 3:
 446         default:
 447             /* stop remain files processing */
 448             return FALSE;
 449         }
 450     }
 451 
 452     return TRUE;
 453 }
 454 
 455 /* --------------------------------------------------------------------------------------------- */
 456 
 457 static gboolean
 458 do_chmod (const vfs_path_t * p, struct stat *sf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 459 {
 460     gboolean ret;
 461 
 462     sf->st_mode &= and_mask;
 463     sf->st_mode |= or_mask;
 464 
 465     ret = try_chmod (p, sf->st_mode);
 466 
 467     do_file_mark (current_panel, current_file, 0);
 468 
 469     return ret;
 470 }
 471 
 472 /* --------------------------------------------------------------------------------------------- */
 473 
 474 static void
 475 apply_mask (vfs_path_t * vpath, struct stat *sf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 476 {
 477     gboolean ok;
 478 
 479     if (!do_chmod (vpath, sf))
 480         return;
 481 
 482     do
 483     {
 484         const char *fname;
 485 
 486         fname = next_file ();
 487         vpath = vfs_path_from_str (fname);
 488         ok = (mc_stat (vpath, sf) == 0);
 489 
 490         if (!ok)
 491         {
 492             /* if current file was deleted outside mc -- try next file */
 493             /* decrease current_panel->marked */
 494             do_file_mark (current_panel, current_file, 0);
 495 
 496             /* try next file */
 497             ok = TRUE;
 498         }
 499         else
 500         {
 501             ch_mode = sf->st_mode;
 502 
 503             ok = do_chmod (vpath, sf);
 504         }
 505 
 506         vfs_path_free (vpath);
 507     }
 508     while (ok && current_panel->marked != 0);
 509 }
 510 
 511 /* --------------------------------------------------------------------------------------------- */
 512 /*** public functions ****************************************************************************/
 513 /* --------------------------------------------------------------------------------------------- */
 514 
 515 void
 516 chmod_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 517 {
 518     gboolean need_update;
 519     gboolean end_chmod;
 520 
 521     chmod_i18n ();
 522 
 523     current_file = 0;
 524     ignore_all = FALSE;
 525 
 526     do
 527     {                           /* do while any files remaining */
 528         vfs_path_t *vpath;
 529         WDialog *ch_dlg;
 530         struct stat sf_stat;
 531         const char *fname;
 532         int i, result;
 533 
 534         do_refresh ();
 535 
 536         need_update = FALSE;
 537         end_chmod = FALSE;
 538 
 539         if (current_panel->marked != 0)
 540             fname = next_file ();       /* next marked file */
 541         else
 542             fname = selection (current_panel)->fname;   /* single file */
 543 
 544         vpath = vfs_path_from_str (fname);
 545 
 546         if (mc_stat (vpath, &sf_stat) != 0)
 547         {
 548             vfs_path_free (vpath);
 549             break;
 550         }
 551 
 552         ch_mode = sf_stat.st_mode;
 553 
 554         ch_dlg = chmod_init (fname, &sf_stat);
 555 
 556         result = dlg_run (ch_dlg);
 557 
 558         switch (result)
 559         {
 560         case B_CANCEL:
 561             end_chmod = TRUE;
 562             break;
 563 
 564         case B_ENTER:
 565             if (mode_change)
 566             {
 567                 if (current_panel->marked <= 1)
 568                 {
 569                     /* single or last file */
 570                     if (mc_chmod (vpath, ch_mode) == -1 && !ignore_all)
 571                         message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"), fname,
 572                                  unix_error_string (errno));
 573                     end_chmod = TRUE;
 574                 }
 575                 else if (!try_chmod (vpath, ch_mode))
 576                 {
 577                     /* stop multiple files processing */
 578                     result = B_CANCEL;
 579                     end_chmod = TRUE;
 580                 }
 581             }
 582 
 583             need_update = TRUE;
 584             break;
 585 
 586         case B_SETALL:
 587         case B_MARKED:
 588             and_mask = or_mask = 0;
 589             and_mask = ~and_mask;
 590 
 591             for (i = 0; i < BUTTONS_PERM; i++)
 592                 if (check_perm[i].selected || result == B_SETALL)
 593                 {
 594                     if (check_perm[i].check->state)
 595                         or_mask |= check_perm[i].mode;
 596                     else
 597                         and_mask &= ~check_perm[i].mode;
 598                 }
 599 
 600             apply_mask (vpath, &sf_stat);
 601             need_update = TRUE;
 602             end_chmod = TRUE;
 603             break;
 604 
 605         case B_SETMRK:
 606             and_mask = or_mask = 0;
 607             and_mask = ~and_mask;
 608 
 609             for (i = 0; i < BUTTONS_PERM; i++)
 610                 if (check_perm[i].selected)
 611                     or_mask |= check_perm[i].mode;
 612 
 613             apply_mask (vpath, &sf_stat);
 614             need_update = TRUE;
 615             end_chmod = TRUE;
 616             break;
 617 
 618         case B_CLRMRK:
 619             and_mask = or_mask = 0;
 620             and_mask = ~and_mask;
 621 
 622             for (i = 0; i < BUTTONS_PERM; i++)
 623                 if (check_perm[i].selected)
 624                     and_mask &= ~check_perm[i].mode;
 625 
 626             apply_mask (vpath, &sf_stat);
 627             need_update = TRUE;
 628             end_chmod = TRUE;
 629             break;
 630 
 631         default:
 632             break;
 633         }
 634 
 635         if (current_panel->marked != 0 && result != B_CANCEL)
 636         {
 637             do_file_mark (current_panel, current_file, 0);
 638             need_update = TRUE;
 639         }
 640 
 641         vfs_path_free (vpath);
 642 
 643         dlg_destroy (ch_dlg);
 644     }
 645     while (current_panel->marked != 0 && !end_chmod);
 646 
 647     chmod_done (need_update);
 648 }
 649 
 650 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */