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     char buffer[BUF_TINY];
 968 
 969     if (!verbose || ctx == NULL || ctx->ui == NULL)
 970         return;
 971 
 972     ui = ctx->ui;
 973 
 974     if (total == 0)
 975     {
 976         gauge_show (ui->progress_file_gauge, FALSE);
 977         return;
 978     }
 979 
 980     gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
 981     gauge_show (ui->progress_file_gauge, TRUE);
 982 
 983     if (!force_update)
 984         return;
 985 
 986     if (ui->showing_eta && ctx->eta_secs > 0.5)
 987     {
 988         char buffer2[BUF_TINY];
 989 
 990         file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
 991         if (ctx->bps == 0)
 992             g_snprintf (buffer, sizeof (buffer), "%s %s", buffer2, stalled_msg);
 993         else
 994         {
 995             char buffer3[BUF_TINY];
 996 
 997             file_bps_prepare_for_show (buffer3, ctx->bps);
 998             g_snprintf (buffer, sizeof (buffer), "%s (%s) %s", buffer2, buffer3, stalled_msg);
 999         }
1000     }
1001     else
1002         g_snprintf (buffer, sizeof (buffer), "%s", stalled_msg);
1003 
1004     label_set_text (ui->progress_file_label, buffer);
1005 }
1006 
1007 /* --------------------------------------------------------------------------------------------- */
1008 
1009 void
1010 file_progress_show_count (file_op_context_t * ctx, size_t done, size_t total)
     /* [previous][next][first][last][top][bottom][index][help]  */
1011 {
1012     char buffer[BUF_TINY];
1013     file_op_context_ui_t *ui;
1014 
1015     if (ctx == NULL || ctx->ui == NULL)
1016         return;
1017 
1018     ui = ctx->ui;
1019 
1020     if (ui->total_files_processed_label == NULL)
1021         return;
1022 
1023     if (ctx->progress_totals_computed)
1024         g_snprintf (buffer, sizeof (buffer), _("Files processed: %zu/%zu"), done, total);
1025     else
1026         g_snprintf (buffer, sizeof (buffer), _("Files processed: %zu"), done);
1027     label_set_text (ui->total_files_processed_label, buffer);
1028 }
1029 
1030 /* --------------------------------------------------------------------------------------------- */
1031 
1032 void
1033 file_progress_show_total (file_op_total_context_t * tctx, file_op_context_t * ctx,
     /* [previous][next][first][last][top][bottom][index][help]  */
1034                           uintmax_t copied_bytes, gboolean show_summary)
1035 {
1036     char buffer[BUF_TINY];
1037     char buffer2[BUF_TINY];
1038     char buffer3[BUF_TINY];
1039     file_op_context_ui_t *ui;
1040 
1041     if (ctx == NULL || ctx->ui == NULL)
1042         return;
1043 
1044     ui = ctx->ui;
1045 
1046     if (ui->progress_total_gauge != NULL)
1047     {
1048         if (ctx->progress_bytes == 0)
1049             gauge_show (ui->progress_total_gauge, FALSE);
1050         else
1051         {
1052             gauge_set_value (ui->progress_total_gauge, 1024,
1053                              (int) (1024 * copied_bytes / ctx->progress_bytes));
1054             gauge_show (ui->progress_total_gauge, TRUE);
1055         }
1056     }
1057 
1058     if (!show_summary && tctx->bps == 0)
1059         return;
1060 
1061     if (ui->time_label != NULL)
1062     {
1063         struct timeval tv_current;
1064         char buffer4[BUF_TINY];
1065 
1066         gettimeofday (&tv_current, NULL);
1067         file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec);
1068 
1069         if (ctx->progress_totals_computed)
1070         {
1071             file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
1072             if (tctx->bps == 0)
1073                 g_snprintf (buffer, sizeof (buffer), _("Time: %s %s"), buffer2, buffer3);
1074             else
1075             {
1076                 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1077                 g_snprintf (buffer, sizeof (buffer), _("Time: %s %s (%s)"), buffer2, buffer3,
1078                             buffer4);
1079             }
1080         }
1081         else
1082         {
1083             if (tctx->bps == 0)
1084                 g_snprintf (buffer, sizeof (buffer), _("Time: %s"), buffer2);
1085             else
1086             {
1087                 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1088                 g_snprintf (buffer, sizeof (buffer), _("Time: %s (%s)"), buffer2, buffer4);
1089             }
1090         }
1091 
1092         label_set_text (ui->time_label, buffer);
1093     }
1094 
1095     if (ui->total_bytes_label != NULL)
1096     {
1097         size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
1098 
1099         if (!ctx->progress_totals_computed)
1100             g_snprintf (buffer, sizeof (buffer), _(" Total: %s "), buffer2);
1101         else
1102         {
1103             size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
1104             g_snprintf (buffer, sizeof (buffer), _(" Total: %s/%s "), buffer2, buffer3);
1105         }
1106 
1107         hline_set_text (ui->total_bytes_label, buffer);
1108     }
1109 }
1110 
1111 /* }}} */
1112 
1113 /* --------------------------------------------------------------------------------------------- */
1114 
1115 void
1116 file_progress_show_source (file_op_context_t * ctx, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1117 {
1118     file_op_context_ui_t *ui;
1119 
1120     if (ctx == NULL || ctx->ui == NULL)
1121         return;
1122 
1123     ui = ctx->ui;
1124 
1125     if (vpath != NULL)
1126     {
1127         char *s;
1128 
1129         s = vfs_path_tokens_get (vpath, -1, 1);
1130         label_set_text (ui->src_file_label, _("Source"));
1131         label_set_text (ui->src_file, truncFileString (ui->op_dlg, s));
1132         g_free (s);
1133     }
1134     else
1135     {
1136         label_set_text (ui->src_file_label, "");
1137         label_set_text (ui->src_file, "");
1138     }
1139 }
1140 
1141 /* --------------------------------------------------------------------------------------------- */
1142 
1143 void
1144 file_progress_show_target (file_op_context_t * ctx, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1145 {
1146     file_op_context_ui_t *ui;
1147 
1148     if (ctx == NULL || ctx->ui == NULL)
1149         return;
1150 
1151     ui = ctx->ui;
1152 
1153     if (vpath != NULL)
1154     {
1155         label_set_text (ui->tgt_file_label, _("Target"));
1156         label_set_text (ui->tgt_file, truncFileStringSecure (ui->op_dlg, vfs_path_as_str (vpath)));
1157     }
1158     else
1159     {
1160         label_set_text (ui->tgt_file_label, "");
1161         label_set_text (ui->tgt_file, "");
1162     }
1163 }
1164 
1165 /* --------------------------------------------------------------------------------------------- */
1166 
1167 gboolean
1168 file_progress_show_deleting (file_op_context_t * ctx, const char *s, size_t * count)
     /* [previous][next][first][last][top][bottom][index][help]  */
1169 {
1170     static guint64 timestamp = 0;
1171     /* update with 25 FPS rate */
1172     static const guint64 delay = G_USEC_PER_SEC / 25;
1173 
1174     gboolean ret;
1175 
1176     if (ctx == NULL || ctx->ui == NULL)
1177         return FALSE;
1178 
1179     ret = mc_time_elapsed (&timestamp, delay);
1180 
1181     if (ret)
1182     {
1183         file_op_context_ui_t *ui;
1184 
1185         ui = ctx->ui;
1186 
1187         if (ui->src_file_label != NULL)
1188             label_set_text (ui->src_file_label, _("Deleting"));
1189 
1190         label_set_text (ui->src_file, truncFileStringSecure (ui->op_dlg, s));
1191     }
1192 
1193     if (count != NULL)
1194         (*count)++;
1195 
1196     return ret;
1197 }
1198 
1199 /* --------------------------------------------------------------------------------------------- */
1200 
1201 FileProgressStatus
1202 file_progress_real_query_replace (file_op_context_t * ctx, enum OperationMode mode,
     /* [previous][next][first][last][top][bottom][index][help]  */
1203                                   const char *src, struct stat * src_stat,
1204                                   const char *dst, struct stat * dst_stat)
1205 {
1206     file_op_context_ui_t *ui;
1207     FileProgressStatus replace_with_zero;
1208 
1209     if (ctx == NULL || ctx->ui == NULL)
1210         return FILE_CONT;
1211 
1212     ui = ctx->ui;
1213 
1214     if (ui->replace_result == REPLACE_YES || ui->replace_result == REPLACE_NO
1215         || ui->replace_result == REPLACE_APPEND)
1216     {
1217         ui->src_filename = src;
1218         ui->src_stat = src_stat;
1219         ui->tgt_filename = dst;
1220         ui->dst_stat = dst_stat;
1221         ui->replace_result = overwrite_query_dialog (ctx, mode);
1222     }
1223 
1224     replace_with_zero = (src_stat->st_size == 0
1225                          && ui->dont_overwrite_with_zero) ? FILE_SKIP : FILE_CONT;
1226 
1227     switch (ui->replace_result)
1228     {
1229     case REPLACE_OLDER:
1230         do_refresh ();
1231         if (src_stat->st_mtime > dst_stat->st_mtime)
1232             return replace_with_zero;
1233         else
1234             return FILE_SKIP;
1235 
1236     case REPLACE_SIZE:
1237         do_refresh ();
1238         if (src_stat->st_size == dst_stat->st_size)
1239             return FILE_SKIP;
1240         else
1241             return replace_with_zero;
1242 
1243     case REPLACE_SMALLER:
1244         do_refresh ();
1245         if (src_stat->st_size > dst_stat->st_size)
1246             return FILE_CONT;
1247         else
1248             return FILE_SKIP;
1249 
1250     case REPLACE_ALL:
1251         do_refresh ();
1252         return replace_with_zero;
1253 
1254     case REPLACE_REGET:
1255         /* Careful: we fall through and set do_append */
1256         ctx->do_reget = dst_stat->st_size;
1257         MC_FALLTHROUGH;
1258 
1259     case REPLACE_APPEND:
1260         ctx->do_append = TRUE;
1261         MC_FALLTHROUGH;
1262 
1263     case REPLACE_YES:
1264         do_refresh ();
1265         return FILE_CONT;
1266 
1267     case REPLACE_NO:
1268     case REPLACE_NONE:
1269         do_refresh ();
1270         return FILE_SKIP;
1271 
1272     case REPLACE_ABORT:
1273     default:
1274         return FILE_ABORT;
1275     }
1276 }
1277 
1278 /* --------------------------------------------------------------------------------------------- */
1279 
1280 char *
1281 file_mask_dialog (file_op_context_t * ctx, FileOperation operation,
     /* [previous][next][first][last][top][bottom][index][help]  */
1282                   gboolean only_one,
1283                   const char *format, const void *text, const char *def_text, gboolean * do_bg)
1284 {
1285     size_t fmd_xlen;
1286     vfs_path_t *vpath;
1287     gboolean source_easy_patterns = easy_patterns;
1288     char fmd_buf[BUF_MEDIUM];
1289     char *dest_dir, *tmp;
1290     char *def_text_secure;
1291 
1292     if (ctx == NULL)
1293         return NULL;
1294 
1295     /* unselect checkbox if target filesystem doesn't support attributes */
1296     ctx->op_preserve = copymove_persistent_attr && filegui__check_attrs_on_fs (def_text);
1297     ctx->stable_symlinks = FALSE;
1298     *do_bg = FALSE;
1299 
1300     /* filter out a possible password from def_text */
1301     vpath = vfs_path_from_str_flags (def_text, only_one ? VPF_NO_CANON : VPF_NONE);
1302     tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1303     vfs_path_free (vpath);
1304 
1305     if (source_easy_patterns)
1306         def_text_secure = strutils_glob_escape (tmp);
1307     else
1308         def_text_secure = strutils_regex_escape (tmp);
1309     g_free (tmp);
1310 
1311     if (only_one)
1312     {
1313         int format_len, text_len;
1314         int max_len;
1315 
1316         format_len = str_term_width1 (format);
1317         text_len = str_term_width1 (text);
1318         max_len = COLS - 2 - 6;
1319 
1320         if (format_len + text_len <= max_len)
1321         {
1322             fmd_xlen = format_len + text_len + 6;
1323             fmd_xlen = MAX (fmd_xlen, 68);
1324         }
1325         else
1326         {
1327             text = str_trunc ((const char *) text, max_len - format_len);
1328             fmd_xlen = max_len + 6;
1329         }
1330 
1331         g_snprintf (fmd_buf, sizeof (fmd_buf), format, (const char *) text);
1332     }
1333     else
1334     {
1335         fmd_xlen = COLS * 2 / 3;
1336         fmd_xlen = MAX (fmd_xlen, 68);
1337         g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1338     }
1339 
1340     {
1341         char *source_mask, *orig_mask;
1342         int val;
1343         struct stat buf;
1344 
1345         quick_widget_t quick_widgets[] = {
1346             /* *INDENT-OFF* */
1347             QUICK_LABELED_INPUT (fmd_buf, input_label_above,
1348                                  easy_patterns ? "*" : "^(.*)$", "input-def", &source_mask,
1349                                  NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1350             QUICK_START_COLUMNS,
1351                 QUICK_SEPARATOR (FALSE),
1352             QUICK_NEXT_COLUMN,
1353                 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns, NULL),
1354             QUICK_STOP_COLUMNS,
1355             QUICK_LABELED_INPUT (N_("to:"), input_label_above,
1356                                  def_text_secure, "input2", &dest_dir, NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1357             QUICK_SEPARATOR (TRUE),
1358             QUICK_START_COLUMNS,
1359                 QUICK_CHECKBOX (N_("Follow &links"), &ctx->follow_links, NULL),
1360                 QUICK_CHECKBOX (N_("Preserve &attributes"), &ctx->op_preserve, NULL),
1361             QUICK_NEXT_COLUMN,
1362                 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx->dive_into_subdirs, NULL),
1363                 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx->stable_symlinks, NULL),
1364             QUICK_STOP_COLUMNS,
1365             QUICK_START_BUTTONS (TRUE, TRUE),
1366                 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
1367 #ifdef ENABLE_BACKGROUND
1368                 QUICK_BUTTON (N_("&Background"), B_USER, NULL, NULL),
1369 #endif /* ENABLE_BACKGROUND */
1370                 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1371             QUICK_END
1372             /* *INDENT-ON* */
1373         };
1374 
1375         quick_dialog_t qdlg = {
1376             -1, -1, fmd_xlen,
1377             op_names[operation], "[Mask Copy/Rename]",
1378             quick_widgets, NULL, NULL
1379         };
1380 
1381       ask_file_mask:
1382         val = quick_dialog_skip (&qdlg, 4);
1383 
1384         if (val == B_CANCEL)
1385         {
1386             g_free (def_text_secure);
1387             return NULL;
1388         }
1389 
1390         ctx->stat_func = ctx->follow_links ? mc_stat : mc_lstat;
1391 
1392         if (ctx->op_preserve)
1393         {
1394             ctx->preserve = TRUE;
1395             ctx->umask_kill = 0777777;
1396             ctx->preserve_uidgid = (geteuid () == 0);
1397         }
1398         else
1399         {
1400             mode_t i2;
1401 
1402             ctx->preserve = ctx->preserve_uidgid = FALSE;
1403             i2 = umask (0);
1404             umask (i2);
1405             ctx->umask_kill = i2 ^ 0777777;
1406         }
1407 
1408         if ((dest_dir == NULL) || (*dest_dir == '\0'))
1409         {
1410             g_free (def_text_secure);
1411             g_free (source_mask);
1412             g_free (dest_dir);
1413             return NULL;
1414         }
1415 
1416         ctx->search_handle = mc_search_new (source_mask, NULL);
1417 
1418         if (ctx->search_handle == NULL)
1419         {
1420             message (D_ERROR, MSG_ERROR, _("Invalid source pattern '%s'"), source_mask);
1421             g_free (dest_dir);
1422             g_free (source_mask);
1423             goto ask_file_mask;
1424         }
1425 
1426         g_free (def_text_secure);
1427         g_free (source_mask);
1428 
1429         ctx->search_handle->is_case_sensitive = TRUE;
1430         if (source_easy_patterns)
1431             ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1432         else
1433             ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1434 
1435         tmp = dest_dir;
1436         dest_dir = tilde_expand (tmp);
1437         g_free (tmp);
1438         vpath = vfs_path_from_str (dest_dir);
1439 
1440         ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1441         if (ctx->dest_mask == NULL)
1442             ctx->dest_mask = dest_dir;
1443         else
1444             ctx->dest_mask++;
1445 
1446         orig_mask = ctx->dest_mask;
1447 
1448         if (*ctx->dest_mask == '\0'
1449             || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1450                 && (!only_one
1451                     || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1452             || (ctx->dive_into_subdirs
1453                 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1454                     || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1455             ctx->dest_mask = g_strdup ("\\0");
1456         else
1457         {
1458             ctx->dest_mask = g_strdup (ctx->dest_mask);
1459             *orig_mask = '\0';
1460         }
1461 
1462         if (*dest_dir == '\0')
1463         {
1464             g_free (dest_dir);
1465             dest_dir = g_strdup ("./");
1466         }
1467 
1468         vfs_path_free (vpath);
1469 
1470         if (val == B_USER)
1471             *do_bg = TRUE;
1472     }
1473 
1474     return dest_dir;
1475 }
1476 
1477 /* --------------------------------------------------------------------------------------------- */

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