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. next_file
  23. try_advanced_chown
  24. do_advanced_chown
  25. apply_advanced_chowns
  26. advanced_chown_cmd

   1 /*
   2    Chown-advanced command -- for the Midnight Commander
   3 
   4    Copyright (C) 1994-2021
   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 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 /*** file scope variables ************************************************************************/
  66 
  67 static struct
  68 {
  69     unsigned long id;
  70     int ret_cmd;
  71     button_flags_t flags;
  72     int x;
  73     int len;
  74     const char *text;
  75 } advanced_chown_but[BUTTONS] =
  76 {
  77     /* *INDENT-OFF* */
  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     /* *INDENT-ON* */
  89 };
  90 
  91 static int current_file;
  92 static gboolean ignore_all;
  93 
  94 static WButton *b_att[3];       /* permission */
  95 static WButton *b_user, *b_group;       /* owner */
  96 static WLabel *l_filename;
  97 static WLabel *l_mode;
  98 
  99 static int flag_pos;
 100 static int x_toggle;
 101 static char ch_flags[11];
 102 static const char ch_perm[] = "rwx";
 103 static mode_t ch_cmode;
 104 static struct stat sf_stat;
 105 
 106 /* --------------------------------------------------------------------------------------------- */
 107 /*** file scope functions ************************************************************************/
 108 /* --------------------------------------------------------------------------------------------- */
 109 
 110 static void
 111 advanced_chown_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 112 {
 113     static gboolean i18n = FALSE;
 114     int i;
 115 
 116     if (i18n)
 117         return;
 118 
 119     i18n = TRUE;
 120 
 121     for (i = BUTTONS_PERM; i < BUTTONS; i++)
 122     {
 123 #ifdef ENABLE_NLS
 124         advanced_chown_but[i].text = _(advanced_chown_but[i].text);
 125 #endif /* ENABLE_NLS */
 126 
 127         advanced_chown_but[i].len = str_term_width1 (advanced_chown_but[i].text) + 3;
 128         if (advanced_chown_but[i].flags == DEFPUSH_BUTTON)
 129             advanced_chown_but[i].len += 2;     /* "<>" */
 130     }
 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] == '-') ? 0 :
 192         ((s[0] == '+') ? (mode_t) (1 << (base + 2)) : (1 << (base + 2)) & ch_cmode);
 193 
 194     m |= (s[1] == '-') ? 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_stop (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_pos;
 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)->x + 1;
 546         }
 547         else
 548         {
 549             title = _("group");
 550             lxx = WIDGET (b_group)->x + 1;
 551         }
 552 
 553         chl_dlg =
 554             dlg_create (TRUE, wh->y - 1, lxx, wh->lines + 2, 17, WPOS_KEEP_DEFAULT, TRUE,
 555                         dialog_colors, chl_callback, NULL, "[Advanced Chown]", title);
 556 
 557         /* get new listboxes */
 558         chl_list =
 559             listbox_new (1, 1, WIDGET (chl_dlg)->lines - 2, WIDGET (chl_dlg)->cols - 2, FALSE,
 560                          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_select_entry (chl_list, fe);
 584 
 585         b_pos = chl_list->pos;
 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_pos != chl_list->pos)
 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         dlg_destroy (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 =
 749         dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors,
 750                     advanced_chown_callback, NULL, "[Advanced Chown]", _("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, "");
 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) y, BX+advanced_chown_but[i].x, \
 762         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, "");
 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 (ch_grp,
 784                                                      button_new (y,
 785                                                                  WIDGET (ch_dlg)->cols / 2 -
 786                                                                  advanced_chown_but[i].len,
 787                                                                  advanced_chown_but[i].ret_cmd,
 788                                                                  advanced_chown_but[i].flags,
 789                                                                  advanced_chown_but[i].text, NULL));
 790         i++;
 791         advanced_chown_but[i].id = group_add_widget (ch_grp,
 792                                                      button_new (y, WIDGET (ch_dlg)->cols / 2 + 1,
 793                                                                  advanced_chown_but[i].ret_cmd,
 794                                                                  advanced_chown_but[i].flags,
 795                                                                  advanced_chown_but[i].text, NULL));
 796         y++;
 797     }
 798 
 799     i = BUTTONS_PERM + 2;
 800     group_add_widget (ch_grp, hline_new (y++, -1, -1));
 801     advanced_chown_but[i].id = group_add_widget (ch_grp,
 802                                                  button_new (y,
 803                                                              WIDGET (ch_dlg)->cols / 2 -
 804                                                              advanced_chown_but[i].len,
 805                                                              advanced_chown_but[i].ret_cmd,
 806                                                              advanced_chown_but[i].flags,
 807                                                              advanced_chown_but[i].text, NULL));
 808     i++;
 809     advanced_chown_but[i].id = group_add_widget (ch_grp,
 810                                                  button_new (y, WIDGET (ch_dlg)->cols / 2 + 1,
 811                                                              advanced_chown_but[i].ret_cmd,
 812                                                              advanced_chown_but[i].flags,
 813                                                              advanced_chown_but[i].text, NULL));
 814 
 815     widget_select (WIDGET (b_att[0]));
 816 
 817     return ch_dlg;
 818 }
 819 
 820 /* --------------------------------------------------------------------------------------------- */
 821 
 822 static void
 823 advanced_chown_done (gboolean need_update)
     /* [previous][next][first][last][top][bottom][index][help]  */
 824 {
 825     if (need_update)
 826         update_panels (UP_OPTIMIZE, UP_KEEPSEL);
 827     repaint_screen ();
 828 }
 829 
 830 /* --------------------------------------------------------------------------------------------- */
 831 
 832 static const GString *
 833 next_file (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 834 {
 835     while (!panel->dir.list[current_file].f.marked)
 836         current_file++;
 837 
 838     return panel->dir.list[current_file].fname;
 839 }
 840 
 841 /* --------------------------------------------------------------------------------------------- */
 842 
 843 static gboolean
 844 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]  */
 845 {
 846     int chmod_result;
 847     const char *fname;
 848 
 849     fname = x_basename (vfs_path_as_str (p));
 850 
 851     while ((chmod_result = mc_chmod (p, m)) == -1 && !ignore_all)
 852     {
 853         int my_errno = errno;
 854         int result;
 855         char *msg;
 856 
 857         msg = g_strdup_printf (_("Cannot chmod \"%s\"\n%s"), fname, unix_error_string (my_errno));
 858         result =
 859             query_dialog (MSG_ERROR, msg, D_ERROR, 4, _("&Ignore"), _("Ignore &all"), _("&Retry"),
 860                           _("&Cancel"));
 861         g_free (msg);
 862 
 863         switch (result)
 864         {
 865         case 0:
 866             /* call mc_chown() only, if mc_chmod() didn't fail */
 867             return TRUE;
 868 
 869         case 1:
 870             ignore_all = TRUE;
 871             /* call mc_chown() only, if mc_chmod() didn't fail */
 872             return TRUE;
 873 
 874         case 2:
 875             /* retry chmod of this file */
 876             break;
 877 
 878         case 3:
 879         default:
 880             /* stop remain files processing */
 881             return FALSE;
 882         }
 883     }
 884 
 885     /* call mc_chown() only, if mc_chmod didn't fail */
 886     while (chmod_result != -1 && mc_chown (p, u, g) == -1 && !ignore_all)
 887     {
 888         int my_errno = errno;
 889         int result;
 890         char *msg;
 891 
 892         msg = g_strdup_printf (_("Cannot chown \"%s\"\n%s"), fname, unix_error_string (my_errno));
 893         result =
 894             query_dialog (MSG_ERROR, msg, D_ERROR, 4, _("&Ignore"), _("Ignore &all"), _("&Retry"),
 895                           _("&Cancel"));
 896         g_free (msg);
 897 
 898         switch (result)
 899         {
 900         case 0:
 901             /* try next file */
 902             return TRUE;
 903 
 904         case 1:
 905             ignore_all = TRUE;
 906             /* try next file */
 907             return TRUE;
 908 
 909         case 2:
 910             /* retry chown of this file */
 911             break;
 912 
 913         case 3:
 914         default:
 915             /* stop remain files processing */
 916             return FALSE;
 917         }
 918     }
 919 
 920     return TRUE;
 921 
 922 }
 923 
 924 /* --------------------------------------------------------------------------------------------- */
 925 
 926 static gboolean
 927 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]  */
 928 {
 929     gboolean ret;
 930 
 931     ret = try_advanced_chown (p, m, u, g);
 932 
 933     do_file_mark (panel, current_file, 0);
 934 
 935     return ret;
 936 }
 937 
 938  /* --------------------------------------------------------------------------------------------- */
 939 
 940 static void
 941 apply_advanced_chowns (WPanel * panel, vfs_path_t * vpath, struct stat *sf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 942 {
 943     gid_t a_gid = sf->st_gid;
 944     uid_t a_uid = sf->st_uid;
 945     gboolean ok;
 946 
 947     if (!do_advanced_chown (panel, vpath, get_mode (),
 948                             (ch_flags[9] == '+') ? a_uid : (uid_t) (-1),
 949                             (ch_flags[10] == '+') ? a_gid : (gid_t) (-1)))
 950         return;
 951 
 952     do
 953     {
 954         const GString *fname;
 955 
 956         fname = next_file (panel);
 957         vpath = vfs_path_from_str (fname->str);
 958         ok = (mc_stat (vpath, sf) == 0);
 959 
 960         if (!ok)
 961         {
 962             /* if current file was deleted outside mc -- try next file */
 963             /* decrease panel->marked */
 964             do_file_mark (panel, current_file, 0);
 965 
 966             /* try next file */
 967             ok = TRUE;
 968         }
 969         else
 970         {
 971             ch_cmode = sf->st_mode;
 972 
 973             ok = do_advanced_chown (panel, vpath, get_mode (),
 974                                     (ch_flags[9] == '+') ? a_uid : (uid_t) (-1),
 975                                     (ch_flags[10] == '+') ? a_gid : (gid_t) (-1));
 976         }
 977 
 978         vfs_path_free (vpath, TRUE);
 979     }
 980     while (ok && panel->marked != 0);
 981 }
 982 
 983 /* --------------------------------------------------------------------------------------------- */
 984 /*** public functions ****************************************************************************/
 985 /* --------------------------------------------------------------------------------------------- */
 986 
 987 void
 988 advanced_chown_cmd (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 989 {
 990     gboolean need_update;
 991     gboolean end_chown;
 992 
 993     /* Number of files at startup */
 994     int files_on_begin;
 995 
 996     files_on_begin = MAX (1, panel->marked);
 997 
 998     advanced_chown_init ();
 999 
1000     current_file = 0;
1001     ignore_all = FALSE;
1002 
1003     do
1004     {                           /* do while any files remaining */
1005         vfs_path_t *vpath;
1006         WDialog *ch_dlg;
1007         const GString *fname;
1008         int result;
1009         int file_idx;
1010 
1011         do_refresh ();
1012 
1013         need_update = FALSE;
1014         end_chown = FALSE;
1015 
1016         if (panel->marked != 0)
1017             fname = next_file (panel);  /* next marked file */
1018         else
1019             fname = selection (panel)->fname;   /* single file */
1020 
1021         vpath = vfs_path_from_str (fname->str);
1022 
1023         if (mc_stat (vpath, &sf_stat) != 0)
1024         {
1025             vfs_path_free (vpath, TRUE);
1026             break;
1027         }
1028 
1029         ch_cmode = sf_stat.st_mode;
1030 
1031         ch_dlg = advanced_chown_dlg_create (panel);
1032 
1033         file_idx = files_on_begin == 1 ? 1 : (files_on_begin - panel->marked + 1);
1034         label_set_textv (l_filename, "%s (%d/%d)",
1035                          str_fit_to_term (fname->str, WIDGET (ch_dlg)->cols - 20, J_LEFT_FIT),
1036                          file_idx, files_on_begin);
1037         update_ownership ();
1038 
1039         result = dlg_run (ch_dlg);
1040 
1041         switch (result)
1042         {
1043         case B_CANCEL:
1044             end_chown = TRUE;
1045             break;
1046 
1047         case B_ENTER:
1048             if (panel->marked <= 1)
1049             {
1050                 /* single or last file */
1051                 if (mc_chmod (vpath, get_mode ()) == -1)
1052                     message (D_ERROR, MSG_ERROR, _("Cannot chmod \"%s\"\n%s"),
1053                              fname->str, unix_error_string (errno));
1054                 /* call mc_chown only, if mc_chmod didn't fail */
1055                 else if (mc_chown
1056                          (vpath, (ch_flags[9] == '+') ? sf_stat.st_uid : (uid_t) (-1),
1057                           (ch_flags[10] == '+') ? sf_stat.st_gid : (gid_t) (-1)) == -1)
1058                     message (D_ERROR, MSG_ERROR, _("Cannot chown \"%s\"\n%s"), fname->str,
1059                              unix_error_string (errno));
1060 
1061                 end_chown = TRUE;
1062             }
1063             else if (!try_advanced_chown
1064                      (vpath, get_mode (), (ch_flags[9] == '+') ? sf_stat.st_uid : (uid_t) (-1),
1065                       (ch_flags[10] == '+') ? sf_stat.st_gid : (gid_t) (-1)))
1066             {
1067                 /* stop multiple files processing */
1068                 result = B_CANCEL;
1069                 end_chown = TRUE;
1070             }
1071 
1072             need_update = TRUE;
1073             break;
1074 
1075         case B_SETALL:
1076             apply_advanced_chowns (panel, vpath, &sf_stat);
1077             need_update = TRUE;
1078             end_chown = TRUE;
1079             break;
1080 
1081         case B_SKIP:
1082         default:
1083             break;
1084         }
1085 
1086         if (panel->marked != 0 && result != B_CANCEL)
1087         {
1088             do_file_mark (panel, current_file, 0);
1089             need_update = TRUE;
1090         }
1091 
1092         vfs_path_free (vpath, TRUE);
1093 
1094         dlg_destroy (ch_dlg);
1095     }
1096     while (panel->marked != 0 && !end_chown);
1097 
1098     advanced_chown_done (need_update);
1099 }
1100 
1101 /* --------------------------------------------------------------------------------------------- */

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