root/src/filemanager/achown.c

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

DEFINITIONS

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

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