root/src/filemanager/filegui.c

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

DEFINITIONS

This source file includes following definitions.
  1. statfs
  2. statvfs_works
  3. filegui__check_attrs_on_fs
  4. file_frmt_time
  5. file_eta_prepare_for_show
  6. file_bps_prepare_for_show
  7. overwrite_query_dialog
  8. is_wildcarded
  9. place_progress_buttons
  10. progress_button_callback
  11. check_progress_buttons
  12. file_op_context_create_ui
  13. file_op_context_destroy_ui
  14. file_progress_show
  15. file_progress_show_count
  16. file_progress_show_total
  17. file_progress_show_source
  18. file_progress_show_target
  19. file_progress_show_deleting
  20. file_progress_real_query_replace
  21. file_mask_dialog

   1 /*
   2    File management GUI for the text mode edition
   3 
   4    The copy code was based in GNU's cp, and was written by:
   5    Torbjorn Granlund, David MacKenzie, and Jim Meyering.
   6 
   7    The move code was based in GNU's mv, and was written by:
   8    Mike Parker and David MacKenzie.
   9 
  10    Janne Kukonlehto added much error recovery to them for being used
  11    in an interactive program.
  12 
  13    Copyright (C) 1994-2020
  14    Free Software Foundation, Inc.
  15 
  16    Written by:
  17    Janne Kukonlehto, 1994, 1995
  18    Fred Leeflang, 1994, 1995
  19    Miguel de Icaza, 1994, 1995, 1996
  20    Jakub Jelinek, 1995, 1996
  21    Norbert Warmuth, 1997
  22    Pavel Machek, 1998
  23    Slava Zanko, 2009, 2010, 2011, 2012, 2013
  24    Andrew Borodin <aborodin@vmail.ru>, 2009, 2010, 2011, 2012, 2013
  25 
  26    This file is part of the Midnight Commander.
  27 
  28    The Midnight Commander is free software: you can redistribute it
  29    and/or modify it under the terms of the GNU General Public License as
  30    published by the Free Software Foundation, either version 3 of the License,
  31    or (at your option) any later version.
  32 
  33    The Midnight Commander is distributed in the hope that it will be useful,
  34    but WITHOUT ANY WARRANTY; without even the implied warranty of
  35    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  36    GNU General Public License for more details.
  37 
  38    You should have received a copy of the GNU General Public License
  39    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  40  */
  41 
  42 /*
  43  * Please note that all dialogs used here must be safe for background
  44  * operations.
  45  */
  46 
  47 /** \file  filegui.c
  48  *  \brief Source: file management GUI for the text mode edition
  49  */
  50 
  51 /* {{{ Include files */
  52 
  53 #include <config.h>
  54 
  55 #if ((defined STAT_STATVFS || defined STAT_STATVFS64)                                       \
  56      && (defined HAVE_STRUCT_STATVFS_F_BASETYPE || defined HAVE_STRUCT_STATVFS_F_FSTYPENAME \
  57          || (! defined HAVE_STRUCT_STATFS_F_FSTYPENAME)))
  58 #define USE_STATVFS 1
  59 #else
  60 #define USE_STATVFS 0
  61 #endif
  62 
  63 #include <errno.h>
  64 #include <ctype.h>
  65 #include <stdio.h>
  66 #include <string.h>
  67 #include <sys/types.h>
  68 #include <sys/stat.h>
  69 
  70 #if USE_STATVFS
  71 #include <sys/statvfs.h>
  72 #elif defined HAVE_SYS_VFS_H
  73 #include <sys/vfs.h>
  74 #elif defined HAVE_SYS_MOUNT_H && defined HAVE_SYS_PARAM_H
  75 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
  76    It does have statvfs.h, but shouldn't use it, since it doesn't
  77    HAVE_STRUCT_STATVFS_F_BASETYPE.  So find a clean way to fix it.  */
  78 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
  79 #include <sys/param.h>
  80 #include <sys/mount.h>
  81 #elif defined HAVE_OS_H         /* Haiku, also (obsolete) BeOS */
  82 #include <fs_info.h>
  83 #endif
  84 
  85 #if USE_STATVFS
  86 #if ! defined STAT_STATVFS && defined STAT_STATVFS64
  87 #define STRUCT_STATVFS struct statvfs64
  88 #define STATFS statvfs64
  89 #else
  90 #define STRUCT_STATVFS struct statvfs
  91 #define STATFS statvfs
  92 
  93 #if defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__)
  94 #include <sys/utsname.h>
  95 #include <sys/statfs.h>
  96 #define STAT_STATFS2_BSIZE 1
  97 #endif
  98 #endif
  99 
 100 #else
 101 #define STATFS statfs
 102 #define STRUCT_STATVFS struct statfs
 103 #ifdef HAVE_OS_H                /* Haiku, also (obsolete) BeOS */
 104 /* BeOS has a statvfs function, but it does not return sensible values
 105    for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
 106    f_fstypename.  Use 'struct fs_info' instead.  */
 107 static int
 108 statfs (char const *filename, struct fs_info *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 109 {
 110     dev_t device;
 111 
 112     device = dev_for_path (filename);
 113 
 114     if (device < 0)
 115     {
 116         errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
 117                  : device == B_BAD_VALUE ? EINVAL
 118                  : device == B_NAME_TOO_LONG ? ENAMETOOLONG
 119                  : device == B_NO_MEMORY ? ENOMEM : device == B_FILE_ERROR ? EIO : 0);
 120         return -1;
 121     }
 122     /* If successful, buf->dev will be == device.  */
 123     return fs_stat_dev (device, buf);
 124 }
 125 
 126 #define STRUCT_STATVFS struct fs_info
 127 #else
 128 #define STRUCT_STATVFS struct statfs
 129 #endif
 130 #endif
 131 
 132 #ifdef HAVE_STRUCT_STATVFS_F_BASETYPE
 133 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
 134 #else
 135 #if defined HAVE_STRUCT_STATVFS_F_FSTYPENAME || defined HAVE_STRUCT_STATFS_F_FSTYPENAME
 136 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
 137 #elif defined HAVE_OS_H         /* Haiku, also (obsolete) BeOS */
 138 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
 139 #endif
 140 #endif
 141 
 142 #include <unistd.h>
 143 
 144 #include "lib/global.h"
 145 
 146 #include "lib/tty/key.h"        /* tty_get_event */
 147 #include "lib/mcconfig.h"
 148 #include "lib/search.h"
 149 #include "lib/vfs/vfs.h"
 150 #include "lib/strescape.h"
 151 #include "lib/strutil.h"
 152 #include "lib/timefmt.h"        /* file_date() */
 153 #include "lib/util.h"
 154 #include "lib/widget.h"
 155 
 156 #include "src/setup.h"          /* verbose, safe_overwrite */
 157 
 158 #include "midnight.h"
 159 #include "fileopctx.h"          /* FILE_CONT */
 160 
 161 #include "filegui.h"
 162 
 163 /* }}} */
 164 
 165 /*** global variables ****************************************************************************/
 166 
 167 gboolean classic_progressbar = TRUE;
 168 
 169 /*** file scope macro definitions ****************************************************************/
 170 
 171 #define truncFileString(dlg, s)       str_trunc (s, WIDGET (dlg)->cols - 10)
 172 #define truncFileStringSecure(dlg, s) path_trunc (s, WIDGET (dlg)->cols - 10)
 173 
 174 /*** file scope type declarations ****************************************************************/
 175 
 176 /* *INDENT-OFF* */
 177 typedef enum {
 178     MSDOS_SUPER_MAGIC     = 0x4d44,
 179     NTFS_SB_MAGIC         = 0x5346544e,
 180     FUSE_MAGIC            = 0x65735546,
 181     PROC_SUPER_MAGIC      = 0x9fa0,
 182     SMB_SUPER_MAGIC       = 0x517B,
 183     NCP_SUPER_MAGIC       = 0x564c,
 184     USBDEVICE_SUPER_MAGIC = 0x9fa2
 185 } filegui_nonattrs_fs_t;
 186 /* *INDENT-ON* */
 187 
 188 /* Used for button result values */
 189 typedef enum
 190 {
 191     REPLACE_YES = B_USER,
 192     REPLACE_NO,
 193     REPLACE_APPEND,
 194     REPLACE_REGET,
 195     REPLACE_ALL,
 196     REPLACE_OLDER,
 197     REPLACE_NONE,
 198     REPLACE_SMALLER,
 199     REPLACE_SIZE,
 200     REPLACE_ABORT
 201 } replace_action_t;
 202 
 203 /* This structure describes the UI and internal data required by a file
 204  * operation context.
 205  */
 206 typedef struct
 207 {
 208     /* ETA and bps */
 209     gboolean showing_eta;
 210     gboolean showing_bps;
 211 
 212     /* Dialog and widgets for the operation progress window */
 213     WDialog *op_dlg;
 214     /* Source file: label and name */
 215     WLabel *src_file_label;
 216     WLabel *src_file;
 217     /* Target file: label and name */
 218     WLabel *tgt_file_label;
 219     WLabel *tgt_file;
 220 
 221     WGauge *progress_file_gauge;
 222     WLabel *progress_file_label;
 223 
 224     WGauge *progress_total_gauge;
 225 
 226     WLabel *total_files_processed_label;
 227     WLabel *time_label;
 228     WHLine *total_bytes_label;
 229 
 230     /* Query replace dialog */
 231     WDialog *replace_dlg;
 232     const char *src_filename;
 233     const char *tgt_filename;
 234     replace_action_t replace_result;
 235     gboolean dont_overwrite_with_zero;
 236 
 237     struct stat *src_stat, *dst_stat;
 238 } file_op_context_ui_t;
 239 
 240 /*** file scope variables ************************************************************************/
 241 
 242 static struct
 243 {
 244     Widget *w;
 245     FileProgressStatus action;
 246     const char *text;
 247     button_flags_t flags;
 248     int len;
 249 } progress_buttons[] =
 250 {
 251     /* *INDENT-OFF* */
 252     { NULL, FILE_SKIP, N_("&Skip"), NORMAL_BUTTON, -1 },
 253     { NULL, FILE_SUSPEND, N_("S&uspend"), NORMAL_BUTTON, -1 },
 254     { NULL, FILE_SUSPEND, N_("Con&tinue"), NORMAL_BUTTON, -1 },
 255     { NULL, FILE_ABORT, N_("&Abort"), NORMAL_BUTTON, -1 }
 256     /* *INDENT-ON* */
 257 };
 258 
 259 /* --------------------------------------------------------------------------------------------- */
 260 /*** file scope functions ************************************************************************/
 261 /* --------------------------------------------------------------------------------------------- */
 262 
 263 /* Return true if statvfs works.  This is false for statvfs on systems
 264    with GNU libc on Linux kernels before 2.6.36, which stats all
 265    preceding entries in /proc/mounts; that makes df hang if even one
 266    of the corresponding file systems is hard-mounted but not available.  */
 267 
 268 #if USE_STATVFS && ! (! defined STAT_STATVFS && defined STAT_STATVFS64)
 269 static int
 270 statvfs_works (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 271 {
 272 #if ! (defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__))
 273     return 1;
 274 #else
 275     static int statvfs_works_cache = -1;
 276     struct utsname name;
 277 
 278     if (statvfs_works_cache < 0)
 279         statvfs_works_cache = (uname (&name) == 0 && 0 <= str_verscmp (name.release, "2.6.36"));
 280     return statvfs_works_cache;
 281 #endif
 282 }
 283 #endif
 284 
 285 /* --------------------------------------------------------------------------------------------- */
 286 
 287 static gboolean
 288 filegui__check_attrs_on_fs (const char *fs_path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 289 {
 290     STRUCT_STATVFS stfs;
 291 
 292 #if USE_STATVFS && defined(STAT_STATVFS)
 293     if (statvfs_works () && statvfs (fs_path, &stfs) != 0)
 294         return TRUE;
 295 #else
 296     if (STATFS (fs_path, &stfs) != 0)
 297         return TRUE;
 298 #endif
 299 
 300 #if (USE_STATVFS && defined(HAVE_STRUCT_STATVFS_F_TYPE)) || \
 301         (!USE_STATVFS && defined(HAVE_STRUCT_STATFS_F_TYPE))
 302     switch ((filegui_nonattrs_fs_t) stfs.f_type)
 303     {
 304     case MSDOS_SUPER_MAGIC:
 305     case NTFS_SB_MAGIC:
 306     case PROC_SUPER_MAGIC:
 307     case SMB_SUPER_MAGIC:
 308     case NCP_SUPER_MAGIC:
 309     case USBDEVICE_SUPER_MAGIC:
 310         return FALSE;
 311     default:
 312         break;
 313     }
 314 #elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
 315     if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdos") == 0
 316         || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdosfs") == 0
 317         || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
 318         || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "procfs") == 0
 319         || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
 320         || strstr (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fusefs") != NULL)
 321         return FALSE;
 322 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
 323     if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "pcfs") == 0
 324         || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
 325         || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "proc") == 0
 326         || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
 327         || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fuse") == 0)
 328         return FALSE;
 329 #endif
 330 
 331     return TRUE;
 332 }
 333 
 334 /* --------------------------------------------------------------------------------------------- */
 335 
 336 static void
 337 file_frmt_time (char *buffer, double eta_secs)
     /* [previous][next][first][last][top][bottom][index][help]  */
 338 {
 339     int eta_hours, eta_mins, eta_s;
 340 
 341     eta_hours = (int) (eta_secs / (60 * 60));
 342     eta_mins = (int) ((eta_secs - (eta_hours * 60 * 60)) / 60);
 343     eta_s = (int) (eta_secs - (eta_hours * 60 * 60 + eta_mins * 60));
 344     g_snprintf (buffer, BUF_TINY, _("%d:%02d.%02d"), eta_hours, eta_mins, eta_s);
 345 }
 346 
 347 /* --------------------------------------------------------------------------------------------- */
 348 
 349 static void
 350 file_eta_prepare_for_show (char *buffer, double eta_secs, gboolean always_show)
     /* [previous][next][first][last][top][bottom][index][help]  */
 351 {
 352     char _fmt_buff[BUF_TINY];
 353 
 354     if (eta_secs <= 0.5 && !always_show)
 355     {
 356         *buffer = '\0';
 357         return;
 358     }
 359 
 360     if (eta_secs <= 0.5)
 361         eta_secs = 1;
 362     file_frmt_time (_fmt_buff, eta_secs);
 363     g_snprintf (buffer, BUF_TINY, _("ETA %s"), _fmt_buff);
 364 }
 365 
 366 /* --------------------------------------------------------------------------------------------- */
 367 
 368 static void
 369 file_bps_prepare_for_show (char *buffer, long bps)
     /* [previous][next][first][last][top][bottom][index][help]  */
 370 {
 371     if (bps > 1024 * 1024)
 372         g_snprintf (buffer, BUF_TINY, _("%.2f MB/s"), bps / (1024 * 1024.0));
 373     else if (bps > 1024)
 374         g_snprintf (buffer, BUF_TINY, _("%.2f KB/s"), bps / 1024.0);
 375     else if (bps > 1)
 376         g_snprintf (buffer, BUF_TINY, _("%ld B/s"), bps);
 377     else
 378         *buffer = '\0';
 379 }
 380 
 381 /* --------------------------------------------------------------------------------------------- */
 382 
 383 /* The dialog layout:
 384  *
 385  * +---------------------- File exists -----------------------+
 386  * | New     : /path/to/original_file_name                    |   // 0, 1
 387  * |                    1234567             feb  4 2017 13:38 |   // 2, 3
 388  * | Existing: /path/to/target_file_name                      |   // 4, 5
 389  * |                 1234567890             feb  4 2017 13:37 |   // 6, 7
 390  * +----------------------------------------------------------+
 391  * |                   Overwrite this file?                   |   // 8
 392  * |            [ Yes ] [ No ] [ Append ] [ Reget ]           |   // 9, 10, 11, 12
 393  * +----------------------------------------------------------+
 394  * |                   Overwrite all files?                   |   // 13
 395  * |  [ ] Don't overwrite with zero length file               |   // 14
 396  * |  [ All ] [ Older ] [None] [ Smaller ] [ Size differs ]   |   // 15, 16, 17, 18, 19
 397  * +----------------------------------------------------------|
 398  * |                         [ Abort ]                        |   // 20
 399  * +----------------------------------------------------------+
 400  */
 401 
 402 static replace_action_t
 403 overwrite_query_dialog (file_op_context_t * ctx, enum OperationMode mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 404 {
 405 #define W(i) dlg_widgets[i].widget
 406 #define WX(i) W(i)->x
 407 #define WCOLS(i) W(i)->cols
 408 
 409 #define NEW_LABEL(i, text) \
 410     W(i) = WIDGET (label_new (dlg_widgets[i].y, dlg_widgets[i].x, text))
 411 
 412 #define ADD_LABEL(i) \
 413     group_add_widget_autopos (g, W(i), dlg_widgets[i].pos_flags, \
 414                               g->current != NULL ? g->current->data : NULL)
 415 
 416 #define NEW_BUTTON(i) \
 417     W(i) = WIDGET (button_new (dlg_widgets[i].y, dlg_widgets[i].x, \
 418                                dlg_widgets[i].value, NORMAL_BUTTON, dlg_widgets[i].text, NULL))
 419 
 420 #define ADD_BUTTON(i) \
 421     group_add_widget_autopos (g, W(i), dlg_widgets[i].pos_flags, g->current->data)
 422 
 423     /* dialog sizes */
 424     const int dlg_height = 17;
 425     int dlg_width = 60;
 426 
 427     struct
 428     {
 429         Widget *widget;
 430         const char *text;
 431         int y;
 432         int x;
 433         widget_pos_flags_t pos_flags;
 434         int value;              /* 0 for labels and checkbox */
 435     } dlg_widgets[] =
 436     {
 437         /* *INDENT-OFF* */
 438         /*  0 - label */
 439         { NULL, N_("New     :"), 2, 3, WPOS_KEEP_DEFAULT, 0 },
 440         /*  1 - label - name */
 441         { NULL, NULL, 2, 14, WPOS_KEEP_DEFAULT, 0 },
 442         /*  2 - label - size */
 443         { NULL, NULL, 3, 3, WPOS_KEEP_DEFAULT, 0 },
 444         /*  3 - label - date & time */
 445         { NULL, NULL, 3, 43, WPOS_KEEP_TOP | WPOS_KEEP_RIGHT, 0 },
 446         /*  4 - label */
 447         { NULL, N_("Existing:"), 4, 3, WPOS_KEEP_DEFAULT, 0 },
 448         /*  5 - label - name */
 449         { NULL, NULL, 4, 14, WPOS_KEEP_DEFAULT, 0 },
 450         /*  6 - label - size */
 451         { NULL, NULL, 5, 3, WPOS_KEEP_DEFAULT, 0 },
 452         /*  7 - label - date & time */
 453         { NULL, NULL, 5, 43, WPOS_KEEP_TOP | WPOS_KEEP_RIGHT, 0 },
 454         /* --------------------------------------------------- */
 455         /*  8 - label */
 456         { NULL, N_("Overwrite this file?"), 7, 21, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
 457         /*  9 - button */
 458         { NULL, N_("&Yes"), 8, 14, WPOS_KEEP_DEFAULT, REPLACE_YES },
 459         /* 10 - button */
 460         { NULL, N_("&No"), 8, 22, WPOS_KEEP_DEFAULT, REPLACE_NO },
 461         /* 11 - button */
 462         { NULL, N_("A&ppend"), 8, 29, WPOS_KEEP_DEFAULT, REPLACE_APPEND },
 463         /* 12 - button */
 464         { NULL, N_("&Reget"), 8, 40, WPOS_KEEP_DEFAULT, REPLACE_REGET },
 465         /* --------------------------------------------------- */
 466         /* 13 - label */
 467         { NULL, N_("Overwrite all files?"), 10, 21, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
 468         /* 14 - checkbox */
 469         { NULL, N_("Don't overwrite with &zero length file"), 11, 3, WPOS_KEEP_DEFAULT, 0 },
 470         /* 15 - button */
 471         { NULL, N_("A&ll"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_ALL },
 472         /* 16 - button */
 473         { NULL, N_("&Older"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_OLDER },
 474         /* 17 - button */
 475         { NULL, N_("Non&e"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_NONE },
 476         /* 18 - button */
 477         { NULL, N_("S&maller"), 12, 25, WPOS_KEEP_DEFAULT, REPLACE_SMALLER },
 478         /* 19 - button */
 479         { NULL, N_("&Size differs"), 12, 40, WPOS_KEEP_DEFAULT, REPLACE_SIZE },
 480         /* --------------------------------------------------- */
 481         /* 20 - button */
 482         { NULL, N_("&Abort"), 14, 27, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, REPLACE_ABORT }
 483         /* *INDENT-ON* */
 484     };
 485 
 486     const int gap = 1;
 487 
 488     file_op_context_ui_t *ui = ctx->ui;
 489     Widget *wd;
 490     WGroup *g;
 491     const char *title;
 492 
 493     vfs_path_t *p;
 494     char *s1;
 495     char s2[BUF_SMALL];
 496     int w, bw1, bw2;
 497     unsigned short i;
 498 
 499     gboolean do_append = FALSE, do_reget = FALSE;
 500     unsigned long yes_id, no_id;
 501     int result;
 502 
 503     if (mode == Foreground)
 504         title = _("File exists");
 505     else
 506         title = _("Background process: File exists");
 507 
 508 #ifdef ENABLE_NLS
 509     {
 510         const unsigned short num = G_N_ELEMENTS (dlg_widgets);
 511 
 512         for (i = 0; i < num; i++)
 513             if (dlg_widgets[i].text != NULL)
 514                 dlg_widgets[i].text = _(dlg_widgets[i].text);
 515     }
 516 #endif /* ENABLE_NLS */
 517 
 518     /* create widgets to get their real widths */
 519     /* new file */
 520     NEW_LABEL (0, dlg_widgets[0].text);
 521     /* new file name */
 522     p = vfs_path_from_str (ui->src_filename);
 523     s1 = vfs_path_to_str_flags (p, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
 524     NEW_LABEL (1, s1);
 525     vfs_path_free (p);
 526     g_free (s1);
 527     /* new file size */
 528     size_trunc_len (s2, sizeof (s2), ui->src_stat->st_size, 0, panels_options.kilobyte_si);
 529     NEW_LABEL (2, s2);
 530     /* new file modification date & time */
 531     s1 = (char *) file_date (ui->src_stat->st_mtime);
 532     NEW_LABEL (3, s1);
 533 
 534     /* existing file */
 535     NEW_LABEL (4, dlg_widgets[4].text);
 536     /* existing file name */
 537     p = vfs_path_from_str (ui->tgt_filename);
 538     s1 = vfs_path_to_str_flags (p, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
 539     NEW_LABEL (5, s1);
 540     vfs_path_free (p);
 541     g_free (s1);
 542     /* existing file size */
 543     size_trunc_len (s2, sizeof (s2), ui->dst_stat->st_size, 0, panels_options.kilobyte_si);
 544     NEW_LABEL (6, s2);
 545     /* existing file modification date & time */
 546     s1 = (char *) file_date (ui->dst_stat->st_mtime);
 547     NEW_LABEL (7, s1);
 548 
 549     /* will "Append" and "Reget" buttons be in the dialog? */
 550     do_append = !S_ISDIR (ui->dst_stat->st_mode);
 551     do_reget = do_append && ctx->operation == OP_COPY && ui->dst_stat->st_size != 0
 552         && ui->src_stat->st_size > ui->dst_stat->st_size;
 553 
 554     NEW_LABEL (8, dlg_widgets[8].text);
 555     NEW_BUTTON (9);
 556     NEW_BUTTON (10);
 557     if (do_append)
 558         NEW_BUTTON (11);
 559     if (do_reget)
 560         NEW_BUTTON (12);
 561 
 562     NEW_LABEL (13, dlg_widgets[13].text);
 563     dlg_widgets[14].widget =
 564         WIDGET (check_new (dlg_widgets[14].y, dlg_widgets[14].x, FALSE, dlg_widgets[14].text));
 565     for (i = 15; i <= 20; i++)
 566         NEW_BUTTON (i);
 567 
 568     /* place widgets */
 569     dlg_width -= 2 * (2 + gap); /* inside frame */
 570 
 571     /* perhaps longest line is buttons */
 572     bw1 = WCOLS (9) + gap + WCOLS (10);
 573     if (do_append)
 574         bw1 += gap + WCOLS (11);
 575     if (do_reget)
 576         bw1 += gap + WCOLS (12);
 577     dlg_width = MAX (dlg_width, bw1);
 578 
 579     bw2 = WCOLS (15);
 580     for (i = 16; i <= 19; i++)
 581         bw2 += gap + WCOLS (i);
 582     dlg_width = MAX (dlg_width, bw2);
 583 
 584     dlg_width = MAX (dlg_width, WCOLS (8));
 585     dlg_width = MAX (dlg_width, WCOLS (13));
 586     dlg_width = MAX (dlg_width, WCOLS (14));
 587 
 588     /* truncate file names */
 589     w = WCOLS (0) + gap + WCOLS (1);
 590     if (w > dlg_width)
 591     {
 592         WLabel *l = LABEL (W (1));
 593 
 594         w = dlg_width - gap - WCOLS (0);
 595         label_set_text (l, str_trunc (l->text, w));
 596     }
 597 
 598     w = WCOLS (4) + gap + WCOLS (5);
 599     if (w > dlg_width)
 600     {
 601         WLabel *l = LABEL (W (5));
 602 
 603         w = dlg_width - gap - WCOLS (4);
 604         label_set_text (l, str_trunc (l->text, w));
 605     }
 606 
 607     /* real dlalog width */
 608     dlg_width += 2 * (2 + gap);
 609 
 610     WX (1) = WX (0) + WCOLS (0) + gap;
 611     WX (5) = WX (4) + WCOLS (4) + gap;
 612 
 613     /* sizes: right alignment */
 614     WX (2) = dlg_width / 2 - WCOLS (2);
 615     WX (6) = dlg_width / 2 - WCOLS (6);
 616 
 617     w = dlg_width - (2 + gap);  /* right bound */
 618 
 619     /* date & time */
 620     WX (3) = w - WCOLS (3);
 621     WX (7) = w - WCOLS (7);
 622 
 623     /* buttons: center alignment */
 624     WX (9) = dlg_width / 2 - bw1 / 2;
 625     WX (10) = WX (9) + WCOLS (9) + gap;
 626     if (do_append)
 627         WX (11) = WX (10) + WCOLS (10) + gap;
 628     if (do_reget)
 629         WX (12) = WX (11) + WCOLS (11) + gap;
 630 
 631     WX (15) = dlg_width / 2 - bw2 / 2;
 632     for (i = 16; i <= 19; i++)
 633         WX (i) = WX (i - 1) + WCOLS (i - 1) + gap;
 634 
 635     /* TODO: write help (ticket #3970) */
 636     ui->replace_dlg =
 637         dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, alarm_colors, NULL, NULL,
 638                     "[Replace]", title);
 639     wd = WIDGET (ui->replace_dlg);
 640     g = GROUP (ui->replace_dlg);
 641 
 642     /* file info */
 643     for (i = 0; i <= 7; i++)
 644         ADD_LABEL (i);
 645     group_add_widget (g, hline_new (W (7)->y - wd->y + 1, -1, -1));
 646 
 647     /* label & buttons */
 648     ADD_LABEL (8);              /* Overwrite this file? */
 649     yes_id = ADD_BUTTON (9);    /* Yes */
 650     no_id = ADD_BUTTON (10);    /* No */
 651     if (do_append)
 652         ADD_BUTTON (11);        /* Append */
 653     if (do_reget)
 654         ADD_BUTTON (12);        /* Reget */
 655     group_add_widget (g, hline_new (W (10)->y - wd->y + 1, -1, -1));
 656 
 657     /* label & buttons */
 658     ADD_LABEL (13);             /* Overwrite all files? */
 659     group_add_widget (g, dlg_widgets[14].widget);
 660     for (i = 15; i <= 19; i++)
 661         ADD_BUTTON (i);
 662     group_add_widget (g, hline_new (W (19)->y - wd->y + 1, -1, -1));
 663 
 664     ADD_BUTTON (20);            /* Abort */
 665 
 666     group_select_widget_by_id (g, safe_overwrite ? no_id : yes_id);
 667 
 668     result = dlg_run (ui->replace_dlg);
 669 
 670     if (result != B_CANCEL)
 671         ui->dont_overwrite_with_zero = CHECK (dlg_widgets[14].widget)->state;
 672 
 673     dlg_destroy (ui->replace_dlg);
 674 
 675     return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
 676 
 677 #undef ADD_BUTTON
 678 #undef NEW_BUTTON
 679 #undef ADD_LABEL
 680 #undef NEW_LABEL
 681 #undef WCOLS
 682 #undef WX
 683 #undef W
 684 }
 685 
 686 /* --------------------------------------------------------------------------------------------- */
 687 
 688 static gboolean
 689 is_wildcarded (const char *p)
     /* [previous][next][first][last][top][bottom][index][help]  */
 690 {
 691     gboolean escaped = FALSE;
 692 
 693     for (; *p != '\0'; p++)
 694     {
 695         if (*p == '\\')
 696         {
 697             if (p[1] >= '1' && p[1] <= '9' && !escaped)
 698                 return TRUE;
 699             escaped = !escaped;
 700         }
 701         else
 702         {
 703             if ((*p == '*' || *p == '?') && !escaped)
 704                 return TRUE;
 705             escaped = FALSE;
 706         }
 707     }
 708     return FALSE;
 709 }
 710 
 711 /* --------------------------------------------------------------------------------------------- */
 712 
 713 static void
 714 place_progress_buttons (WDialog * h, gboolean suspended)
     /* [previous][next][first][last][top][bottom][index][help]  */
 715 {
 716     const size_t i = suspended ? 2 : 1;
 717     Widget *w = WIDGET (h);
 718     int buttons_width;
 719 
 720     buttons_width = 2 + progress_buttons[0].len + progress_buttons[3].len;
 721     buttons_width += progress_buttons[i].len;
 722     button_set_text (BUTTON (progress_buttons[i].w), progress_buttons[i].text);
 723 
 724     progress_buttons[0].w->x = w->x + (w->cols - buttons_width) / 2;
 725     progress_buttons[i].w->x = progress_buttons[0].w->x + progress_buttons[0].len + 1;
 726     progress_buttons[3].w->x = progress_buttons[i].w->x + progress_buttons[i].len + 1;
 727 }
 728 
 729 /* --------------------------------------------------------------------------------------------- */
 730 
 731 static int
 732 progress_button_callback (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 733 {
 734     (void) button;
 735     (void) action;
 736 
 737     /* don't close dialog in any case */
 738     return 0;
 739 }
 740 
 741 /* --------------------------------------------------------------------------------------------- */
 742 /*** public functions ****************************************************************************/
 743 /* --------------------------------------------------------------------------------------------- */
 744 
 745 FileProgressStatus
 746 check_progress_buttons (file_op_context_t * ctx)
     /* [previous][next][first][last][top][bottom][index][help]  */
 747 {
 748     int c;
 749     Gpm_Event event;
 750     file_op_context_ui_t *ui;
 751 
 752     if (ctx == NULL || ctx->ui == NULL)
 753         return FILE_CONT;
 754 
 755     ui = ctx->ui;
 756 
 757   get_event:
 758     event.x = -1;               /* Don't show the GPM cursor */
 759     c = tty_get_event (&event, FALSE, ctx->suspended);
 760     if (c == EV_NONE)
 761         return FILE_CONT;
 762 
 763     /* Reinitialize to avoid old values after events other than selecting a button */
 764     ui->op_dlg->ret_value = FILE_CONT;
 765 
 766     dlg_process_event (ui->op_dlg, c, &event);
 767     switch (ui->op_dlg->ret_value)
 768     {
 769     case FILE_SKIP:
 770         if (ctx->suspended)
 771         {
 772             /* redraw dialog in case of Skip after Suspend */
 773             place_progress_buttons (ui->op_dlg, FALSE);
 774             widget_draw (WIDGET (ui->op_dlg));
 775         }
 776         ctx->suspended = FALSE;
 777         return FILE_SKIP;
 778     case B_CANCEL:
 779     case FILE_ABORT:
 780         ctx->suspended = FALSE;
 781         return FILE_ABORT;
 782     case FILE_SUSPEND:
 783         ctx->suspended = !ctx->suspended;
 784         place_progress_buttons (ui->op_dlg, ctx->suspended);
 785         widget_draw (WIDGET (ui->op_dlg));
 786         MC_FALLTHROUGH;
 787     default:
 788         if (ctx->suspended)
 789             goto get_event;
 790         return FILE_CONT;
 791     }
 792 }
 793 
 794 /* --------------------------------------------------------------------------------------------- */
 795 /* {{{ File progress display routines */
 796 
 797 void
 798 file_op_context_create_ui (file_op_context_t * ctx, gboolean with_eta,
     /* [previous][next][first][last][top][bottom][index][help]  */
 799                            filegui_dialog_type_t dialog_type)
 800 {
 801     file_op_context_ui_t *ui;
 802     Widget *w;
 803     WGroup *g;
 804     int buttons_width;
 805     int dlg_width = 58, dlg_height = 17;
 806     int y = 2, x = 3;
 807 
 808     if (ctx == NULL || ctx->ui != NULL)
 809         return;
 810 
 811 #ifdef ENABLE_NLS
 812     if (progress_buttons[0].len == -1)
 813     {
 814         size_t i;
 815 
 816         for (i = 0; i < G_N_ELEMENTS (progress_buttons); i++)
 817             progress_buttons[i].text = _(progress_buttons[i].text);
 818     }
 819 #endif
 820 
 821     ctx->dialog_type = dialog_type;
 822     ctx->recursive_result = RECURSIVE_YES;
 823     ctx->ui = g_new0 (file_op_context_ui_t, 1);
 824 
 825     ui = ctx->ui;
 826     ui->replace_result = REPLACE_YES;
 827 
 828     ui->op_dlg =
 829         dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, dialog_colors, NULL,
 830                     NULL, NULL, op_names[ctx->operation]);
 831     w = WIDGET (ui->op_dlg);
 832     g = GROUP (ui->op_dlg);
 833 
 834     if (dialog_type != FILEGUI_DIALOG_DELETE_ITEM)
 835     {
 836         ui->showing_eta = with_eta && ctx->progress_totals_computed;
 837         ui->showing_bps = with_eta;
 838 
 839         ui->src_file_label = label_new (y++, x, "");
 840         group_add_widget (g, ui->src_file_label);
 841 
 842         ui->src_file = label_new (y++, x, "");
 843         group_add_widget (g, ui->src_file);
 844 
 845         ui->tgt_file_label = label_new (y++, x, "");
 846         group_add_widget (g, ui->tgt_file_label);
 847 
 848         ui->tgt_file = label_new (y++, x, "");
 849         group_add_widget (g, ui->tgt_file);
 850 
 851         ui->progress_file_gauge = gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
 852         if (!classic_progressbar && (current_panel == right_panel))
 853             ui->progress_file_gauge->from_left_to_right = FALSE;
 854         group_add_widget_autopos (g, ui->progress_file_gauge, WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
 855 
 856         ui->progress_file_label = label_new (y++, x, "");
 857         group_add_widget (g, ui->progress_file_label);
 858 
 859         if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
 860         {
 861             ui->total_bytes_label = hline_new (y++, -1, -1);
 862             group_add_widget (g, ui->total_bytes_label);
 863 
 864             if (ctx->progress_totals_computed)
 865             {
 866                 ui->progress_total_gauge =
 867                     gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
 868                 if (!classic_progressbar && (current_panel == right_panel))
 869                     ui->progress_total_gauge->from_left_to_right = FALSE;
 870                 group_add_widget_autopos (g, ui->progress_total_gauge,
 871                                           WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
 872             }
 873 
 874             ui->total_files_processed_label = label_new (y++, x, "");
 875             group_add_widget (g, ui->total_files_processed_label);
 876 
 877             ui->time_label = label_new (y++, x, "");
 878             group_add_widget (g, ui->time_label);
 879         }
 880     }
 881     else
 882     {
 883         ui->src_file = label_new (y++, x, "");
 884         group_add_widget (g, ui->src_file);
 885 
 886         ui->total_files_processed_label = label_new (y++, x, "");
 887         group_add_widget (g, ui->total_files_processed_label);
 888     }
 889 
 890     group_add_widget (g, hline_new (y++, -1, -1));
 891 
 892     progress_buttons[0].w = WIDGET (button_new (y, 0, progress_buttons[0].action,
 893                                                 progress_buttons[0].flags, progress_buttons[0].text,
 894                                                 progress_button_callback));
 895     if (progress_buttons[0].len == -1)
 896         progress_buttons[0].len = button_get_len (BUTTON (progress_buttons[0].w));
 897 
 898     progress_buttons[1].w = WIDGET (button_new (y, 0, progress_buttons[1].action,
 899                                                 progress_buttons[1].flags, progress_buttons[1].text,
 900                                                 progress_button_callback));
 901     if (progress_buttons[1].len == -1)
 902         progress_buttons[1].len = button_get_len (BUTTON (progress_buttons[1].w));
 903 
 904     if (progress_buttons[2].len == -1)
 905     {
 906         /* create and destroy button to get it length */
 907         progress_buttons[2].w = WIDGET (button_new (y, 0, progress_buttons[2].action,
 908                                                     progress_buttons[2].flags,
 909                                                     progress_buttons[2].text,
 910                                                     progress_button_callback));
 911         progress_buttons[2].len = button_get_len (BUTTON (progress_buttons[2].w));
 912         widget_destroy (progress_buttons[2].w);
 913     }
 914     progress_buttons[2].w = progress_buttons[1].w;
 915 
 916     progress_buttons[3].w = WIDGET (button_new (y, 0, progress_buttons[3].action,
 917                                                 progress_buttons[3].flags, progress_buttons[3].text,
 918                                                 NULL));
 919     if (progress_buttons[3].len == -1)
 920         progress_buttons[3].len = button_get_len (BUTTON (progress_buttons[3].w));
 921 
 922     group_add_widget (g, progress_buttons[0].w);
 923     group_add_widget (g, progress_buttons[1].w);
 924     group_add_widget (g, progress_buttons[3].w);
 925 
 926     buttons_width = 2 +
 927         progress_buttons[0].len + MAX (progress_buttons[1].len, progress_buttons[2].len) +
 928         progress_buttons[3].len;
 929 
 930     /* adjust dialog sizes  */
 931     widget_set_size (w, w->y, w->x, y + 3, MAX (COLS * 2 / 3, buttons_width + 6));
 932 
 933     place_progress_buttons (ui->op_dlg, FALSE);
 934 
 935     widget_select (progress_buttons[0].w);
 936 
 937     /* We will manage the dialog without any help, that's why
 938        we have to call dlg_init */
 939     dlg_init (ui->op_dlg);
 940 }
 941 
 942 /* --------------------------------------------------------------------------------------------- */
 943 
 944 void
 945 file_op_context_destroy_ui (file_op_context_t * ctx)
     /* [previous][next][first][last][top][bottom][index][help]  */
 946 {
 947     if (ctx != NULL && ctx->ui != NULL)
 948     {
 949         file_op_context_ui_t *ui = (file_op_context_ui_t *) ctx->ui;
 950 
 951         dlg_run_done (ui->op_dlg);
 952         dlg_destroy (ui->op_dlg);
 953         MC_PTR_FREE (ctx->ui);
 954     }
 955 }
 956 
 957 /* --------------------------------------------------------------------------------------------- */
 958 /**
 959    show progressbar for file
 960  */
 961 
 962 void
 963 file_progress_show (file_op_context_t * ctx, off_t done, off_t total,
     /* [previous][next][first][last][top][bottom][index][help]  */
 964                     const char *stalled_msg, gboolean force_update)
 965 {
 966     file_op_context_ui_t *ui;
 967 
 968     if (!verbose || ctx == NULL || ctx->ui == NULL)
 969         return;
 970 
 971     ui = ctx->ui;
 972 
 973     if (total == 0)
 974     {
 975         gauge_show (ui->progress_file_gauge, FALSE);
 976         return;
 977     }
 978 
 979     gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
 980     gauge_show (ui->progress_file_gauge, TRUE);
 981 
 982     if (!force_update)
 983         return;
 984 
 985     if (!ui->showing_eta || ctx->eta_secs <= 0.5)
 986         label_set_text (ui->progress_file_label, stalled_msg);
 987     else
 988     {
 989         char buffer2[BUF_TINY];
 990 
 991         file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
 992         if (ctx->bps == 0)
 993             label_set_textv (ui->progress_file_label, "%s %s", buffer2, stalled_msg);
 994         else
 995         {
 996             char buffer3[BUF_TINY];
 997 
 998             file_bps_prepare_for_show (buffer3, ctx->bps);
 999             label_set_textv (ui->progress_file_label, "%s (%s) %s", buffer2, buffer3, stalled_msg);
1000         }
1001 
1002     }
1003 }
1004 
1005 /* --------------------------------------------------------------------------------------------- */
1006 
1007 void
1008 file_progress_show_count (file_op_context_t * ctx, size_t done, size_t total)
     /* [previous][next][first][last][top][bottom][index][help]  */
1009 {
1010     file_op_context_ui_t *ui;
1011 
1012     if (ctx == NULL || ctx->ui == NULL)
1013         return;
1014 
1015     ui = ctx->ui;
1016 
1017     if (ui->total_files_processed_label == NULL)
1018         return;
1019 
1020     if (ctx->progress_totals_computed)
1021         label_set_textv (ui->total_files_processed_label, _("Files processed: %zu/%zu"), done,
1022                          total);
1023     else
1024         label_set_textv (ui->total_files_processed_label, _("Files processed: %zu"), done);
1025 }
1026 
1027 /* --------------------------------------------------------------------------------------------- */
1028 
1029 void
1030 file_progress_show_total (file_op_total_context_t * tctx, file_op_context_t * ctx,
     /* [previous][next][first][last][top][bottom][index][help]  */
1031                           uintmax_t copied_bytes, gboolean show_summary)
1032 {
1033     char buffer2[BUF_TINY];
1034     char buffer3[BUF_TINY];
1035     file_op_context_ui_t *ui;
1036 
1037     if (ctx == NULL || ctx->ui == NULL)
1038         return;
1039 
1040     ui = ctx->ui;
1041 
1042     if (ui->progress_total_gauge != NULL)
1043     {
1044         if (ctx->progress_bytes == 0)
1045             gauge_show (ui->progress_total_gauge, FALSE);
1046         else
1047         {
1048             gauge_set_value (ui->progress_total_gauge, 1024,
1049                              (int) (1024 * copied_bytes / ctx->progress_bytes));
1050             gauge_show (ui->progress_total_gauge, TRUE);
1051         }
1052     }
1053 
1054     if (!show_summary && tctx->bps == 0)
1055         return;
1056 
1057     if (ui->time_label != NULL)
1058     {
1059         struct timeval tv_current;
1060         char buffer4[BUF_TINY];
1061 
1062         gettimeofday (&tv_current, NULL);
1063         file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec);
1064 
1065         if (ctx->progress_totals_computed)
1066         {
1067             file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
1068             if (tctx->bps == 0)
1069                 label_set_textv (ui->time_label, _("Time: %s %s"), buffer2, buffer3);
1070             else
1071             {
1072                 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1073                 label_set_textv (ui->time_label, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
1074             }
1075         }
1076         else
1077         {
1078             if (tctx->bps == 0)
1079                 label_set_textv (ui->time_label, _("Time: %s"), buffer2);
1080             else
1081             {
1082                 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1083                 label_set_textv (ui->time_label, _("Time: %s (%s)"), buffer2, buffer4);
1084             }
1085         }
1086     }
1087 
1088     if (ui->total_bytes_label != NULL)
1089     {
1090         size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
1091 
1092         if (!ctx->progress_totals_computed)
1093             hline_set_textv (ui->total_bytes_label, _(" Total: %s "), buffer2);
1094         else
1095         {
1096             size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
1097             hline_set_textv (ui->total_bytes_label, _(" Total: %s/%s "), buffer2, buffer3);
1098         }
1099     }
1100 }
1101 
1102 /* }}} */
1103 
1104 /* --------------------------------------------------------------------------------------------- */
1105 
1106 void
1107 file_progress_show_source (file_op_context_t * ctx, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1108 {
1109     file_op_context_ui_t *ui;
1110 
1111     if (ctx == NULL || ctx->ui == NULL)
1112         return;
1113 
1114     ui = ctx->ui;
1115 
1116     if (vpath != NULL)
1117     {
1118         char *s;
1119 
1120         s = vfs_path_tokens_get (vpath, -1, 1);
1121         label_set_text (ui->src_file_label, _("Source"));
1122         label_set_text (ui->src_file, truncFileString (ui->op_dlg, s));
1123         g_free (s);
1124     }
1125     else
1126     {
1127         label_set_text (ui->src_file_label, "");
1128         label_set_text (ui->src_file, "");
1129     }
1130 }
1131 
1132 /* --------------------------------------------------------------------------------------------- */
1133 
1134 void
1135 file_progress_show_target (file_op_context_t * ctx, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1136 {
1137     file_op_context_ui_t *ui;
1138 
1139     if (ctx == NULL || ctx->ui == NULL)
1140         return;
1141 
1142     ui = ctx->ui;
1143 
1144     if (vpath != NULL)
1145     {
1146         label_set_text (ui->tgt_file_label, _("Target"));
1147         label_set_text (ui->tgt_file, truncFileStringSecure (ui->op_dlg, vfs_path_as_str (vpath)));
1148     }
1149     else
1150     {
1151         label_set_text (ui->tgt_file_label, "");
1152         label_set_text (ui->tgt_file, "");
1153     }
1154 }
1155 
1156 /* --------------------------------------------------------------------------------------------- */
1157 
1158 gboolean
1159 file_progress_show_deleting (file_op_context_t * ctx, const char *s, size_t * count)
     /* [previous][next][first][last][top][bottom][index][help]  */
1160 {
1161     static guint64 timestamp = 0;
1162     /* update with 25 FPS rate */
1163     static const guint64 delay = G_USEC_PER_SEC / 25;
1164 
1165     gboolean ret;
1166 
1167     if (ctx == NULL || ctx->ui == NULL)
1168         return FALSE;
1169 
1170     ret = mc_time_elapsed (&timestamp, delay);
1171 
1172     if (ret)
1173     {
1174         file_op_context_ui_t *ui;
1175 
1176         ui = ctx->ui;
1177 
1178         if (ui->src_file_label != NULL)
1179             label_set_text (ui->src_file_label, _("Deleting"));
1180 
1181         label_set_text (ui->src_file, truncFileStringSecure (ui->op_dlg, s));
1182     }
1183 
1184     if (count != NULL)
1185         (*count)++;
1186 
1187     return ret;
1188 }
1189 
1190 /* --------------------------------------------------------------------------------------------- */
1191 
1192 FileProgressStatus
1193 file_progress_real_query_replace (file_op_context_t * ctx, enum OperationMode mode,
     /* [previous][next][first][last][top][bottom][index][help]  */
1194                                   const char *src, struct stat * src_stat,
1195                                   const char *dst, struct stat * dst_stat)
1196 {
1197     file_op_context_ui_t *ui;
1198     FileProgressStatus replace_with_zero;
1199 
1200     if (ctx == NULL || ctx->ui == NULL)
1201         return FILE_CONT;
1202 
1203     ui = ctx->ui;
1204 
1205     if (ui->replace_result == REPLACE_YES || ui->replace_result == REPLACE_NO
1206         || ui->replace_result == REPLACE_APPEND)
1207     {
1208         ui->src_filename = src;
1209         ui->src_stat = src_stat;
1210         ui->tgt_filename = dst;
1211         ui->dst_stat = dst_stat;
1212         ui->replace_result = overwrite_query_dialog (ctx, mode);
1213     }
1214 
1215     replace_with_zero = (src_stat->st_size == 0
1216                          && ui->dont_overwrite_with_zero) ? FILE_SKIP : FILE_CONT;
1217 
1218     switch (ui->replace_result)
1219     {
1220     case REPLACE_OLDER:
1221         do_refresh ();
1222         if (src_stat->st_mtime > dst_stat->st_mtime)
1223             return replace_with_zero;
1224         else
1225             return FILE_SKIP;
1226 
1227     case REPLACE_SIZE:
1228         do_refresh ();
1229         if (src_stat->st_size == dst_stat->st_size)
1230             return FILE_SKIP;
1231         else
1232             return replace_with_zero;
1233 
1234     case REPLACE_SMALLER:
1235         do_refresh ();
1236         if (src_stat->st_size > dst_stat->st_size)
1237             return FILE_CONT;
1238         else
1239             return FILE_SKIP;
1240 
1241     case REPLACE_ALL:
1242         do_refresh ();
1243         return replace_with_zero;
1244 
1245     case REPLACE_REGET:
1246         /* Careful: we fall through and set do_append */
1247         ctx->do_reget = dst_stat->st_size;
1248         MC_FALLTHROUGH;
1249 
1250     case REPLACE_APPEND:
1251         ctx->do_append = TRUE;
1252         MC_FALLTHROUGH;
1253 
1254     case REPLACE_YES:
1255         do_refresh ();
1256         return FILE_CONT;
1257 
1258     case REPLACE_NO:
1259     case REPLACE_NONE:
1260         do_refresh ();
1261         return FILE_SKIP;
1262 
1263     case REPLACE_ABORT:
1264     default:
1265         return FILE_ABORT;
1266     }
1267 }
1268 
1269 /* --------------------------------------------------------------------------------------------- */
1270 
1271 char *
1272 file_mask_dialog (file_op_context_t * ctx, FileOperation operation,
     /* [previous][next][first][last][top][bottom][index][help]  */
1273                   gboolean only_one,
1274                   const char *format, const void *text, const char *def_text, gboolean * do_bg)
1275 {
1276     size_t fmd_xlen;
1277     vfs_path_t *vpath;
1278     gboolean source_easy_patterns = easy_patterns;
1279     char fmd_buf[BUF_MEDIUM];
1280     char *dest_dir, *tmp;
1281     char *def_text_secure;
1282 
1283     if (ctx == NULL)
1284         return NULL;
1285 
1286     /* unselect checkbox if target filesystem doesn't support attributes */
1287     ctx->op_preserve = copymove_persistent_attr && filegui__check_attrs_on_fs (def_text);
1288     ctx->stable_symlinks = FALSE;
1289     *do_bg = FALSE;
1290 
1291     /* filter out a possible password from def_text */
1292     vpath = vfs_path_from_str_flags (def_text, only_one ? VPF_NO_CANON : VPF_NONE);
1293     tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1294     vfs_path_free (vpath);
1295 
1296     if (source_easy_patterns)
1297         def_text_secure = strutils_glob_escape (tmp);
1298     else
1299         def_text_secure = strutils_regex_escape (tmp);
1300     g_free (tmp);
1301 
1302     if (only_one)
1303     {
1304         int format_len, text_len;
1305         int max_len;
1306 
1307         format_len = str_term_width1 (format);
1308         text_len = str_term_width1 (text);
1309         max_len = COLS - 2 - 6;
1310 
1311         if (format_len + text_len <= max_len)
1312         {
1313             fmd_xlen = format_len + text_len + 6;
1314             fmd_xlen = MAX (fmd_xlen, 68);
1315         }
1316         else
1317         {
1318             text = str_trunc ((const char *) text, max_len - format_len);
1319             fmd_xlen = max_len + 6;
1320         }
1321 
1322         g_snprintf (fmd_buf, sizeof (fmd_buf), format, (const char *) text);
1323     }
1324     else
1325     {
1326         fmd_xlen = COLS * 2 / 3;
1327         fmd_xlen = MAX (fmd_xlen, 68);
1328         g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1329     }
1330 
1331     {
1332         char *source_mask, *orig_mask;
1333         int val;
1334         struct stat buf;
1335 
1336         quick_widget_t quick_widgets[] = {
1337             /* *INDENT-OFF* */
1338             QUICK_LABELED_INPUT (fmd_buf, input_label_above,
1339                                  easy_patterns ? "*" : "^(.*)$", "input-def", &source_mask,
1340                                  NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1341             QUICK_START_COLUMNS,
1342                 QUICK_SEPARATOR (FALSE),
1343             QUICK_NEXT_COLUMN,
1344                 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns, NULL),
1345             QUICK_STOP_COLUMNS,
1346             QUICK_LABELED_INPUT (N_("to:"), input_label_above,
1347                                  def_text_secure, "input2", &dest_dir, NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1348             QUICK_SEPARATOR (TRUE),
1349             QUICK_START_COLUMNS,
1350                 QUICK_CHECKBOX (N_("Follow &links"), &ctx->follow_links, NULL),
1351                 QUICK_CHECKBOX (N_("Preserve &attributes"), &ctx->op_preserve, NULL),
1352             QUICK_NEXT_COLUMN,
1353                 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx->dive_into_subdirs, NULL),
1354                 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx->stable_symlinks, NULL),
1355             QUICK_STOP_COLUMNS,
1356             QUICK_START_BUTTONS (TRUE, TRUE),
1357                 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
1358 #ifdef ENABLE_BACKGROUND
1359                 QUICK_BUTTON (N_("&Background"), B_USER, NULL, NULL),
1360 #endif /* ENABLE_BACKGROUND */
1361                 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1362             QUICK_END
1363             /* *INDENT-ON* */
1364         };
1365 
1366         quick_dialog_t qdlg = {
1367             -1, -1, fmd_xlen,
1368             op_names[operation], "[Mask Copy/Rename]",
1369             quick_widgets, NULL, NULL
1370         };
1371 
1372       ask_file_mask:
1373         val = quick_dialog_skip (&qdlg, 4);
1374 
1375         if (val == B_CANCEL)
1376         {
1377             g_free (def_text_secure);
1378             return NULL;
1379         }
1380 
1381         ctx->stat_func = ctx->follow_links ? mc_stat : mc_lstat;
1382 
1383         if (ctx->op_preserve)
1384         {
1385             ctx->preserve = TRUE;
1386             ctx->umask_kill = 0777777;
1387             ctx->preserve_uidgid = (geteuid () == 0);
1388         }
1389         else
1390         {
1391             mode_t i2;
1392 
1393             ctx->preserve = ctx->preserve_uidgid = FALSE;
1394             i2 = umask (0);
1395             umask (i2);
1396             ctx->umask_kill = i2 ^ 0777777;
1397         }
1398 
1399         if ((dest_dir == NULL) || (*dest_dir == '\0'))
1400         {
1401             g_free (def_text_secure);
1402             g_free (source_mask);
1403             g_free (dest_dir);
1404             return NULL;
1405         }
1406 
1407         ctx->search_handle = mc_search_new (source_mask, NULL);
1408 
1409         if (ctx->search_handle == NULL)
1410         {
1411             message (D_ERROR, MSG_ERROR, _("Invalid source pattern '%s'"), source_mask);
1412             g_free (dest_dir);
1413             g_free (source_mask);
1414             goto ask_file_mask;
1415         }
1416 
1417         g_free (def_text_secure);
1418         g_free (source_mask);
1419 
1420         ctx->search_handle->is_case_sensitive = TRUE;
1421         if (source_easy_patterns)
1422             ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1423         else
1424             ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1425 
1426         tmp = dest_dir;
1427         dest_dir = tilde_expand (tmp);
1428         g_free (tmp);
1429         vpath = vfs_path_from_str (dest_dir);
1430 
1431         ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1432         if (ctx->dest_mask == NULL)
1433             ctx->dest_mask = dest_dir;
1434         else
1435             ctx->dest_mask++;
1436 
1437         orig_mask = ctx->dest_mask;
1438 
1439         if (*ctx->dest_mask == '\0'
1440             || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1441                 && (!only_one
1442                     || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1443             || (ctx->dive_into_subdirs
1444                 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1445                     || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1446             ctx->dest_mask = g_strdup ("\\0");
1447         else
1448         {
1449             ctx->dest_mask = g_strdup (ctx->dest_mask);
1450             *orig_mask = '\0';
1451         }
1452 
1453         if (*dest_dir == '\0')
1454         {
1455             g_free (dest_dir);
1456             dest_dir = g_strdup ("./");
1457         }
1458 
1459         vfs_path_free (vpath);
1460 
1461         if (val == B_USER)
1462             *do_bg = TRUE;
1463     }
1464 
1465     return dest_dir;
1466 }
1467 
1468 /* --------------------------------------------------------------------------------------------- */

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