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-2019
  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 __linux__ && (__GLIBC__ || __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 ! (__linux__ && (__GLIBC__ || __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     add_widget_autopos (ui->replace_dlg, W(i), dlg_widgets[i].pos_flags, \
 414                         ui->replace_dlg->current != NULL ? ui->replace_dlg->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     add_widget_autopos (ui->replace_dlg, W(i), dlg_widgets[i].pos_flags, \
 422                         ui->replace_dlg->current->data)
 423 
 424     /* dialog sizes */
 425     const int dlg_height = 17;
 426     int dlg_width = 60;
 427 
 428     struct
 429     {
 430         Widget *widget;
 431         const char *text;
 432         int y;
 433         int x;
 434         widget_pos_flags_t pos_flags;
 435         int value;              /* 0 for labels and checkbox */
 436     } dlg_widgets[] =
 437     {
 438         /* *INDENT-OFF* */
 439         /*  0 - label */
 440         { NULL, N_("New     :"), 2, 3, WPOS_KEEP_DEFAULT, 0 },
 441         /*  1 - label - name */
 442         { NULL, NULL, 2, 14, WPOS_KEEP_DEFAULT, 0 },
 443         /*  2 - label - size */
 444         { NULL, NULL, 3, 3, WPOS_KEEP_DEFAULT, 0 },
 445         /*  3 - label - date & time */
 446         { NULL, NULL, 3, 43, WPOS_KEEP_TOP | WPOS_KEEP_RIGHT, 0 },
 447         /*  4 - label */
 448         { NULL, N_("Existing:"), 4, 3, WPOS_KEEP_DEFAULT, 0 },
 449         /*  5 - label - name */
 450         { NULL, NULL, 4, 14, WPOS_KEEP_DEFAULT, 0 },
 451         /*  6 - label - size */
 452         { NULL, NULL, 5, 3, WPOS_KEEP_DEFAULT, 0 },
 453         /*  7 - label - date & time */
 454         { NULL, NULL, 5, 43, WPOS_KEEP_TOP | WPOS_KEEP_RIGHT, 0 },
 455         /* --------------------------------------------------- */
 456         /*  8 - label */
 457         { NULL, N_("Overwrite this file?"), 7, 21, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
 458         /*  9 - button */
 459         { NULL, N_("&Yes"), 8, 14, WPOS_KEEP_DEFAULT, REPLACE_YES },
 460         /* 10 - button */
 461         { NULL, N_("&No"), 8, 22, WPOS_KEEP_DEFAULT, REPLACE_NO },
 462         /* 11 - button */
 463         { NULL, N_("A&ppend"), 8, 29, WPOS_KEEP_DEFAULT, REPLACE_APPEND },
 464         /* 12 - button */
 465         { NULL, N_("&Reget"), 8, 40, WPOS_KEEP_DEFAULT, REPLACE_REGET },
 466         /* --------------------------------------------------- */
 467         /* 13 - label */
 468         { NULL, N_("Overwrite all files?"), 10, 21, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
 469         /* 14 - checkbox */
 470         { NULL, N_("Don't overwrite with &zero length file"), 11, 3, WPOS_KEEP_DEFAULT, 0 },
 471         /* 15 - button */
 472         { NULL, N_("A&ll"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_ALL },
 473         /* 16 - button */
 474         { NULL, N_("&Older"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_OLDER },
 475         /* 17 - button */
 476         { NULL, N_("Non&e"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_NONE },
 477         /* 18 - button */
 478         { NULL, N_("S&maller"), 12, 25, WPOS_KEEP_DEFAULT, REPLACE_SMALLER },
 479         /* 19 - button */
 480         { NULL, N_("&Size differs"), 12, 40, WPOS_KEEP_DEFAULT, REPLACE_SIZE },
 481         /* --------------------------------------------------- */
 482         /* 20 - button */
 483         { NULL, N_("&Abort"), 14, 27, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, REPLACE_ABORT }
 484         /* *INDENT-ON* */
 485     };
 486 
 487     const int gap = 1;
 488 
 489     file_op_context_ui_t *ui = ctx->ui;
 490     Widget *wd;
 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 
 641     /* file info */
 642     for (i = 0; i <= 7; i++)
 643         ADD_LABEL (i);
 644     add_widget (ui->replace_dlg, hline_new (W (7)->y - wd->y + 1, -1, -1));
 645 
 646     /* label & buttons */
 647     ADD_LABEL (8);              /* Overwrite this file? */
 648     yes_id = ADD_BUTTON (9);    /* Yes */
 649     no_id = ADD_BUTTON (10);    /* No */
 650     if (do_append)
 651         ADD_BUTTON (11);        /* Append */
 652     if (do_reget)
 653         ADD_BUTTON (12);        /* Reget */
 654     add_widget (ui->replace_dlg, hline_new (W (10)->y - wd->y + 1, -1, -1));
 655 
 656     /* label & buttons */
 657     ADD_LABEL (13);             /* Overwrite all files? */
 658     add_widget (ui->replace_dlg, dlg_widgets[14].widget);
 659     for (i = 15; i <= 19; i++)
 660         ADD_BUTTON (i);
 661     add_widget (ui->replace_dlg, hline_new (W (19)->y - wd->y + 1, -1, -1));
 662 
 663     ADD_BUTTON (20);            /* Abort */
 664 
 665     dlg_select_by_id (ui->replace_dlg, safe_overwrite ? no_id : yes_id);
 666 
 667     result = dlg_run (ui->replace_dlg);
 668 
 669     if (result != B_CANCEL)
 670         ui->dont_overwrite_with_zero = CHECK (dlg_widgets[14].widget)->state;
 671 
 672     dlg_destroy (ui->replace_dlg);
 673 
 674     return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
 675 
 676 #undef ADD_BUTTON
 677 #undef NEW_BUTTON
 678 #undef ADD_LABEL
 679 #undef NEW_LABEL
 680 #undef WCOLS
 681 #undef WX
 682 #undef W
 683 }
 684 
 685 /* --------------------------------------------------------------------------------------------- */
 686 
 687 static gboolean
 688 is_wildcarded (const char *p)
     /* [previous][next][first][last][top][bottom][index][help]  */
 689 {
 690     gboolean escaped = FALSE;
 691 
 692     for (; *p != '\0'; p++)
 693     {
 694         if (*p == '\\')
 695         {
 696             if (p[1] >= '1' && p[1] <= '9' && !escaped)
 697                 return TRUE;
 698             escaped = !escaped;
 699         }
 700         else
 701         {
 702             if ((*p == '*' || *p == '?') && !escaped)
 703                 return TRUE;
 704             escaped = FALSE;
 705         }
 706     }
 707     return FALSE;
 708 }
 709 
 710 /* --------------------------------------------------------------------------------------------- */
 711 
 712 static void
 713 place_progress_buttons (WDialog * h, gboolean suspended)
     /* [previous][next][first][last][top][bottom][index][help]  */
 714 {
 715     const size_t i = suspended ? 2 : 1;
 716     Widget *w = WIDGET (h);
 717     int buttons_width;
 718 
 719     buttons_width = 2 + progress_buttons[0].len + progress_buttons[3].len;
 720     buttons_width += progress_buttons[i].len;
 721     button_set_text (BUTTON (progress_buttons[i].w), progress_buttons[i].text);
 722 
 723     progress_buttons[0].w->x = w->x + (w->cols - buttons_width) / 2;
 724     progress_buttons[i].w->x = progress_buttons[0].w->x + progress_buttons[0].len + 1;
 725     progress_buttons[3].w->x = progress_buttons[i].w->x + progress_buttons[i].len + 1;
 726 }
 727 
 728 /* --------------------------------------------------------------------------------------------- */
 729 
 730 static int
 731 progress_button_callback (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 732 {
 733     (void) button;
 734     (void) action;
 735 
 736     /* don't close dialog in any case */
 737     return 0;
 738 }
 739 
 740 /* --------------------------------------------------------------------------------------------- */
 741 /*** public functions ****************************************************************************/
 742 /* --------------------------------------------------------------------------------------------- */
 743 
 744 FileProgressStatus
 745 check_progress_buttons (file_op_context_t * ctx)
     /* [previous][next][first][last][top][bottom][index][help]  */
 746 {
 747     int c;
 748     Gpm_Event event;
 749     file_op_context_ui_t *ui;
 750 
 751     if (ctx == NULL || ctx->ui == NULL)
 752         return FILE_CONT;
 753 
 754     ui = ctx->ui;
 755 
 756   get_event:
 757     event.x = -1;               /* Don't show the GPM cursor */
 758     c = tty_get_event (&event, FALSE, ctx->suspended);
 759     if (c == EV_NONE)
 760         return FILE_CONT;
 761 
 762     /* Reinitialize to avoid old values after events other than selecting a button */
 763     ui->op_dlg->ret_value = FILE_CONT;
 764 
 765     dlg_process_event (ui->op_dlg, c, &event);
 766     switch (ui->op_dlg->ret_value)
 767     {
 768     case FILE_SKIP:
 769         if (ctx->suspended)
 770         {
 771             /* redraw dialog in case of Skip after Suspend */
 772             place_progress_buttons (ui->op_dlg, FALSE);
 773             dlg_draw (ui->op_dlg);
 774         }
 775         ctx->suspended = FALSE;
 776         return FILE_SKIP;
 777     case B_CANCEL:
 778     case FILE_ABORT:
 779         ctx->suspended = FALSE;
 780         return FILE_ABORT;
 781     case FILE_SUSPEND:
 782         ctx->suspended = !ctx->suspended;
 783         place_progress_buttons (ui->op_dlg, ctx->suspended);
 784         dlg_draw (ui->op_dlg);
 785         MC_FALLTHROUGH;
 786     default:
 787         if (ctx->suspended)
 788             goto get_event;
 789         return FILE_CONT;
 790     }
 791 }
 792 
 793 /* --------------------------------------------------------------------------------------------- */
 794 /* {{{ File progress display routines */
 795 
 796 void
 797 file_op_context_create_ui (file_op_context_t * ctx, gboolean with_eta,
     /* [previous][next][first][last][top][bottom][index][help]  */
 798                            filegui_dialog_type_t dialog_type)
 799 {
 800     file_op_context_ui_t *ui;
 801     int buttons_width;
 802     int dlg_width = 58, dlg_height = 17;
 803     int y = 2, x = 3;
 804 
 805     if (ctx == NULL || ctx->ui != NULL)
 806         return;
 807 
 808 #ifdef ENABLE_NLS
 809     if (progress_buttons[0].len == -1)
 810     {
 811         size_t i;
 812 
 813         for (i = 0; i < G_N_ELEMENTS (progress_buttons); i++)
 814             progress_buttons[i].text = _(progress_buttons[i].text);
 815     }
 816 #endif
 817 
 818     ctx->dialog_type = dialog_type;
 819     ctx->recursive_result = RECURSIVE_YES;
 820     ctx->ui = g_new0 (file_op_context_ui_t, 1);
 821 
 822     ui = ctx->ui;
 823     ui->replace_result = REPLACE_YES;
 824 
 825     ui->op_dlg =
 826         dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, dialog_colors, NULL,
 827                     NULL, NULL, op_names[ctx->operation]);
 828 
 829     if (dialog_type != FILEGUI_DIALOG_DELETE_ITEM)
 830     {
 831         ui->showing_eta = with_eta && ctx->progress_totals_computed;
 832         ui->showing_bps = with_eta;
 833 
 834         ui->src_file_label = label_new (y++, x, "");
 835         add_widget (ui->op_dlg, ui->src_file_label);
 836 
 837         ui->src_file = label_new (y++, x, "");
 838         add_widget (ui->op_dlg, ui->src_file);
 839 
 840         ui->tgt_file_label = label_new (y++, x, "");
 841         add_widget (ui->op_dlg, ui->tgt_file_label);
 842 
 843         ui->tgt_file = label_new (y++, x, "");
 844         add_widget (ui->op_dlg, ui->tgt_file);
 845 
 846         ui->progress_file_gauge = gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
 847         if (!classic_progressbar && (current_panel == right_panel))
 848             ui->progress_file_gauge->from_left_to_right = FALSE;
 849         add_widget_autopos (ui->op_dlg, ui->progress_file_gauge, WPOS_KEEP_TOP | WPOS_KEEP_HORZ,
 850                             NULL);
 851 
 852         ui->progress_file_label = label_new (y++, x, "");
 853         add_widget (ui->op_dlg, ui->progress_file_label);
 854 
 855         if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
 856         {
 857             ui->total_bytes_label = hline_new (y++, -1, -1);
 858             add_widget (ui->op_dlg, ui->total_bytes_label);
 859 
 860             if (ctx->progress_totals_computed)
 861             {
 862                 ui->progress_total_gauge =
 863                     gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
 864                 if (!classic_progressbar && (current_panel == right_panel))
 865                     ui->progress_total_gauge->from_left_to_right = FALSE;
 866                 add_widget_autopos (ui->op_dlg, ui->progress_total_gauge,
 867                                     WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
 868             }
 869 
 870             ui->total_files_processed_label = label_new (y++, x, "");
 871             add_widget (ui->op_dlg, ui->total_files_processed_label);
 872 
 873             ui->time_label = label_new (y++, x, "");
 874             add_widget (ui->op_dlg, ui->time_label);
 875         }
 876     }
 877     else
 878     {
 879         ui->src_file = label_new (y++, x, "");
 880         add_widget (ui->op_dlg, ui->src_file);
 881 
 882         ui->total_files_processed_label = label_new (y++, x, "");
 883         add_widget (ui->op_dlg, ui->total_files_processed_label);
 884     }
 885 
 886     add_widget (ui->op_dlg, hline_new (y++, -1, -1));
 887 
 888     progress_buttons[0].w = WIDGET (button_new (y, 0, progress_buttons[0].action,
 889                                                 progress_buttons[0].flags, progress_buttons[0].text,
 890                                                 progress_button_callback));
 891     if (progress_buttons[0].len == -1)
 892         progress_buttons[0].len = button_get_len (BUTTON (progress_buttons[0].w));
 893 
 894     progress_buttons[1].w = WIDGET (button_new (y, 0, progress_buttons[1].action,
 895                                                 progress_buttons[1].flags, progress_buttons[1].text,
 896                                                 progress_button_callback));
 897     if (progress_buttons[1].len == -1)
 898         progress_buttons[1].len = button_get_len (BUTTON (progress_buttons[1].w));
 899 
 900     if (progress_buttons[2].len == -1)
 901     {
 902         /* create and destroy button to get it length */
 903         progress_buttons[2].w = WIDGET (button_new (y, 0, progress_buttons[2].action,
 904                                                     progress_buttons[2].flags,
 905                                                     progress_buttons[2].text,
 906                                                     progress_button_callback));
 907         progress_buttons[2].len = button_get_len (BUTTON (progress_buttons[2].w));
 908         widget_destroy (progress_buttons[2].w);
 909     }
 910     progress_buttons[2].w = progress_buttons[1].w;
 911 
 912     progress_buttons[3].w = WIDGET (button_new (y, 0, progress_buttons[3].action,
 913                                                 progress_buttons[3].flags, progress_buttons[3].text,
 914                                                 NULL));
 915     if (progress_buttons[3].len == -1)
 916         progress_buttons[3].len = button_get_len (BUTTON (progress_buttons[3].w));
 917 
 918     add_widget (ui->op_dlg, progress_buttons[0].w);
 919     add_widget (ui->op_dlg, progress_buttons[1].w);
 920     add_widget (ui->op_dlg, progress_buttons[3].w);
 921 
 922     buttons_width = 2 +
 923         progress_buttons[0].len + MAX (progress_buttons[1].len, progress_buttons[2].len) +
 924         progress_buttons[3].len;
 925 
 926     /* adjust dialog sizes  */
 927     dlg_set_size (ui->op_dlg, y + 3, MAX (COLS * 2 / 3, buttons_width + 6));
 928 
 929     place_progress_buttons (ui->op_dlg, FALSE);
 930 
 931     widget_select (progress_buttons[0].w);
 932 
 933     /* We will manage the dialog without any help, that's why
 934        we have to call dlg_init */
 935     dlg_init (ui->op_dlg);
 936 }
 937 
 938 /* --------------------------------------------------------------------------------------------- */
 939 
 940 void
 941 file_op_context_destroy_ui (file_op_context_t * ctx)
     /* [previous][next][first][last][top][bottom][index][help]  */
 942 {
 943     if (ctx != NULL && ctx->ui != NULL)
 944     {
 945         file_op_context_ui_t *ui = (file_op_context_ui_t *) ctx->ui;
 946 
 947         dlg_run_done (ui->op_dlg);
 948         dlg_destroy (ui->op_dlg);
 949         MC_PTR_FREE (ctx->ui);
 950     }
 951 }
 952 
 953 /* --------------------------------------------------------------------------------------------- */
 954 /**
 955    show progressbar for file
 956  */
 957 
 958 void
 959 file_progress_show (file_op_context_t * ctx, off_t done, off_t total,
     /* [previous][next][first][last][top][bottom][index][help]  */
 960                     const char *stalled_msg, gboolean force_update)
 961 {
 962     file_op_context_ui_t *ui;
 963     char buffer[BUF_TINY];
 964 
 965     if (!verbose || ctx == NULL || ctx->ui == NULL)
 966         return;
 967 
 968     ui = ctx->ui;
 969 
 970     if (total == 0)
 971     {
 972         gauge_show (ui->progress_file_gauge, FALSE);
 973         return;
 974     }
 975 
 976     gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
 977     gauge_show (ui->progress_file_gauge, TRUE);
 978 
 979     if (!force_update)
 980         return;
 981 
 982     if (ui->showing_eta && ctx->eta_secs > 0.5)
 983     {
 984         char buffer2[BUF_TINY];
 985 
 986         file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
 987         if (ctx->bps == 0)
 988             g_snprintf (buffer, sizeof (buffer), "%s %s", buffer2, stalled_msg);
 989         else
 990         {
 991             char buffer3[BUF_TINY];
 992 
 993             file_bps_prepare_for_show (buffer3, ctx->bps);
 994             g_snprintf (buffer, sizeof (buffer), "%s (%s) %s", buffer2, buffer3, stalled_msg);
 995         }
 996     }
 997     else
 998         g_snprintf (buffer, sizeof (buffer), "%s", stalled_msg);
 999 
1000     label_set_text (ui->progress_file_label, buffer);
1001 }
1002 
1003 /* --------------------------------------------------------------------------------------------- */
1004 
1005 void
1006 file_progress_show_count (file_op_context_t * ctx, size_t done, size_t total)
     /* [previous][next][first][last][top][bottom][index][help]  */
1007 {
1008     char buffer[BUF_TINY];
1009     file_op_context_ui_t *ui;
1010 
1011     if (ctx == NULL || ctx->ui == NULL)
1012         return;
1013 
1014     ui = ctx->ui;
1015 
1016     if (ui->total_files_processed_label == NULL)
1017         return;
1018 
1019     if (ctx->progress_totals_computed)
1020         g_snprintf (buffer, sizeof (buffer), _("Files processed: %zu/%zu"), done, total);
1021     else
1022         g_snprintf (buffer, sizeof (buffer), _("Files processed: %zu"), done);
1023     label_set_text (ui->total_files_processed_label, buffer);
1024 }
1025 
1026 /* --------------------------------------------------------------------------------------------- */
1027 
1028 void
1029 file_progress_show_total (file_op_total_context_t * tctx, file_op_context_t * ctx,
     /* [previous][next][first][last][top][bottom][index][help]  */
1030                           uintmax_t copied_bytes, gboolean show_summary)
1031 {
1032     char buffer[BUF_TINY];
1033     char buffer2[BUF_TINY];
1034     char buffer3[BUF_TINY];
1035     file_op_context_ui_t *ui;
1036 
1037     if (ctx == NULL || ctx->ui == NULL)
1038         return;
1039 
1040     ui = ctx->ui;
1041 
1042     if (ui->progress_total_gauge != NULL)
1043     {
1044         if (ctx->progress_bytes == 0)
1045             gauge_show (ui->progress_total_gauge, FALSE);
1046         else
1047         {
1048             gauge_set_value (ui->progress_total_gauge, 1024,
1049                              (int) (1024 * copied_bytes / ctx->progress_bytes));
1050             gauge_show (ui->progress_total_gauge, TRUE);
1051         }
1052     }
1053 
1054     if (!show_summary && tctx->bps == 0)
1055         return;
1056 
1057     if (ui->time_label != NULL)
1058     {
1059         struct timeval tv_current;
1060         char buffer4[BUF_TINY];
1061 
1062         gettimeofday (&tv_current, NULL);
1063         file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec);
1064 
1065         if (ctx->progress_totals_computed)
1066         {
1067             file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
1068             if (tctx->bps == 0)
1069                 g_snprintf (buffer, sizeof (buffer), _("Time: %s %s"), buffer2, buffer3);
1070             else
1071             {
1072                 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1073                 g_snprintf (buffer, sizeof (buffer), _("Time: %s %s (%s)"), buffer2, buffer3,
1074                             buffer4);
1075             }
1076         }
1077         else
1078         {
1079             if (tctx->bps == 0)
1080                 g_snprintf (buffer, sizeof (buffer), _("Time: %s"), buffer2);
1081             else
1082             {
1083                 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1084                 g_snprintf (buffer, sizeof (buffer), _("Time: %s (%s)"), buffer2, buffer4);
1085             }
1086         }
1087 
1088         label_set_text (ui->time_label, buffer);
1089     }
1090 
1091     if (ui->total_bytes_label != NULL)
1092     {
1093         size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
1094 
1095         if (!ctx->progress_totals_computed)
1096             g_snprintf (buffer, sizeof (buffer), _(" Total: %s "), buffer2);
1097         else
1098         {
1099             size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
1100             g_snprintf (buffer, sizeof (buffer), _(" Total: %s/%s "), buffer2, buffer3);
1101         }
1102 
1103         hline_set_text (ui->total_bytes_label, buffer);
1104     }
1105 }
1106 
1107 /* }}} */
1108 
1109 /* --------------------------------------------------------------------------------------------- */
1110 
1111 void
1112 file_progress_show_source (file_op_context_t * ctx, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1113 {
1114     file_op_context_ui_t *ui;
1115 
1116     if (ctx == NULL || ctx->ui == NULL)
1117         return;
1118 
1119     ui = ctx->ui;
1120 
1121     if (vpath != NULL)
1122     {
1123         char *s;
1124 
1125         s = vfs_path_tokens_get (vpath, -1, 1);
1126         label_set_text (ui->src_file_label, _("Source"));
1127         label_set_text (ui->src_file, truncFileString (ui->op_dlg, s));
1128         g_free (s);
1129     }
1130     else
1131     {
1132         label_set_text (ui->src_file_label, "");
1133         label_set_text (ui->src_file, "");
1134     }
1135 }
1136 
1137 /* --------------------------------------------------------------------------------------------- */
1138 
1139 void
1140 file_progress_show_target (file_op_context_t * ctx, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1141 {
1142     file_op_context_ui_t *ui;
1143 
1144     if (ctx == NULL || ctx->ui == NULL)
1145         return;
1146 
1147     ui = ctx->ui;
1148 
1149     if (vpath != NULL)
1150     {
1151         label_set_text (ui->tgt_file_label, _("Target"));
1152         label_set_text (ui->tgt_file, truncFileStringSecure (ui->op_dlg, vfs_path_as_str (vpath)));
1153     }
1154     else
1155     {
1156         label_set_text (ui->tgt_file_label, "");
1157         label_set_text (ui->tgt_file, "");
1158     }
1159 }
1160 
1161 /* --------------------------------------------------------------------------------------------- */
1162 
1163 gboolean
1164 file_progress_show_deleting (file_op_context_t * ctx, const char *s, size_t * count)
     /* [previous][next][first][last][top][bottom][index][help]  */
1165 {
1166     static guint64 timestamp = 0;
1167     /* update with 25 FPS rate */
1168     static const guint64 delay = G_USEC_PER_SEC / 25;
1169 
1170     gboolean ret;
1171 
1172     if (ctx == NULL || ctx->ui == NULL)
1173         return FALSE;
1174 
1175     ret = mc_time_elapsed (&timestamp, delay);
1176 
1177     if (ret)
1178     {
1179         file_op_context_ui_t *ui;
1180 
1181         ui = ctx->ui;
1182 
1183         if (ui->src_file_label != NULL)
1184             label_set_text (ui->src_file_label, _("Deleting"));
1185 
1186         label_set_text (ui->src_file, truncFileStringSecure (ui->op_dlg, s));
1187     }
1188 
1189     if (count != NULL)
1190         (*count)++;
1191 
1192     return ret;
1193 }
1194 
1195 /* --------------------------------------------------------------------------------------------- */
1196 
1197 FileProgressStatus
1198 file_progress_real_query_replace (file_op_context_t * ctx, enum OperationMode mode,
     /* [previous][next][first][last][top][bottom][index][help]  */
1199                                   const char *src, struct stat * src_stat,
1200                                   const char *dst, struct stat * dst_stat)
1201 {
1202     file_op_context_ui_t *ui;
1203     FileProgressStatus replace_with_zero;
1204 
1205     if (ctx == NULL || ctx->ui == NULL)
1206         return FILE_CONT;
1207 
1208     ui = ctx->ui;
1209 
1210     if (ui->replace_result == REPLACE_YES || ui->replace_result == REPLACE_NO
1211         || ui->replace_result == REPLACE_APPEND)
1212     {
1213         ui->src_filename = src;
1214         ui->src_stat = src_stat;
1215         ui->tgt_filename = dst;
1216         ui->dst_stat = dst_stat;
1217         ui->replace_result = overwrite_query_dialog (ctx, mode);
1218     }
1219 
1220     replace_with_zero = (src_stat->st_size == 0
1221                          && ui->dont_overwrite_with_zero) ? FILE_SKIP : FILE_CONT;
1222 
1223     switch (ui->replace_result)
1224     {
1225     case REPLACE_OLDER:
1226         do_refresh ();
1227         if (src_stat->st_mtime > dst_stat->st_mtime)
1228             return replace_with_zero;
1229         else
1230             return FILE_SKIP;
1231 
1232     case REPLACE_SIZE:
1233         do_refresh ();
1234         if (src_stat->st_size == dst_stat->st_size)
1235             return FILE_SKIP;
1236         else
1237             return replace_with_zero;
1238 
1239     case REPLACE_SMALLER:
1240         do_refresh ();
1241         if (src_stat->st_size > dst_stat->st_size)
1242             return FILE_CONT;
1243         else
1244             return FILE_SKIP;
1245 
1246     case REPLACE_ALL:
1247         do_refresh ();
1248         return replace_with_zero;
1249 
1250     case REPLACE_REGET:
1251         /* Careful: we fall through and set do_append */
1252         ctx->do_reget = dst_stat->st_size;
1253         MC_FALLTHROUGH;
1254 
1255     case REPLACE_APPEND:
1256         ctx->do_append = TRUE;
1257         MC_FALLTHROUGH;
1258 
1259     case REPLACE_YES:
1260         do_refresh ();
1261         return FILE_CONT;
1262 
1263     case REPLACE_NO:
1264     case REPLACE_NONE:
1265         do_refresh ();
1266         return FILE_SKIP;
1267 
1268     case REPLACE_ABORT:
1269     default:
1270         return FILE_ABORT;
1271     }
1272 }
1273 
1274 /* --------------------------------------------------------------------------------------------- */
1275 
1276 char *
1277 file_mask_dialog (file_op_context_t * ctx, FileOperation operation,
     /* [previous][next][first][last][top][bottom][index][help]  */
1278                   gboolean only_one,
1279                   const char *format, const void *text, const char *def_text, gboolean * do_bg)
1280 {
1281     size_t fmd_xlen;
1282     vfs_path_t *vpath;
1283     gboolean source_easy_patterns = easy_patterns;
1284     char fmd_buf[BUF_MEDIUM];
1285     char *dest_dir, *tmp;
1286     char *def_text_secure;
1287 
1288     if (ctx == NULL)
1289         return NULL;
1290 
1291     /* unselect checkbox if target filesystem doesn't support attributes */
1292     ctx->op_preserve = copymove_persistent_attr && filegui__check_attrs_on_fs (def_text);
1293     ctx->stable_symlinks = FALSE;
1294     *do_bg = FALSE;
1295 
1296     /* filter out a possible password from def_text */
1297     vpath = vfs_path_from_str_flags (def_text, only_one ? VPF_NO_CANON : VPF_NONE);
1298     tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1299     vfs_path_free (vpath);
1300 
1301     if (source_easy_patterns)
1302         def_text_secure = strutils_glob_escape (tmp);
1303     else
1304         def_text_secure = strutils_regex_escape (tmp);
1305     g_free (tmp);
1306 
1307     if (only_one)
1308     {
1309         int format_len, text_len;
1310         int max_len;
1311 
1312         format_len = str_term_width1 (format);
1313         text_len = str_term_width1 (text);
1314         max_len = COLS - 2 - 6;
1315 
1316         if (format_len + text_len <= max_len)
1317         {
1318             fmd_xlen = format_len + text_len + 6;
1319             fmd_xlen = MAX (fmd_xlen, 68);
1320         }
1321         else
1322         {
1323             text = str_trunc ((const char *) text, max_len - format_len);
1324             fmd_xlen = max_len + 6;
1325         }
1326 
1327         g_snprintf (fmd_buf, sizeof (fmd_buf), format, (const char *) text);
1328     }
1329     else
1330     {
1331         fmd_xlen = COLS * 2 / 3;
1332         fmd_xlen = MAX (fmd_xlen, 68);
1333         g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1334     }
1335 
1336     {
1337         char *source_mask, *orig_mask;
1338         int val;
1339         struct stat buf;
1340 
1341         quick_widget_t quick_widgets[] = {
1342             /* *INDENT-OFF* */
1343             QUICK_LABELED_INPUT (fmd_buf, input_label_above,
1344                                  easy_patterns ? "*" : "^(.*)$", "input-def", &source_mask,
1345                                  NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1346             QUICK_START_COLUMNS,
1347                 QUICK_SEPARATOR (FALSE),
1348             QUICK_NEXT_COLUMN,
1349                 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns, NULL),
1350             QUICK_STOP_COLUMNS,
1351             QUICK_LABELED_INPUT (N_("to:"), input_label_above,
1352                                  def_text_secure, "input2", &dest_dir, NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1353             QUICK_SEPARATOR (TRUE),
1354             QUICK_START_COLUMNS,
1355                 QUICK_CHECKBOX (N_("Follow &links"), &ctx->follow_links, NULL),
1356                 QUICK_CHECKBOX (N_("Preserve &attributes"), &ctx->op_preserve, NULL),
1357             QUICK_NEXT_COLUMN,
1358                 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx->dive_into_subdirs, NULL),
1359                 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx->stable_symlinks, NULL),
1360             QUICK_STOP_COLUMNS,
1361             QUICK_START_BUTTONS (TRUE, TRUE),
1362                 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
1363 #ifdef ENABLE_BACKGROUND
1364                 QUICK_BUTTON (N_("&Background"), B_USER, NULL, NULL),
1365 #endif /* ENABLE_BACKGROUND */
1366                 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1367             QUICK_END
1368             /* *INDENT-ON* */
1369         };
1370 
1371         quick_dialog_t qdlg = {
1372             -1, -1, fmd_xlen,
1373             op_names[operation], "[Mask Copy/Rename]",
1374             quick_widgets, NULL, NULL
1375         };
1376 
1377       ask_file_mask:
1378         val = quick_dialog_skip (&qdlg, 4);
1379 
1380         if (val == B_CANCEL)
1381         {
1382             g_free (def_text_secure);
1383             return NULL;
1384         }
1385 
1386         ctx->stat_func = ctx->follow_links ? mc_stat : mc_lstat;
1387 
1388         if (ctx->op_preserve)
1389         {
1390             ctx->preserve = TRUE;
1391             ctx->umask_kill = 0777777;
1392             ctx->preserve_uidgid = (geteuid () == 0);
1393         }
1394         else
1395         {
1396             mode_t i2;
1397 
1398             ctx->preserve = ctx->preserve_uidgid = FALSE;
1399             i2 = umask (0);
1400             umask (i2);
1401             ctx->umask_kill = i2 ^ 0777777;
1402         }
1403 
1404         if ((dest_dir == NULL) || (*dest_dir == '\0'))
1405         {
1406             g_free (def_text_secure);
1407             g_free (source_mask);
1408             g_free (dest_dir);
1409             return NULL;
1410         }
1411 
1412         ctx->search_handle = mc_search_new (source_mask, NULL);
1413 
1414         if (ctx->search_handle == NULL)
1415         {
1416             message (D_ERROR, MSG_ERROR, _("Invalid source pattern '%s'"), source_mask);
1417             g_free (dest_dir);
1418             g_free (source_mask);
1419             goto ask_file_mask;
1420         }
1421 
1422         g_free (def_text_secure);
1423         g_free (source_mask);
1424 
1425         ctx->search_handle->is_case_sensitive = TRUE;
1426         if (source_easy_patterns)
1427             ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1428         else
1429             ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1430 
1431         tmp = dest_dir;
1432         dest_dir = tilde_expand (tmp);
1433         g_free (tmp);
1434         vpath = vfs_path_from_str (dest_dir);
1435 
1436         ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1437         if (ctx->dest_mask == NULL)
1438             ctx->dest_mask = dest_dir;
1439         else
1440             ctx->dest_mask++;
1441 
1442         orig_mask = ctx->dest_mask;
1443 
1444         if (*ctx->dest_mask == '\0'
1445             || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1446                 && (!only_one
1447                     || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1448             || (ctx->dive_into_subdirs
1449                 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1450                     || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1451             ctx->dest_mask = g_strdup ("\\0");
1452         else
1453         {
1454             ctx->dest_mask = g_strdup (ctx->dest_mask);
1455             *orig_mask = '\0';
1456         }
1457 
1458         if (*dest_dir == '\0')
1459         {
1460             g_free (dest_dir);
1461             dest_dir = g_strdup ("./");
1462         }
1463 
1464         vfs_path_free (vpath);
1465 
1466         if (val == B_USER)
1467             *do_bg = TRUE;
1468     }
1469 
1470     return dest_dir;
1471 }
1472 
1473 /* --------------------------------------------------------------------------------------------- */

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