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-2021
  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 "filemanager.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     const char *cs1;
 496     char s2[BUF_SMALL];
 497     int w, bw1, bw2;
 498     unsigned short i;
 499 
 500     gboolean do_append = FALSE, do_reget = FALSE;
 501     unsigned long yes_id, no_id;
 502     int result;
 503 
 504     if (mode == Foreground)
 505         title = _("File exists");
 506     else
 507         title = _("Background process: File exists");
 508 
 509 #ifdef ENABLE_NLS
 510     {
 511         const unsigned short num = G_N_ELEMENTS (dlg_widgets);
 512 
 513         for (i = 0; i < num; i++)
 514             if (dlg_widgets[i].text != NULL)
 515                 dlg_widgets[i].text = _(dlg_widgets[i].text);
 516     }
 517 #endif /* ENABLE_NLS */
 518 
 519     /* create widgets to get their real widths */
 520     /* new file */
 521     NEW_LABEL (0, dlg_widgets[0].text);
 522     /* new file name */
 523     p = vfs_path_from_str (ui->src_filename);
 524     s1 = vfs_path_to_str_flags (p, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
 525     NEW_LABEL (1, s1);
 526     vfs_path_free (p, TRUE);
 527     g_free (s1);
 528     /* new file size */
 529     size_trunc_len (s2, sizeof (s2), ui->src_stat->st_size, 0, panels_options.kilobyte_si);
 530     NEW_LABEL (2, s2);
 531     /* new file modification date & time */
 532     cs1 = file_date (ui->src_stat->st_mtime);
 533     NEW_LABEL (3, cs1);
 534 
 535     /* existing file */
 536     NEW_LABEL (4, dlg_widgets[4].text);
 537     /* existing file name */
 538     p = vfs_path_from_str (ui->tgt_filename);
 539     s1 = vfs_path_to_str_flags (p, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
 540     NEW_LABEL (5, s1);
 541     vfs_path_free (p, TRUE);
 542     g_free (s1);
 543     /* existing file size */
 544     size_trunc_len (s2, sizeof (s2), ui->dst_stat->st_size, 0, panels_options.kilobyte_si);
 545     NEW_LABEL (6, s2);
 546     /* existing file modification date & time */
 547     cs1 = file_date (ui->dst_stat->st_mtime);
 548     NEW_LABEL (7, cs1);
 549 
 550     /* will "Append" and "Reget" buttons be in the dialog? */
 551     do_append = !S_ISDIR (ui->dst_stat->st_mode);
 552     do_reget = do_append && ctx->operation == OP_COPY && ui->dst_stat->st_size != 0
 553         && ui->src_stat->st_size > ui->dst_stat->st_size;
 554 
 555     NEW_LABEL (8, dlg_widgets[8].text);
 556     NEW_BUTTON (9);
 557     NEW_BUTTON (10);
 558     if (do_append)
 559         NEW_BUTTON (11);
 560     if (do_reget)
 561         NEW_BUTTON (12);
 562 
 563     NEW_LABEL (13, dlg_widgets[13].text);
 564     dlg_widgets[14].widget =
 565         WIDGET (check_new (dlg_widgets[14].y, dlg_widgets[14].x, FALSE, dlg_widgets[14].text));
 566     for (i = 15; i <= 20; i++)
 567         NEW_BUTTON (i);
 568 
 569     /* place widgets */
 570     dlg_width -= 2 * (2 + gap); /* inside frame */
 571 
 572     /* perhaps longest line is buttons */
 573     bw1 = WCOLS (9) + gap + WCOLS (10);
 574     if (do_append)
 575         bw1 += gap + WCOLS (11);
 576     if (do_reget)
 577         bw1 += gap + WCOLS (12);
 578     dlg_width = MAX (dlg_width, bw1);
 579 
 580     bw2 = WCOLS (15);
 581     for (i = 16; i <= 19; i++)
 582         bw2 += gap + WCOLS (i);
 583     dlg_width = MAX (dlg_width, bw2);
 584 
 585     dlg_width = MAX (dlg_width, WCOLS (8));
 586     dlg_width = MAX (dlg_width, WCOLS (13));
 587     dlg_width = MAX (dlg_width, WCOLS (14));
 588 
 589     /* truncate file names */
 590     w = WCOLS (0) + gap + WCOLS (1);
 591     if (w > dlg_width)
 592     {
 593         WLabel *l = LABEL (W (1));
 594 
 595         w = dlg_width - gap - WCOLS (0);
 596         label_set_text (l, str_trunc (l->text, w));
 597     }
 598 
 599     w = WCOLS (4) + gap + WCOLS (5);
 600     if (w > dlg_width)
 601     {
 602         WLabel *l = LABEL (W (5));
 603 
 604         w = dlg_width - gap - WCOLS (4);
 605         label_set_text (l, str_trunc (l->text, w));
 606     }
 607 
 608     /* real dlalog width */
 609     dlg_width += 2 * (2 + gap);
 610 
 611     WX (1) = WX (0) + WCOLS (0) + gap;
 612     WX (5) = WX (4) + WCOLS (4) + gap;
 613 
 614     /* sizes: right alignment */
 615     WX (2) = dlg_width / 2 - WCOLS (2);
 616     WX (6) = dlg_width / 2 - WCOLS (6);
 617 
 618     w = dlg_width - (2 + gap);  /* right bound */
 619 
 620     /* date & time */
 621     WX (3) = w - WCOLS (3);
 622     WX (7) = w - WCOLS (7);
 623 
 624     /* buttons: center alignment */
 625     WX (9) = dlg_width / 2 - bw1 / 2;
 626     WX (10) = WX (9) + WCOLS (9) + gap;
 627     if (do_append)
 628         WX (11) = WX (10) + WCOLS (10) + gap;
 629     if (do_reget)
 630         WX (12) = WX (11) + WCOLS (11) + gap;
 631 
 632     WX (15) = dlg_width / 2 - bw2 / 2;
 633     for (i = 16; i <= 19; i++)
 634         WX (i) = WX (i - 1) + WCOLS (i - 1) + gap;
 635 
 636     /* TODO: write help (ticket #3970) */
 637     ui->replace_dlg =
 638         dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, alarm_colors, NULL, NULL,
 639                     "[Replace]", title);
 640     wd = WIDGET (ui->replace_dlg);
 641     g = GROUP (ui->replace_dlg);
 642 
 643     /* file info */
 644     for (i = 0; i <= 7; i++)
 645         ADD_LABEL (i);
 646     group_add_widget (g, hline_new (W (7)->y - wd->y + 1, -1, -1));
 647 
 648     /* label & buttons */
 649     ADD_LABEL (8);              /* Overwrite this file? */
 650     yes_id = ADD_BUTTON (9);    /* Yes */
 651     no_id = ADD_BUTTON (10);    /* No */
 652     if (do_append)
 653         ADD_BUTTON (11);        /* Append */
 654     if (do_reget)
 655         ADD_BUTTON (12);        /* Reget */
 656     group_add_widget (g, hline_new (W (10)->y - wd->y + 1, -1, -1));
 657 
 658     /* label & buttons */
 659     ADD_LABEL (13);             /* Overwrite all files? */
 660     group_add_widget (g, dlg_widgets[14].widget);
 661     for (i = 15; i <= 19; i++)
 662         ADD_BUTTON (i);
 663     group_add_widget (g, hline_new (W (19)->y - wd->y + 1, -1, -1));
 664 
 665     ADD_BUTTON (20);            /* Abort */
 666 
 667     group_select_widget_by_id (g, safe_overwrite ? no_id : yes_id);
 668 
 669     result = dlg_run (ui->replace_dlg);
 670 
 671     if (result != B_CANCEL)
 672         ui->dont_overwrite_with_zero = CHECK (dlg_widgets[14].widget)->state;
 673 
 674     widget_destroy (wd);
 675 
 676     return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
 677 
 678 #undef ADD_BUTTON
 679 #undef NEW_BUTTON
 680 #undef ADD_LABEL
 681 #undef NEW_LABEL
 682 #undef WCOLS
 683 #undef WX
 684 #undef W
 685 }
 686 
 687 /* --------------------------------------------------------------------------------------------- */
 688 
 689 static gboolean
 690 is_wildcarded (const char *p)
     /* [previous][next][first][last][top][bottom][index][help]  */
 691 {
 692     gboolean escaped = FALSE;
 693 
 694     for (; *p != '\0'; p++)
 695     {
 696         if (*p == '\\')
 697         {
 698             if (p[1] >= '1' && p[1] <= '9' && !escaped)
 699                 return TRUE;
 700             escaped = !escaped;
 701         }
 702         else
 703         {
 704             if ((*p == '*' || *p == '?') && !escaped)
 705                 return TRUE;
 706             escaped = FALSE;
 707         }
 708     }
 709     return FALSE;
 710 }
 711 
 712 /* --------------------------------------------------------------------------------------------- */
 713 
 714 static void
 715 place_progress_buttons (WDialog * h, gboolean suspended)
     /* [previous][next][first][last][top][bottom][index][help]  */
 716 {
 717     const size_t i = suspended ? 2 : 1;
 718     Widget *w = WIDGET (h);
 719     int buttons_width;
 720 
 721     buttons_width = 2 + progress_buttons[0].len + progress_buttons[3].len;
 722     buttons_width += progress_buttons[i].len;
 723     button_set_text (BUTTON (progress_buttons[i].w), progress_buttons[i].text);
 724 
 725     progress_buttons[0].w->x = w->x + (w->cols - buttons_width) / 2;
 726     progress_buttons[i].w->x = progress_buttons[0].w->x + progress_buttons[0].len + 1;
 727     progress_buttons[3].w->x = progress_buttons[i].w->x + progress_buttons[i].len + 1;
 728 }
 729 
 730 /* --------------------------------------------------------------------------------------------- */
 731 
 732 static int
 733 progress_button_callback (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 734 {
 735     (void) button;
 736     (void) action;
 737 
 738     /* don't close dialog in any case */
 739     return 0;
 740 }
 741 
 742 /* --------------------------------------------------------------------------------------------- */
 743 /*** public functions ****************************************************************************/
 744 /* --------------------------------------------------------------------------------------------- */
 745 
 746 FileProgressStatus
 747 check_progress_buttons (file_op_context_t * ctx)
     /* [previous][next][first][last][top][bottom][index][help]  */
 748 {
 749     int c;
 750     Gpm_Event event;
 751     file_op_context_ui_t *ui;
 752 
 753     if (ctx == NULL || ctx->ui == NULL)
 754         return FILE_CONT;
 755 
 756     ui = ctx->ui;
 757 
 758   get_event:
 759     event.x = -1;               /* Don't show the GPM cursor */
 760     c = tty_get_event (&event, FALSE, ctx->suspended);
 761     if (c == EV_NONE)
 762         return FILE_CONT;
 763 
 764     /* Reinitialize to avoid old values after events other than selecting a button */
 765     ui->op_dlg->ret_value = FILE_CONT;
 766 
 767     dlg_process_event (ui->op_dlg, c, &event);
 768     switch (ui->op_dlg->ret_value)
 769     {
 770     case FILE_SKIP:
 771         if (ctx->suspended)
 772         {
 773             /* redraw dialog in case of Skip after Suspend */
 774             place_progress_buttons (ui->op_dlg, FALSE);
 775             widget_draw (WIDGET (ui->op_dlg));
 776         }
 777         ctx->suspended = FALSE;
 778         return FILE_SKIP;
 779     case B_CANCEL:
 780     case FILE_ABORT:
 781         ctx->suspended = FALSE;
 782         return FILE_ABORT;
 783     case FILE_SUSPEND:
 784         ctx->suspended = !ctx->suspended;
 785         place_progress_buttons (ui->op_dlg, ctx->suspended);
 786         widget_draw (WIDGET (ui->op_dlg));
 787         MC_FALLTHROUGH;
 788     default:
 789         if (ctx->suspended)
 790             goto get_event;
 791         return FILE_CONT;
 792     }
 793 }
 794 
 795 /* --------------------------------------------------------------------------------------------- */
 796 /* {{{ File progress display routines */
 797 
 798 void
 799 file_op_context_create_ui (file_op_context_t * ctx, gboolean with_eta,
     /* [previous][next][first][last][top][bottom][index][help]  */
 800                            filegui_dialog_type_t dialog_type)
 801 {
 802     file_op_context_ui_t *ui;
 803     Widget *w;
 804     WGroup *g;
 805     int buttons_width;
 806     int dlg_width = 58, dlg_height = 17;
 807     int y = 2, x = 3;
 808 
 809     if (ctx == NULL || ctx->ui != NULL)
 810         return;
 811 
 812 #ifdef ENABLE_NLS
 813     if (progress_buttons[0].len == -1)
 814     {
 815         size_t i;
 816 
 817         for (i = 0; i < G_N_ELEMENTS (progress_buttons); i++)
 818             progress_buttons[i].text = _(progress_buttons[i].text);
 819     }
 820 #endif
 821 
 822     ctx->dialog_type = dialog_type;
 823     ctx->recursive_result = RECURSIVE_YES;
 824     ctx->ui = g_new0 (file_op_context_ui_t, 1);
 825 
 826     ui = ctx->ui;
 827     ui->replace_result = REPLACE_YES;
 828 
 829     ui->op_dlg =
 830         dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, dialog_colors, NULL,
 831                     NULL, NULL, op_names[ctx->operation]);
 832     w = WIDGET (ui->op_dlg);
 833     g = GROUP (ui->op_dlg);
 834 
 835     if (dialog_type != FILEGUI_DIALOG_DELETE_ITEM)
 836     {
 837         ui->showing_eta = with_eta && ctx->progress_totals_computed;
 838         ui->showing_bps = with_eta;
 839 
 840         ui->src_file_label = label_new (y++, x, "");
 841         group_add_widget (g, ui->src_file_label);
 842 
 843         ui->src_file = label_new (y++, x, "");
 844         group_add_widget (g, ui->src_file);
 845 
 846         ui->tgt_file_label = label_new (y++, x, "");
 847         group_add_widget (g, ui->tgt_file_label);
 848 
 849         ui->tgt_file = label_new (y++, x, "");
 850         group_add_widget (g, ui->tgt_file);
 851 
 852         ui->progress_file_gauge = gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
 853         if (!classic_progressbar && (current_panel == right_panel))
 854             ui->progress_file_gauge->from_left_to_right = FALSE;
 855         group_add_widget_autopos (g, ui->progress_file_gauge, WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
 856 
 857         ui->progress_file_label = label_new (y++, x, "");
 858         group_add_widget (g, ui->progress_file_label);
 859 
 860         if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
 861         {
 862             ui->total_bytes_label = hline_new (y++, -1, -1);
 863             group_add_widget (g, ui->total_bytes_label);
 864 
 865             if (ctx->progress_totals_computed)
 866             {
 867                 ui->progress_total_gauge =
 868                     gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
 869                 if (!classic_progressbar && (current_panel == right_panel))
 870                     ui->progress_total_gauge->from_left_to_right = FALSE;
 871                 group_add_widget_autopos (g, ui->progress_total_gauge,
 872                                           WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
 873             }
 874 
 875             ui->total_files_processed_label = label_new (y++, x, "");
 876             group_add_widget (g, ui->total_files_processed_label);
 877 
 878             ui->time_label = label_new (y++, x, "");
 879             group_add_widget (g, ui->time_label);
 880         }
 881     }
 882     else
 883     {
 884         ui->src_file = label_new (y++, x, "");
 885         group_add_widget (g, ui->src_file);
 886 
 887         ui->total_files_processed_label = label_new (y++, x, "");
 888         group_add_widget (g, ui->total_files_processed_label);
 889     }
 890 
 891     group_add_widget (g, hline_new (y++, -1, -1));
 892 
 893     progress_buttons[0].w = WIDGET (button_new (y, 0, progress_buttons[0].action,
 894                                                 progress_buttons[0].flags, progress_buttons[0].text,
 895                                                 progress_button_callback));
 896     if (progress_buttons[0].len == -1)
 897         progress_buttons[0].len = button_get_len (BUTTON (progress_buttons[0].w));
 898 
 899     progress_buttons[1].w = WIDGET (button_new (y, 0, progress_buttons[1].action,
 900                                                 progress_buttons[1].flags, progress_buttons[1].text,
 901                                                 progress_button_callback));
 902     if (progress_buttons[1].len == -1)
 903         progress_buttons[1].len = button_get_len (BUTTON (progress_buttons[1].w));
 904 
 905     if (progress_buttons[2].len == -1)
 906     {
 907         /* create and destroy button to get it length */
 908         progress_buttons[2].w = WIDGET (button_new (y, 0, progress_buttons[2].action,
 909                                                     progress_buttons[2].flags,
 910                                                     progress_buttons[2].text,
 911                                                     progress_button_callback));
 912         progress_buttons[2].len = button_get_len (BUTTON (progress_buttons[2].w));
 913         widget_destroy (progress_buttons[2].w);
 914     }
 915     progress_buttons[2].w = progress_buttons[1].w;
 916 
 917     progress_buttons[3].w = WIDGET (button_new (y, 0, progress_buttons[3].action,
 918                                                 progress_buttons[3].flags, progress_buttons[3].text,
 919                                                 NULL));
 920     if (progress_buttons[3].len == -1)
 921         progress_buttons[3].len = button_get_len (BUTTON (progress_buttons[3].w));
 922 
 923     group_add_widget (g, progress_buttons[0].w);
 924     group_add_widget (g, progress_buttons[1].w);
 925     group_add_widget (g, progress_buttons[3].w);
 926 
 927     buttons_width = 2 +
 928         progress_buttons[0].len + MAX (progress_buttons[1].len, progress_buttons[2].len) +
 929         progress_buttons[3].len;
 930 
 931     /* adjust dialog sizes  */
 932     widget_set_size (w, w->y, w->x, y + 3, MAX (COLS * 2 / 3, buttons_width + 6));
 933 
 934     place_progress_buttons (ui->op_dlg, FALSE);
 935 
 936     widget_select (progress_buttons[0].w);
 937 
 938     /* We will manage the dialog without any help, that's why
 939        we have to call dlg_init */
 940     dlg_init (ui->op_dlg);
 941 }
 942 
 943 /* --------------------------------------------------------------------------------------------- */
 944 
 945 void
 946 file_op_context_destroy_ui (file_op_context_t * ctx)
     /* [previous][next][first][last][top][bottom][index][help]  */
 947 {
 948     if (ctx != NULL && ctx->ui != NULL)
 949     {
 950         file_op_context_ui_t *ui = (file_op_context_ui_t *) ctx->ui;
 951 
 952         dlg_run_done (ui->op_dlg);
 953         widget_destroy (WIDGET (ui->op_dlg));
 954         MC_PTR_FREE (ctx->ui);
 955     }
 956 }
 957 
 958 /* --------------------------------------------------------------------------------------------- */
 959 /**
 960    show progressbar for file
 961  */
 962 
 963 void
 964 file_progress_show (file_op_context_t * ctx, off_t done, off_t total,
     /* [previous][next][first][last][top][bottom][index][help]  */
 965                     const char *stalled_msg, gboolean force_update)
 966 {
 967     file_op_context_ui_t *ui;
 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         label_set_text (ui->progress_file_label, stalled_msg);
 988     else
 989     {
 990         char buffer2[BUF_TINY];
 991 
 992         file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
 993         if (ctx->bps == 0)
 994             label_set_textv (ui->progress_file_label, "%s %s", buffer2, stalled_msg);
 995         else
 996         {
 997             char buffer3[BUF_TINY];
 998 
 999             file_bps_prepare_for_show (buffer3, ctx->bps);
1000             label_set_textv (ui->progress_file_label, "%s (%s) %s", buffer2, buffer3, stalled_msg);
1001         }
1002 
1003     }
1004 }
1005 
1006 /* --------------------------------------------------------------------------------------------- */
1007 
1008 void
1009 file_progress_show_count (file_op_context_t * ctx, size_t done, size_t total)
     /* [previous][next][first][last][top][bottom][index][help]  */
1010 {
1011     file_op_context_ui_t *ui;
1012 
1013     if (ctx == NULL || ctx->ui == NULL)
1014         return;
1015 
1016     ui = ctx->ui;
1017 
1018     if (ui->total_files_processed_label == NULL)
1019         return;
1020 
1021     if (ctx->progress_totals_computed)
1022         label_set_textv (ui->total_files_processed_label, _("Files processed: %zu/%zu"), done,
1023                          total);
1024     else
1025         label_set_textv (ui->total_files_processed_label, _("Files processed: %zu"), done);
1026 }
1027 
1028 /* --------------------------------------------------------------------------------------------- */
1029 
1030 void
1031 file_progress_show_total (file_op_total_context_t * tctx, file_op_context_t * ctx,
     /* [previous][next][first][last][top][bottom][index][help]  */
1032                           uintmax_t copied_bytes, gboolean show_summary)
1033 {
1034     char buffer2[BUF_TINY];
1035     char buffer3[BUF_TINY];
1036     file_op_context_ui_t *ui;
1037 
1038     if (ctx == NULL || ctx->ui == NULL)
1039         return;
1040 
1041     ui = ctx->ui;
1042 
1043     if (ui->progress_total_gauge != NULL)
1044     {
1045         if (ctx->progress_bytes == 0)
1046             gauge_show (ui->progress_total_gauge, FALSE);
1047         else
1048         {
1049             gauge_set_value (ui->progress_total_gauge, 1024,
1050                              (int) (1024 * copied_bytes / ctx->progress_bytes));
1051             gauge_show (ui->progress_total_gauge, TRUE);
1052         }
1053     }
1054 
1055     if (!show_summary && tctx->bps == 0)
1056         return;
1057 
1058     if (ui->time_label != NULL)
1059     {
1060         struct timeval tv_current;
1061         char buffer4[BUF_TINY];
1062 
1063         gettimeofday (&tv_current, NULL);
1064         file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec);
1065 
1066         if (ctx->progress_totals_computed)
1067         {
1068             file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
1069             if (tctx->bps == 0)
1070                 label_set_textv (ui->time_label, _("Time: %s %s"), buffer2, buffer3);
1071             else
1072             {
1073                 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1074                 label_set_textv (ui->time_label, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
1075             }
1076         }
1077         else
1078         {
1079             if (tctx->bps == 0)
1080                 label_set_textv (ui->time_label, _("Time: %s"), buffer2);
1081             else
1082             {
1083                 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1084                 label_set_textv (ui->time_label, _("Time: %s (%s)"), buffer2, buffer4);
1085             }
1086         }
1087     }
1088 
1089     if (ui->total_bytes_label != NULL)
1090     {
1091         size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
1092 
1093         if (!ctx->progress_totals_computed)
1094             hline_set_textv (ui->total_bytes_label, _(" Total: %s "), buffer2);
1095         else
1096         {
1097             size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
1098             hline_set_textv (ui->total_bytes_label, _(" Total: %s/%s "), buffer2, buffer3);
1099         }
1100     }
1101 }
1102 
1103 /* }}} */
1104 
1105 /* --------------------------------------------------------------------------------------------- */
1106 
1107 void
1108 file_progress_show_source (file_op_context_t * ctx, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1109 {
1110     file_op_context_ui_t *ui;
1111 
1112     if (ctx == NULL || ctx->ui == NULL)
1113         return;
1114 
1115     ui = ctx->ui;
1116 
1117     if (vpath != NULL)
1118     {
1119         char *s;
1120 
1121         s = vfs_path_tokens_get (vpath, -1, 1);
1122         label_set_text (ui->src_file_label, _("Source"));
1123         label_set_text (ui->src_file, truncFileString (ui->op_dlg, s));
1124         g_free (s);
1125     }
1126     else
1127     {
1128         label_set_text (ui->src_file_label, "");
1129         label_set_text (ui->src_file, "");
1130     }
1131 }
1132 
1133 /* --------------------------------------------------------------------------------------------- */
1134 
1135 void
1136 file_progress_show_target (file_op_context_t * ctx, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1137 {
1138     file_op_context_ui_t *ui;
1139 
1140     if (ctx == NULL || ctx->ui == NULL)
1141         return;
1142 
1143     ui = ctx->ui;
1144 
1145     if (vpath != NULL)
1146     {
1147         label_set_text (ui->tgt_file_label, _("Target"));
1148         label_set_text (ui->tgt_file, truncFileStringSecure (ui->op_dlg, vfs_path_as_str (vpath)));
1149     }
1150     else
1151     {
1152         label_set_text (ui->tgt_file_label, "");
1153         label_set_text (ui->tgt_file, "");
1154     }
1155 }
1156 
1157 /* --------------------------------------------------------------------------------------------- */
1158 
1159 gboolean
1160 file_progress_show_deleting (file_op_context_t * ctx, const char *s, size_t * count)
     /* [previous][next][first][last][top][bottom][index][help]  */
1161 {
1162     static gint64 timestamp = 0;
1163     /* update with 25 FPS rate */
1164     static const gint64 delay = G_USEC_PER_SEC / 25;
1165 
1166     gboolean ret;
1167 
1168     if (ctx == NULL || ctx->ui == NULL)
1169         return FALSE;
1170 
1171     ret = mc_time_elapsed (&timestamp, delay);
1172 
1173     if (ret)
1174     {
1175         file_op_context_ui_t *ui;
1176 
1177         ui = ctx->ui;
1178 
1179         if (ui->src_file_label != NULL)
1180             label_set_text (ui->src_file_label, _("Deleting"));
1181 
1182         label_set_text (ui->src_file, truncFileStringSecure (ui->op_dlg, s));
1183     }
1184 
1185     if (count != NULL)
1186         (*count)++;
1187 
1188     return ret;
1189 }
1190 
1191 /* --------------------------------------------------------------------------------------------- */
1192 
1193 FileProgressStatus
1194 file_progress_real_query_replace (file_op_context_t * ctx, enum OperationMode mode,
     /* [previous][next][first][last][top][bottom][index][help]  */
1195                                   const char *src, struct stat * src_stat,
1196                                   const char *dst, struct stat * dst_stat)
1197 {
1198     file_op_context_ui_t *ui;
1199     FileProgressStatus replace_with_zero;
1200 
1201     if (ctx == NULL || ctx->ui == NULL)
1202         return FILE_CONT;
1203 
1204     ui = ctx->ui;
1205 
1206     if (ui->replace_result == REPLACE_YES || ui->replace_result == REPLACE_NO
1207         || ui->replace_result == REPLACE_APPEND)
1208     {
1209         ui->src_filename = src;
1210         ui->src_stat = src_stat;
1211         ui->tgt_filename = dst;
1212         ui->dst_stat = dst_stat;
1213         ui->replace_result = overwrite_query_dialog (ctx, mode);
1214     }
1215 
1216     replace_with_zero = (src_stat->st_size == 0
1217                          && ui->dont_overwrite_with_zero) ? FILE_SKIP : FILE_CONT;
1218 
1219     switch (ui->replace_result)
1220     {
1221     case REPLACE_OLDER:
1222         do_refresh ();
1223         if (src_stat->st_mtime > dst_stat->st_mtime)
1224             return replace_with_zero;
1225         else
1226             return FILE_SKIP;
1227 
1228     case REPLACE_SIZE:
1229         do_refresh ();
1230         if (src_stat->st_size == dst_stat->st_size)
1231             return FILE_SKIP;
1232         else
1233             return replace_with_zero;
1234 
1235     case REPLACE_SMALLER:
1236         do_refresh ();
1237         if (src_stat->st_size > dst_stat->st_size)
1238             return FILE_CONT;
1239         else
1240             return FILE_SKIP;
1241 
1242     case REPLACE_ALL:
1243         do_refresh ();
1244         return replace_with_zero;
1245 
1246     case REPLACE_REGET:
1247         /* Careful: we fall through and set do_append */
1248         ctx->do_reget = dst_stat->st_size;
1249         MC_FALLTHROUGH;
1250 
1251     case REPLACE_APPEND:
1252         ctx->do_append = TRUE;
1253         MC_FALLTHROUGH;
1254 
1255     case REPLACE_YES:
1256         do_refresh ();
1257         return FILE_CONT;
1258 
1259     case REPLACE_NO:
1260     case REPLACE_NONE:
1261         do_refresh ();
1262         return FILE_SKIP;
1263 
1264     case REPLACE_ABORT:
1265     default:
1266         return FILE_ABORT;
1267     }
1268 }
1269 
1270 /* --------------------------------------------------------------------------------------------- */
1271 
1272 char *
1273 file_mask_dialog (file_op_context_t * ctx, FileOperation operation,
     /* [previous][next][first][last][top][bottom][index][help]  */
1274                   gboolean only_one,
1275                   const char *format, const void *text, const char *def_text, gboolean * do_bg)
1276 {
1277     size_t fmd_xlen;
1278     vfs_path_t *vpath;
1279     gboolean source_easy_patterns = easy_patterns;
1280     char fmd_buf[BUF_MEDIUM];
1281     char *dest_dir, *tmp;
1282     char *def_text_secure;
1283 
1284     if (ctx == NULL)
1285         return NULL;
1286 
1287     /* unselect checkbox if target filesystem doesn't support attributes */
1288     ctx->op_preserve = copymove_persistent_attr && filegui__check_attrs_on_fs (def_text);
1289     ctx->stable_symlinks = FALSE;
1290     *do_bg = FALSE;
1291 
1292     /* filter out a possible password from def_text */
1293     vpath = vfs_path_from_str_flags (def_text, only_one ? VPF_NO_CANON : VPF_NONE);
1294     tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1295     vfs_path_free (vpath, TRUE);
1296 
1297     if (source_easy_patterns)
1298         def_text_secure = strutils_glob_escape (tmp);
1299     else
1300         def_text_secure = strutils_regex_escape (tmp);
1301     g_free (tmp);
1302 
1303     if (only_one)
1304     {
1305         int format_len, text_len;
1306         int max_len;
1307 
1308         format_len = str_term_width1 (format);
1309         text_len = str_term_width1 (text);
1310         max_len = COLS - 2 - 6;
1311 
1312         if (format_len + text_len <= max_len)
1313         {
1314             fmd_xlen = format_len + text_len + 6;
1315             fmd_xlen = MAX (fmd_xlen, 68);
1316         }
1317         else
1318         {
1319             text = str_trunc ((const char *) text, max_len - format_len);
1320             fmd_xlen = max_len + 6;
1321         }
1322 
1323         g_snprintf (fmd_buf, sizeof (fmd_buf), format, (const char *) text);
1324     }
1325     else
1326     {
1327         fmd_xlen = COLS * 2 / 3;
1328         fmd_xlen = MAX (fmd_xlen, 68);
1329         g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1330     }
1331 
1332     {
1333         char *source_mask, *orig_mask;
1334         int val;
1335         struct stat buf;
1336 
1337         quick_widget_t quick_widgets[] = {
1338             /* *INDENT-OFF* */
1339             QUICK_LABELED_INPUT (fmd_buf, input_label_above,
1340                                  easy_patterns ? "*" : "^(.*)$", "input-def", &source_mask,
1341                                  NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1342             QUICK_START_COLUMNS,
1343                 QUICK_SEPARATOR (FALSE),
1344             QUICK_NEXT_COLUMN,
1345                 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns, NULL),
1346             QUICK_STOP_COLUMNS,
1347             QUICK_LABELED_INPUT (N_("to:"), input_label_above,
1348                                  def_text_secure, "input2", &dest_dir, NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1349             QUICK_SEPARATOR (TRUE),
1350             QUICK_START_COLUMNS,
1351                 QUICK_CHECKBOX (N_("Follow &links"), &ctx->follow_links, NULL),
1352                 QUICK_CHECKBOX (N_("Preserve &attributes"), &ctx->op_preserve, NULL),
1353             QUICK_NEXT_COLUMN,
1354                 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx->dive_into_subdirs, NULL),
1355                 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx->stable_symlinks, NULL),
1356             QUICK_STOP_COLUMNS,
1357             QUICK_START_BUTTONS (TRUE, TRUE),
1358                 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
1359 #ifdef ENABLE_BACKGROUND
1360                 QUICK_BUTTON (N_("&Background"), B_USER, NULL, NULL),
1361 #endif /* ENABLE_BACKGROUND */
1362                 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1363             QUICK_END
1364             /* *INDENT-ON* */
1365         };
1366 
1367         quick_dialog_t qdlg = {
1368             -1, -1, fmd_xlen,
1369             op_names[operation], "[Mask Copy/Rename]",
1370             quick_widgets, NULL, NULL
1371         };
1372 
1373       ask_file_mask:
1374         val = quick_dialog_skip (&qdlg, 4);
1375 
1376         if (val == B_CANCEL)
1377         {
1378             g_free (def_text_secure);
1379             return NULL;
1380         }
1381 
1382         ctx->stat_func = ctx->follow_links ? mc_stat : mc_lstat;
1383 
1384         if (ctx->op_preserve)
1385         {
1386             ctx->preserve = TRUE;
1387             ctx->umask_kill = 0777777;
1388             ctx->preserve_uidgid = (geteuid () == 0);
1389         }
1390         else
1391         {
1392             mode_t i2;
1393 
1394             ctx->preserve = ctx->preserve_uidgid = FALSE;
1395             i2 = umask (0);
1396             umask (i2);
1397             ctx->umask_kill = i2 ^ 0777777;
1398         }
1399 
1400         if ((dest_dir == NULL) || (*dest_dir == '\0'))
1401         {
1402             g_free (def_text_secure);
1403             g_free (source_mask);
1404             g_free (dest_dir);
1405             return NULL;
1406         }
1407 
1408         ctx->search_handle = mc_search_new (source_mask, NULL);
1409 
1410         if (ctx->search_handle == NULL)
1411         {
1412             message (D_ERROR, MSG_ERROR, _("Invalid source pattern '%s'"), source_mask);
1413             g_free (dest_dir);
1414             g_free (source_mask);
1415             goto ask_file_mask;
1416         }
1417 
1418         g_free (def_text_secure);
1419         g_free (source_mask);
1420 
1421         ctx->search_handle->is_case_sensitive = TRUE;
1422         if (source_easy_patterns)
1423             ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1424         else
1425             ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1426 
1427         tmp = dest_dir;
1428         dest_dir = tilde_expand (tmp);
1429         g_free (tmp);
1430         vpath = vfs_path_from_str (dest_dir);
1431 
1432         ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1433         if (ctx->dest_mask == NULL)
1434             ctx->dest_mask = dest_dir;
1435         else
1436             ctx->dest_mask++;
1437 
1438         orig_mask = ctx->dest_mask;
1439 
1440         if (*ctx->dest_mask == '\0'
1441             || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1442                 && (!only_one
1443                     || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1444             || (ctx->dive_into_subdirs
1445                 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1446                     || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1447             ctx->dest_mask = g_strdup ("\\0");
1448         else
1449         {
1450             ctx->dest_mask = g_strdup (ctx->dest_mask);
1451             *orig_mask = '\0';
1452         }
1453 
1454         if (*dest_dir == '\0')
1455         {
1456             g_free (dest_dir);
1457             dest_dir = g_strdup ("./");
1458         }
1459 
1460         vfs_path_free (vpath, TRUE);
1461 
1462         if (val == B_USER)
1463             *do_bg = TRUE;
1464     }
1465 
1466     return dest_dir;
1467 }
1468 
1469 /* --------------------------------------------------------------------------------------------- */

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