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

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