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

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