Manual pages: mcmcdiffmceditmcview

root/src/filemanager/achown.c

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

DEFINITIONS

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

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

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