Manual pages: mcmcdiffmceditmcview

root/src/filemanager/achown.c

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

DEFINITIONS

This source file includes following definitions.
  1. advanced_chown_init
  2. inc_flag_pos
  3. dec_flag_pos
  4. set_perm_by_flags
  5. get_perm
  6. get_mode
  7. update_permissions
  8. update_ownership
  9. print_flags
  10. advanced_chown_refresh
  11. advanced_chown_info_update
  12. update_mode
  13. perm_button_callback
  14. perm_button_mouse_callback
  15. perm_button_new
  16. chl_callback
  17. user_group_button_cb
  18. advanced_chown_bg_callback
  19. advanced_chown_callback
  20. advanced_chown_dlg_create
  21. advanced_chown_done
  22. try_advanced_chown
  23. do_advanced_chown
  24. apply_advanced_chowns
  25. advanced_chown_cmd

   1 /*
   2    Chown-advanced command -- for the Midnight Commander
   3 
   4    Copyright (C) 1994-2025
   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 <https://www.gnu.org/licenses/>.
  21  */
  22 
  23 /** \file achown.c
  24  *  \brief Source: Contains functions for advanced chowning
  25  */
  26 
  27 #include <config.h>
  28 
  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>
  37 
  38 #include "lib/global.h"
  39 
  40 #include "lib/tty/tty.h"
  41 #include "lib/tty/key.h"  // XCTRL and ALT macros
  42 #include "lib/skin.h"
  43 #include "lib/vfs/vfs.h"
  44 #include "lib/strutil.h"
  45 #include "lib/util.h"
  46 #include "lib/widget.h"
  47 
  48 #include "src/util.h"  // file_error_message()
  49 
  50 #include "cmd.h"  // advanced_chown_cmd()
  51 
  52 /*** global variables ****************************************************************************/
  53 
  54 /*** file scope macro definitions ****************************************************************/
  55 
  56 #define BX           5
  57 #define BY           5
  58 
  59 #define BUTTONS      9
  60 #define BUTTONS_PERM 5
  61 
  62 #define B_SETALL     B_USER
  63 #define B_SKIP       (B_USER + 1)
  64 
  65 /*** file scope type declarations ****************************************************************/
  66 
  67 /*** forward declarations (file scope functions) *************************************************/
  68 
  69 /*** file scope variables ************************************************************************/
  70 
  71 static struct
  72 {
  73     unsigned long id;
  74     int ret_cmd;
  75     button_flags_t flags;
  76     int x;
  77     int len;
  78     const char *text;
  79 } advanced_chown_but[BUTTONS] = {
  80     { 0, B_ENTER, NARROW_BUTTON, 3, 0, "   " },
  81     { 0, B_ENTER, NARROW_BUTTON, 11, 0, "   " },
  82     { 0, B_ENTER, NARROW_BUTTON, 19, 0, "   " },
  83     { 0, B_ENTER, NARROW_BUTTON, 29, 0, "" },
  84     { 0, B_ENTER, NARROW_BUTTON, 47, 0, "" },
  85 
  86     { 0, B_SETALL, NORMAL_BUTTON, 0, 0, N_ ("Set &all") },
  87     { 0, B_SKIP, NORMAL_BUTTON, 0, 0, N_ ("S&kip") },
  88     { 0, B_ENTER, DEFPUSH_BUTTON, 0, 0, N_ ("&Set") },
  89     { 0, B_CANCEL, NORMAL_BUTTON, 0, 0, N_ ("&Cancel") },
  90 };
  91 
  92 static int current_file;
  93 static gboolean ignore_all;
  94 
  95 static WButton *b_att[3];          // permission
  96 static WButton *b_user, *b_group;  // owner
  97 static WLabel *l_filename;
  98 static WLabel *l_mode;
  99 
 100 static int flag_pos;
 101 static int x_toggle;
 102 static char ch_flags[11];
 103 static const char ch_perm[] = "rwx";
 104 static mode_t ch_cmode;
 105 static struct stat sf_stat;
 106 
 107 /* --------------------------------------------------------------------------------------------- */
 108 /*** file scope functions ************************************************************************/
 109 /* --------------------------------------------------------------------------------------------- */
 110 
 111 static void
 112 advanced_chown_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 113 {
 114     static gboolean i18n = FALSE;
 115     int i;
 116 
 117     if (i18n)
 118         return;
 119 
 120     i18n = TRUE;
 121 
 122     for (i = BUTTONS_PERM; i < BUTTONS; i++)
 123     {
 124 #ifdef ENABLE_NLS
 125         advanced_chown_but[i].text = _ (advanced_chown_but[i].text);
 126 #endif
 127 
 128         advanced_chown_but[i].len = str_term_width1 (advanced_chown_but[i].text) + 3;
 129         if (advanced_chown_but[i].flags == DEFPUSH_BUTTON)
 130             advanced_chown_but[i].len += 2;  // "<>"
 131     }
 132 }
 133 
 134 /* --------------------------------------------------------------------------------------------- */
 135 
 136 static cb_ret_t
 137 inc_flag_pos (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 138 {
 139     if (flag_pos == 10)
 140     {
 141         flag_pos = 0;
 142         return MSG_NOT_HANDLED;
 143     }
 144 
 145     flag_pos++;
 146 
 147     return flag_pos % 3 == 0 ? MSG_NOT_HANDLED : MSG_HANDLED;
 148 }
 149 
 150 /* --------------------------------------------------------------------------------------------- */
 151 
 152 static cb_ret_t
 153 dec_flag_pos (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 154 {
 155     if (flag_pos == 0)
 156     {
 157         flag_pos = 10;
 158         return MSG_NOT_HANDLED;
 159     }
 160 
 161     flag_pos--;
 162 
 163     return (flag_pos + 1) % 3 == 0 ? MSG_NOT_HANDLED : MSG_HANDLED;
 164 }
 165 
 166 /* --------------------------------------------------------------------------------------------- */
 167 
 168 static void
 169 set_perm_by_flags (char *s, int f_p)
     /* [previous][next][first][last][top][bottom][index][help]  */
 170 {
 171     int i;
 172 
 173     for (i = 0; i < 3; i++)
 174     {
 175         if (ch_flags[f_p + i] == '+')
 176             s[i] = ch_perm[i];
 177         else if (ch_flags[f_p + i] == '-')
 178             s[i] = '-';
 179         else
 180             s[i] = (ch_cmode & (1 << (8 - f_p - i))) != 0 ? ch_perm[i] : '-';
 181     }
 182 }
 183 
 184 /* --------------------------------------------------------------------------------------------- */
 185 
 186 static mode_t
 187 get_perm (char *s, int base)
     /* [previous][next][first][last][top][bottom][index][help]  */
 188 {
 189     mode_t m = 0;
 190 
 191     m |= (s[0] == '-')
 192         ? 0
 193         : ((s[0] == '+') ? (mode_t) (1 << (base + 2)) : (1 << (base + 2)) & ch_cmode);
 194 
 195     m |= (s[1] == '-')
 196         ? 0
 197         : ((s[1] == '+') ? (mode_t) (1 << (base + 1)) : (1 << (base + 1)) & ch_cmode);
 198 
 199     m |= (s[2] == '-') ? 0 : ((s[2] == '+') ? (mode_t) (1 << base) : (1 << base) & ch_cmode);
 200 
 201     return m;
 202 }
 203 
 204 /* --------------------------------------------------------------------------------------------- */
 205 
 206 static mode_t
 207 get_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 208 {
 209     mode_t m;
 210 
 211     m = ch_cmode ^ (ch_cmode & 0777);
 212     m |= get_perm (ch_flags, 6);
 213     m |= get_perm (ch_flags + 3, 3);
 214     m |= get_perm (ch_flags + 6, 0);
 215 
 216     return m;
 217 }
 218 
 219 /* --------------------------------------------------------------------------------------------- */
 220 
 221 static void
 222 update_permissions (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 223 {
 224     set_perm_by_flags (b_att[0]->text.start, 0);
 225     set_perm_by_flags (b_att[1]->text.start, 3);
 226     set_perm_by_flags (b_att[2]->text.start, 6);
 227 }
 228 
 229 /* --------------------------------------------------------------------------------------------- */
 230 
 231 static void
 232 update_ownership (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 233 {
 234     button_set_text (b_user, get_owner (sf_stat.st_uid));
 235     button_set_text (b_group, get_group (sf_stat.st_gid));
 236 }
 237 
 238 /* --------------------------------------------------------------------------------------------- */
 239 
 240 static void
 241 print_flags (const WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 242 {
 243     int i;
 244 
 245     tty_setcolor (COLOR_NORMAL);
 246 
 247     for (i = 0; i < 3; i++)
 248     {
 249         widget_gotoyx (h, BY + 1, advanced_chown_but[0].x + 6 + i);
 250         tty_print_char (ch_flags[i]);
 251     }
 252 
 253     for (i = 0; i < 3; i++)
 254     {
 255         widget_gotoyx (h, BY + 1, advanced_chown_but[1].x + 6 + i);
 256         tty_print_char (ch_flags[i + 3]);
 257     }
 258 
 259     for (i = 0; i < 3; i++)
 260     {
 261         widget_gotoyx (h, BY + 1, advanced_chown_but[2].x + 6 + i);
 262         tty_print_char (ch_flags[i + 6]);
 263     }
 264 
 265     update_permissions ();
 266 
 267     for (i = 0; i < 15; i++)
 268     {
 269         widget_gotoyx (h, BY + 1, advanced_chown_but[3].x + 6 + i);
 270         tty_print_char (ch_flags[9]);
 271     }
 272     for (i = 0; i < 15; i++)
 273     {
 274         widget_gotoyx (h, BY + 1, advanced_chown_but[4].x + 6 + i);
 275         tty_print_char (ch_flags[10]);
 276     }
 277 }
 278 
 279 /* --------------------------------------------------------------------------------------------- */
 280 
 281 static void
 282 advanced_chown_refresh (const WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 283 {
 284     tty_setcolor (COLOR_NORMAL);
 285 
 286     widget_gotoyx (h, BY - 1, advanced_chown_but[0].x + 5);
 287     tty_print_string (_ ("owner"));
 288     widget_gotoyx (h, BY - 1, advanced_chown_but[1].x + 5);
 289     tty_print_string (_ ("group"));
 290     widget_gotoyx (h, BY - 1, advanced_chown_but[2].x + 5);
 291     tty_print_string (_ ("other"));
 292 
 293     widget_gotoyx (h, BY - 1, advanced_chown_but[3].x + 5);
 294     tty_print_string (_ ("owner"));
 295     widget_gotoyx (h, BY - 1, advanced_chown_but[4].x + 5);
 296     tty_print_string (_ ("group"));
 297 
 298     widget_gotoyx (h, BY + 1, 3);
 299     tty_print_string (_ ("Flag"));
 300     print_flags (h);
 301 }
 302 
 303 /* --------------------------------------------------------------------------------------------- */
 304 
 305 static void
 306 advanced_chown_info_update (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 307 {
 308     // mode
 309     label_set_textv (l_mode, _ ("Permissions (octal): %o"), get_mode ());
 310 
 311     // permissions
 312     update_permissions ();
 313 }
 314 
 315 /* --------------------------------------------------------------------------------------------- */
 316 
 317 static void
 318 update_mode (WGroup *g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 319 {
 320     print_flags (DIALOG (g));
 321     advanced_chown_info_update ();
 322     widget_set_state (WIDGET (g->current->data), WST_FOCUSED, TRUE);
 323 }
 324 
 325 /* --------------------------------------------------------------------------------------------- */
 326 
 327 static cb_ret_t
 328 perm_button_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 329 {
 330     WButton *b = BUTTON (w);
 331     WGroup *g = w->owner;
 332     int i = 0;
 333     int f_pos;
 334 
 335     // one of permission buttons
 336     if (b == b_att[0])
 337         f_pos = 0;
 338     else if (b == b_att[1])
 339         f_pos = 1;
 340     else  // if (w == b_att [1]
 341         f_pos = 2;
 342 
 343     switch (msg)
 344     {
 345     case MSG_FOCUS:
 346         if (b->hotpos == -1)
 347             b->hotpos = 0;
 348 
 349         flag_pos = f_pos * 3 + b->hotpos;
 350         return MSG_HANDLED;
 351 
 352     case MSG_KEY:
 353         switch (parm)
 354         {
 355         case '*':
 356             parm = '=';
 357             MC_FALLTHROUGH;
 358 
 359         case '-':
 360         case '=':
 361         case '+':
 362             flag_pos = f_pos * 3 + b->hotpos;
 363             ch_flags[flag_pos] = parm;
 364             update_mode (g);
 365             send_message (w, NULL, MSG_KEY, KEY_RIGHT, NULL);
 366             if (b->hotpos == 2)
 367                 group_select_next_widget (g);
 368             break;
 369 
 370         case XCTRL ('f'):
 371         case KEY_RIGHT:
 372         {
 373             cb_ret_t ret;
 374 
 375             ret = inc_flag_pos ();
 376             b->hotpos = flag_pos % 3;
 377             return ret;
 378         }
 379 
 380         case XCTRL ('b'):
 381         case KEY_LEFT:
 382         {
 383             cb_ret_t ret;
 384 
 385             ret = dec_flag_pos ();
 386             b->hotpos = flag_pos % 3;
 387             return ret;
 388         }
 389 
 390         case 'x':
 391             i++;
 392             MC_FALLTHROUGH;
 393 
 394         case 'w':
 395             i++;
 396             MC_FALLTHROUGH;
 397 
 398         case 'r':
 399             b->hotpos = i;
 400             MC_FALLTHROUGH;
 401 
 402         case ' ':
 403             i = b->hotpos;
 404 
 405             flag_pos = f_pos * 3 + i;
 406             if (b->text.start[flag_pos % 3] == '-')
 407                 ch_flags[flag_pos] = '+';
 408             else
 409                 ch_flags[flag_pos] = '-';
 410             update_mode (w->owner);
 411             break;
 412 
 413         case '4':
 414             i++;
 415             MC_FALLTHROUGH;
 416 
 417         case '2':
 418             i++;
 419             MC_FALLTHROUGH;
 420 
 421         case '1':
 422             b->hotpos = i;
 423             flag_pos = f_pos * 3 + i;
 424             ch_flags[flag_pos] = '=';
 425             update_mode (g);
 426             break;
 427 
 428         default:
 429             break;
 430         }
 431         // continue key handling in the dialog level
 432         return MSG_NOT_HANDLED;
 433 
 434     default:
 435         return button_default_callback (w, sender, msg, parm, data);
 436     }
 437 }
 438 
 439 /* --------------------------------------------------------------------------------------------- */
 440 
 441 static void
 442 perm_button_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 443 {
 444     switch (msg)
 445     {
 446     case MSG_MOUSE_DOWN:
 447         // place cursor on flag that is being modified
 448         BUTTON (w)->hotpos = CLAMP (event->x - 1, 0, 2);
 449         MC_FALLTHROUGH;
 450 
 451     default:
 452         button_mouse_default_callback (w, msg, event);
 453         break;
 454     }
 455 }
 456 
 457 /* --------------------------------------------------------------------------------------------- */
 458 
 459 static WButton *
 460 perm_button_new (int y, int x, int action, button_flags_t flags, const char *text,
     /* [previous][next][first][last][top][bottom][index][help]  */
 461                  bcback_fn callback)
 462 {
 463     WButton *b;
 464     Widget *w;
 465 
 466     // create base button using native API
 467     b = button_new (y, x, action, flags, text, callback);
 468     w = WIDGET (b);
 469 
 470     // we don't want HOTKEY
 471     widget_want_hotkey (w, FALSE);
 472 
 473     w->callback = perm_button_callback;
 474     w->mouse_callback = perm_button_mouse_callback;
 475 
 476     return b;
 477 }
 478 
 479 /* --------------------------------------------------------------------------------------------- */
 480 
 481 static cb_ret_t
 482 chl_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 483 {
 484     switch (msg)
 485     {
 486     case MSG_KEY:
 487         switch (parm)
 488         {
 489         case KEY_LEFT:
 490         case KEY_RIGHT:
 491         {
 492             WDialog *h = DIALOG (w);
 493 
 494             h->ret_value = parm;
 495             dlg_close (h);
 496         }
 497         break;
 498         default:
 499             break;
 500         }
 501         MC_FALLTHROUGH;
 502 
 503     default:
 504         return dlg_default_callback (w, sender, msg, parm, data);
 505     }
 506 }
 507 
 508 /* --------------------------------------------------------------------------------------------- */
 509 
 510 static int
 511 user_group_button_cb (WButton *button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 512 {
 513     Widget *w = WIDGET (button);
 514     int f_pos;
 515     gboolean chl_end;
 516 
 517     (void) action;
 518 
 519     if (button == b_user)
 520         f_pos = BUTTONS_PERM - 2;
 521     else if (button == b_group)
 522         f_pos = BUTTONS_PERM - 1;
 523     else
 524         return 0;  // do nothing
 525 
 526     do
 527     {
 528         WGroup *g = w->owner;
 529         WDialog *h = DIALOG (g);
 530         Widget *wh = WIDGET (h);
 531 
 532         gboolean is_owner = (f_pos == BUTTONS_PERM - 2);
 533         const char *title;
 534         int lxx, b_current;
 535         WDialog *chl_dlg;
 536         WListbox *chl_list;
 537         int result;
 538         int fe;
 539         struct passwd *chl_pass;
 540         struct group *chl_grp;
 541 
 542         chl_end = FALSE;
 543 
 544         if (is_owner)
 545         {
 546             title = _ ("owner");
 547             lxx = WIDGET (b_user)->rect.x + 1;
 548         }
 549         else
 550         {
 551             title = _ ("group");
 552             lxx = WIDGET (b_group)->rect.x + 1;
 553         }
 554 
 555         chl_dlg = dlg_create (TRUE, wh->rect.y - 1, lxx, wh->rect.lines + 2, 17, WPOS_KEEP_DEFAULT,
 556                               TRUE, dialog_colors, chl_callback, NULL, "[Advanced Chown]", title);
 557 
 558         // get new listboxes
 559         chl_list = listbox_new (1, 1, WIDGET (chl_dlg)->rect.lines - 2,
 560                                 WIDGET (chl_dlg)->rect.cols - 2, FALSE, NULL);
 561         listbox_add_item (chl_list, LISTBOX_APPEND_AT_END, 0, "<Unknown>", NULL, FALSE);
 562         if (is_owner)
 563         {
 564             // get and put user names in the listbox
 565             setpwent ();
 566             while ((chl_pass = getpwent ()) != NULL)
 567                 listbox_add_item (chl_list, LISTBOX_APPEND_SORTED, 0, chl_pass->pw_name, NULL,
 568                                   FALSE);
 569             endpwent ();
 570             fe = listbox_search_text (chl_list, get_owner (sf_stat.st_uid));
 571         }
 572         else
 573         {
 574             // get and put group names in the listbox
 575             setgrent ();
 576             while ((chl_grp = getgrent ()) != NULL)
 577                 listbox_add_item (chl_list, LISTBOX_APPEND_SORTED, 0, chl_grp->gr_name, NULL,
 578                                   FALSE);
 579             endgrent ();
 580             fe = listbox_search_text (chl_list, get_group (sf_stat.st_gid));
 581         }
 582 
 583         listbox_set_current (chl_list, fe);
 584 
 585         b_current = chl_list->current;
 586         group_add_widget (GROUP (chl_dlg), chl_list);
 587 
 588         result = dlg_run (chl_dlg);
 589 
 590         if (result != B_CANCEL)
 591         {
 592             if (b_current != chl_list->current)
 593             {
 594                 gboolean ok = FALSE;
 595                 char *text;
 596 
 597                 listbox_get_current (chl_list, &text, NULL);
 598                 if (is_owner)
 599                 {
 600                     chl_pass = getpwnam (text);
 601                     if (chl_pass != NULL)
 602                     {
 603                         sf_stat.st_uid = chl_pass->pw_uid;
 604                         ok = TRUE;
 605                     }
 606                 }
 607                 else
 608                 {
 609                     chl_grp = getgrnam (text);
 610                     if (chl_grp != NULL)
 611                     {
 612                         sf_stat.st_gid = chl_grp->gr_gid;
 613                         ok = TRUE;
 614                     }
 615                 }
 616 
 617                 if (!ok)
 618                     group_select_current_widget (g);
 619                 else
 620                 {
 621                     ch_flags[f_pos + 6] = '+';
 622                     update_ownership ();
 623                     group_select_current_widget (g);
 624                     print_flags (h);
 625                 }
 626             }
 627 
 628             if (result == KEY_LEFT)
 629             {
 630                 if (!is_owner)
 631                     chl_end = TRUE;
 632                 group_select_prev_widget (g);
 633                 f_pos--;
 634             }
 635             else if (result == KEY_RIGHT)
 636             {
 637                 if (is_owner)
 638                     chl_end = TRUE;
 639                 group_select_next_widget (g);
 640                 f_pos++;
 641             }
 642         }
 643 
 644         // Here we used to redraw the window
 645         widget_destroy (WIDGET (chl_dlg));
 646     }
 647     while (chl_end);
 648 
 649     return 0;
 650 }
 651 
 652 /* --------------------------------------------------------------------------------------------- */
 653 
 654 static cb_ret_t
 655 advanced_chown_bg_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 656 {
 657     switch (msg)
 658     {
 659     case MSG_DRAW:
 660         frame_callback (w, NULL, MSG_DRAW, 0, NULL);
 661         advanced_chown_refresh (DIALOG (w->owner));
 662         advanced_chown_info_update ();
 663         return MSG_HANDLED;
 664 
 665     default:
 666         return frame_callback (w, sender, msg, parm, data);
 667     }
 668 }
 669 
 670 /* --------------------------------------------------------------------------------------------- */
 671 
 672 static cb_ret_t
 673 advanced_chown_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 674 {
 675     WGroup *g = GROUP (w);
 676     int i = 0;
 677 
 678     switch (msg)
 679     {
 680     case MSG_KEY:
 681         switch (parm)
 682         {
 683         case ALT ('x'):
 684             i++;
 685             MC_FALLTHROUGH;
 686 
 687         case ALT ('w'):
 688             i++;
 689             MC_FALLTHROUGH;
 690 
 691         case ALT ('r'):
 692             parm = i + 3;
 693             for (i = 0; i < 3; i++)
 694                 ch_flags[i * 3 + parm - 3] = (x_toggle & (1 << parm)) ? '-' : '+';
 695             x_toggle ^= (1 << parm);
 696             update_mode (g);
 697             widget_draw (w);
 698             break;
 699 
 700         case XCTRL ('x'):
 701             i++;
 702             MC_FALLTHROUGH;
 703 
 704         case XCTRL ('w'):
 705             i++;
 706             MC_FALLTHROUGH;
 707 
 708         case XCTRL ('r'):
 709             parm = i;
 710             for (i = 0; i < 3; i++)
 711                 ch_flags[i * 3 + parm] = (x_toggle & (1 << parm)) ? '-' : '+';
 712             x_toggle ^= (1 << parm);
 713             update_mode (g);
 714             widget_draw (w);
 715             break;
 716 
 717         default:
 718             break;
 719         }
 720         return MSG_NOT_HANDLED;
 721 
 722     default:
 723         return dlg_default_callback (w, sender, msg, parm, data);
 724     }
 725 }
 726 
 727 /* --------------------------------------------------------------------------------------------- */
 728 
 729 static WDialog *
 730 advanced_chown_dlg_create (WPanel *panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 731 {
 732     gboolean single_set;
 733     WDialog *ch_dlg;
 734     WGroup *ch_grp;
 735     int lines = 12;
 736     int cols = 74;
 737     int i;
 738     int y;
 739 
 740     memset (ch_flags, '=', 11);
 741     flag_pos = 0;
 742     x_toggle = 070;
 743 
 744     single_set = (panel->marked < 2);
 745     if (!single_set)
 746         lines += 2;
 747 
 748     ch_dlg = dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors,
 749                          advanced_chown_callback, NULL, "[Advanced Chown]",
 750                          _ ("Chown advanced command"));
 751     ch_grp = GROUP (ch_dlg);
 752 
 753     // draw background
 754     ch_dlg->bg->callback = advanced_chown_bg_callback;
 755 
 756     l_filename = label_new (2, 3, NULL);
 757     group_add_widget (ch_grp, l_filename);
 758 
 759     group_add_widget (ch_grp, hline_new (3, -1, -1));
 760 
 761 #define XTRACT(i, y, cb)                                                                           \
 762     y, BX + advanced_chown_but[i].x, advanced_chown_but[i].ret_cmd, advanced_chown_but[i].flags,   \
 763         (advanced_chown_but[i].text), cb
 764     b_att[0] = perm_button_new (XTRACT (0, BY, NULL));
 765     advanced_chown_but[0].id = group_add_widget (ch_grp, b_att[0]);
 766     b_att[1] = perm_button_new (XTRACT (1, BY, NULL));
 767     advanced_chown_but[1].id = group_add_widget (ch_grp, b_att[1]);
 768     b_att[2] = perm_button_new (XTRACT (2, BY, NULL));
 769     advanced_chown_but[2].id = group_add_widget (ch_grp, b_att[2]);
 770     b_user = button_new (XTRACT (3, BY, user_group_button_cb));
 771     advanced_chown_but[3].id = group_add_widget (ch_grp, b_user);
 772     b_group = button_new (XTRACT (4, BY, user_group_button_cb));
 773     advanced_chown_but[4].id = group_add_widget (ch_grp, b_group);
 774 
 775     l_mode = label_new (BY + 2, 3, NULL);
 776     group_add_widget (ch_grp, l_mode);
 777 
 778     y = BY + 3;
 779     if (!single_set)
 780     {
 781         i = BUTTONS_PERM;
 782         group_add_widget (ch_grp, hline_new (y++, -1, -1));
 783         advanced_chown_but[i].id = group_add_widget (
 784             ch_grp,
 785             button_new (y, WIDGET (ch_dlg)->rect.cols / 2 - advanced_chown_but[i].len,
 786                         advanced_chown_but[i].ret_cmd, advanced_chown_but[i].flags,
 787                         advanced_chown_but[i].text, NULL));
 788         i++;
 789         advanced_chown_but[i].id = group_add_widget (
 790             ch_grp,
 791             button_new (y, WIDGET (ch_dlg)->rect.cols / 2 + 1, advanced_chown_but[i].ret_cmd,
 792                         advanced_chown_but[i].flags, advanced_chown_but[i].text, NULL));
 793         y++;
 794     }
 795 
 796     i = BUTTONS_PERM + 2;
 797     group_add_widget (ch_grp, hline_new (y++, -1, -1));
 798     advanced_chown_but[i].id =
 799         group_add_widget (ch_grp,
 800                           button_new (y, WIDGET (ch_dlg)->rect.cols / 2 - advanced_chown_but[i].len,
 801                                       advanced_chown_but[i].ret_cmd, advanced_chown_but[i].flags,
 802                                       advanced_chown_but[i].text, NULL));
 803     i++;
 804     advanced_chown_but[i].id = group_add_widget (
 805         ch_grp,
 806         button_new (y, WIDGET (ch_dlg)->rect.cols / 2 + 1, advanced_chown_but[i].ret_cmd,
 807                     advanced_chown_but[i].flags, advanced_chown_but[i].text, NULL));
 808 
 809     widget_select (WIDGET (b_att[0]));
 810 
 811     return ch_dlg;
 812 }
 813 
 814 /* --------------------------------------------------------------------------------------------- */
 815 
 816 static void
 817 advanced_chown_done (gboolean need_update)
     /* [previous][next][first][last][top][bottom][index][help]  */
 818 {
 819     if (need_update)
 820         update_panels (UP_OPTIMIZE, UP_KEEPSEL);
 821     repaint_screen ();
 822 }
 823 
 824 /* --------------------------------------------------------------------------------------------- */
 825 
 826 static gboolean
 827 try_advanced_chown (const vfs_path_t *p, mode_t m, uid_t u, gid_t g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 828 {
 829     int chmod_result;
 830     const char *fname = NULL;
 831 
 832     while ((chmod_result = mc_chmod (p, m)) == -1 && !ignore_all)
 833     {
 834         int my_errno = errno;
 835 
 836         if (fname == NULL)
 837             fname = x_basename (vfs_path_as_str (p));
 838 
 839         errno = my_errno;  // restore errno for file_error(
 840 
 841         switch (file_error (NULL, TRUE, _ ("Cannot chmod\n%sn%s"), fname))
 842         {
 843         case FILE_IGNORE:
 844             // call mc_chown() only, if mc_chmod() didn't fail
 845             return TRUE;
 846 
 847         case FILE_IGNORE_ALL:
 848             ignore_all = TRUE;
 849             // call mc_chown() only, if mc_chmod() didn't fail
 850             return TRUE;
 851 
 852         case FILE_RETRY:
 853             // retry chmod of this file
 854             break;
 855 
 856         case FILE_ABORT:
 857         default:
 858             // stop remain files processing
 859             return FALSE;
 860         }
 861     }
 862 
 863     // call mc_chown() only, if mc_chmod didn't fail
 864     while (chmod_result != -1 && mc_chown (p, u, g) == -1 && !ignore_all)
 865     {
 866         int my_errno = errno;
 867 
 868         if (fname == NULL)
 869             fname = x_basename (vfs_path_as_str (p));
 870 
 871         errno = my_errno;  // restore errno for file_error(
 872 
 873         switch (file_error (NULL, TRUE, _ ("Cannot chown\n%sn%s"), fname))
 874         {
 875         case FILE_IGNORE:
 876             // try next file
 877             return TRUE;
 878 
 879         case FILE_IGNORE_ALL:
 880             ignore_all = TRUE;
 881             // try next file
 882             return TRUE;
 883 
 884         case FILE_RETRY:
 885             // retry chown of this file
 886             break;
 887 
 888         case FILE_ABORT:
 889         default:
 890             // stop remain files processing
 891             return FALSE;
 892         }
 893     }
 894 
 895     return TRUE;
 896 }
 897 
 898 /* --------------------------------------------------------------------------------------------- */
 899 
 900 static gboolean
 901 do_advanced_chown (WPanel *panel, const vfs_path_t *p, mode_t m, uid_t u, gid_t g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 902 {
 903     gboolean ret;
 904 
 905     ret = try_advanced_chown (p, m, u, g);
 906 
 907     do_file_mark (panel, current_file, 0);
 908 
 909     return ret;
 910 }
 911 
 912 /* --------------------------------------------------------------------------------------------- */
 913 
 914 static void
 915 apply_advanced_chowns (WPanel *panel, vfs_path_t *vpath, struct stat *sf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 916 {
 917     gid_t a_gid = sf->st_gid;
 918     uid_t a_uid = sf->st_uid;
 919     gboolean ok;
 920 
 921     if (!do_advanced_chown (panel, vpath, get_mode (), (ch_flags[9] == '+') ? a_uid : (uid_t) (-1),
 922                             (ch_flags[10] == '+') ? a_gid : (gid_t) (-1)))
 923         return;
 924 
 925     do
 926     {
 927         const GString *fname;
 928 
 929         fname = panel_find_marked_file (panel, &current_file);
 930         vpath = vfs_path_from_str (fname->str);
 931         ok = (mc_stat (vpath, sf) == 0);
 932 
 933         if (!ok)
 934         {
 935             // if current file was deleted outside mc -- try next file
 936             // decrease panel->marked
 937             do_file_mark (panel, current_file, 0);
 938 
 939             // try next file
 940             ok = TRUE;
 941         }
 942         else
 943         {
 944             ch_cmode = sf->st_mode;
 945 
 946             ok = do_advanced_chown (panel, vpath, get_mode (),
 947                                     (ch_flags[9] == '+') ? a_uid : (uid_t) (-1),
 948                                     (ch_flags[10] == '+') ? a_gid : (gid_t) (-1));
 949         }
 950 
 951         vfs_path_free (vpath, TRUE);
 952     }
 953     while (ok && panel->marked != 0);
 954 }
 955 
 956 /* --------------------------------------------------------------------------------------------- */
 957 /*** public functions ****************************************************************************/
 958 /* --------------------------------------------------------------------------------------------- */
 959 
 960 void
 961 advanced_chown_cmd (WPanel *panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 962 {
 963     gboolean need_update;
 964     gboolean end_chown;
 965 
 966     // Number of files at startup
 967     int files_on_begin;
 968 
 969     files_on_begin = MAX (1, panel->marked);
 970 
 971     advanced_chown_init ();
 972 
 973     current_file = 0;
 974     ignore_all = FALSE;
 975 
 976     do
 977     {  // do while any files remaining
 978         vfs_path_t *vpath;
 979         WDialog *ch_dlg;
 980         const GString *fname;
 981         int result;
 982         int file_idx;
 983 
 984         do_refresh ();
 985 
 986         need_update = FALSE;
 987         end_chown = FALSE;
 988 
 989         fname = panel_get_marked_file (panel, &current_file);
 990         if (fname == NULL)
 991             break;
 992 
 993         vpath = vfs_path_from_str (fname->str);
 994 
 995         if (mc_stat (vpath, &sf_stat) != 0)
 996         {
 997             vfs_path_free (vpath, TRUE);
 998             break;
 999         }
1000 
1001         ch_cmode = sf_stat.st_mode;
1002 
1003         ch_dlg = advanced_chown_dlg_create (panel);
1004 
1005         file_idx = files_on_begin == 1 ? 1 : (files_on_begin - panel->marked + 1);
1006         label_set_textv (l_filename, "%s (%d/%d)",
1007                          str_fit_to_term (fname->str, WIDGET (ch_dlg)->rect.cols - 20, J_LEFT_FIT),
1008                          file_idx, files_on_begin);
1009         update_ownership ();
1010 
1011         result = dlg_run (ch_dlg);
1012 
1013         switch (result)
1014         {
1015         case B_CANCEL:
1016             end_chown = TRUE;
1017             break;
1018 
1019         case B_ENTER:
1020         {
1021             uid_t uid = ch_flags[9] == '+' ? sf_stat.st_uid : (uid_t) (-1);
1022             gid_t gid = ch_flags[10] == '+' ? sf_stat.st_gid : (gid_t) (-1);
1023 
1024             if (panel->marked <= 1)
1025             {
1026                 // single or last file
1027                 if (mc_chmod (vpath, get_mode ()) == -1)
1028                     file_error_message (_ ("Cannot chmod\n%s"), fname->str);
1029                 // call mc_chown only, if mc_chmod didn't fail
1030                 else if (mc_chown (vpath, uid, gid) == -1)
1031                     file_error_message (_ ("Cannot chown\n%s"), fname->str);
1032 
1033                 end_chown = TRUE;
1034             }
1035             else if (!try_advanced_chown (vpath, get_mode (), uid, gid))
1036             {
1037                 // stop multiple files processing
1038                 result = B_CANCEL;
1039                 end_chown = TRUE;
1040             }
1041 
1042             need_update = TRUE;
1043             break;
1044         }
1045 
1046         case B_SETALL:
1047             apply_advanced_chowns (panel, vpath, &sf_stat);
1048             need_update = TRUE;
1049             end_chown = TRUE;
1050             break;
1051 
1052         case B_SKIP:
1053         default:
1054             break;
1055         }
1056 
1057         if (panel->marked != 0 && result != B_CANCEL)
1058         {
1059             do_file_mark (panel, current_file, 0);
1060             need_update = TRUE;
1061         }
1062 
1063         vfs_path_free (vpath, TRUE);
1064 
1065         widget_destroy (WIDGET (ch_dlg));
1066     }
1067     while (panel->marked != 0 && !end_chown);
1068 
1069     advanced_chown_done (need_update);
1070 }
1071 
1072 /* --------------------------------------------------------------------------------------------- */

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