2 File management GUI for the text mode edition
4 The copy code was based in GNU's cp, and was written by:
5 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
7 The move code was based in GNU's mv, and was written by:
8 Mike Parker and David MacKenzie.
10 Janne Kukonlehto added much error recovery to them for being used
11 in an interactive program.
13 Copyright (C) 1994-2024
14 Free Software Foundation, Inc.
17 Janne Kukonlehto, 1994, 1995
18 Fred Leeflang, 1994, 1995
19 Miguel de Icaza, 1994, 1995, 1996
20 Jakub Jelinek, 1995, 1996
23 Slava Zanko, 2009, 2010, 2011, 2012, 2013
24 Andrew Borodin <aborodin@vmail.ru>, 2009-2023
26 This file is part of the Midnight Commander.
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.
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.
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/>.
43 * Please note that all dialogs used here must be safe for background
48 * \brief Source: file management GUI for the text mode edition
51 /* {{{ Include files */
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)))
67 #include <sys/types.h>
71 #include <sys/statvfs.h>
72 #elif defined HAVE_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 */
86 #if ! defined STAT_STATVFS && defined STAT_STATVFS64
87 #define STRUCT_STATVFS struct statvfs64
88 #define STATFS statvfs64
90 #define STRUCT_STATVFS struct statvfs
91 #define STATFS statvfs
93 #if defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__)
94 #include <sys/utsname.h>
95 #include <sys/statfs.h>
96 #define STAT_STATFS2_BSIZE 1
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. */
108 statfs (char const *filename
, struct fs_info
*buf
)
112 device
= dev_for_path (filename
);
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);
122 /* If successful, buf->dev will be == device. */
123 return fs_stat_dev (device
, buf
);
126 #define STRUCT_STATVFS struct fs_info
128 #define STRUCT_STATVFS struct statfs
132 #ifdef HAVE_STRUCT_STATVFS_F_BASETYPE
133 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
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
144 #include "lib/global.h"
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/strutil.h"
151 #include "lib/timefmt.h" /* file_date() */
152 #include "lib/util.h"
153 #include "lib/widget.h"
155 #include "src/setup.h" /* verbose, safe_overwrite */
157 #include "filemanager.h"
163 /*** global variables ****************************************************************************/
165 gboolean classic_progressbar
= TRUE
;
167 /*** file scope macro definitions ****************************************************************/
169 #define truncFileStringSecure(dlg, s) path_trunc (s, WIDGET (dlg)->rect.cols - 10)
171 /*** file scope type declarations ****************************************************************/
175 MSDOS_SUPER_MAGIC
= 0x4d44,
176 NTFS_SB_MAGIC
= 0x5346544e,
177 FUSE_MAGIC
= 0x65735546,
178 PROC_SUPER_MAGIC
= 0x9fa0,
179 SMB_SUPER_MAGIC
= 0x517B,
180 NCP_SUPER_MAGIC
= 0x564c,
181 USBDEVICE_SUPER_MAGIC
= 0x9fa2
182 } filegui_nonattrs_fs_t
;
185 /* Used for button result values */
188 REPLACE_YES
= B_USER
,
200 /* This structure describes the UI and internal data required by a file
206 gboolean showing_eta
;
207 gboolean showing_bps
;
209 /* Dialog and widgets for the operation progress window */
211 /* Source file: label and name */
212 WLabel
*src_file_label
;
214 /* Target file: label and name */
215 WLabel
*tgt_file_label
;
217 WGauge
*progress_file_gauge
;
218 WLabel
*progress_file_label
;
220 WHLine
*total_bytes_label
;
221 WGauge
*progress_total_gauge
;
222 WLabel
*total_files_processed_label
;
225 /* Query replace dialog */
226 WDialog
*replace_dlg
;
227 const char *src_filename
;
228 const char *tgt_filename
;
229 replace_action_t replace_result
;
230 gboolean dont_overwrite_with_zero
;
232 struct stat
*src_stat
, *dst_stat
;
233 } file_progress_ui_t
;
235 /*** forward declarations (file scope functions) *************************************************/
237 /*** file scope variables ************************************************************************/
242 FileProgressStatus action
;
244 button_flags_t flags
;
246 } progress_buttons
[] = {
248 { NULL
, FILE_SKIP
, N_("&Skip"), NORMAL_BUTTON
, -1 },
249 { NULL
, FILE_SUSPEND
, N_("S&uspend"), NORMAL_BUTTON
, -1 },
250 { NULL
, FILE_SUSPEND
, N_("Con&tinue"), NORMAL_BUTTON
, -1 },
251 { NULL
, FILE_ABORT
, N_("&Abort"), NORMAL_BUTTON
, -1 }
255 /* --------------------------------------------------------------------------------------------- */
256 /*** file scope functions ************************************************************************/
257 /* --------------------------------------------------------------------------------------------- */
259 /* Return true if statvfs works. This is false for statvfs on systems
260 with GNU libc on Linux kernels before 2.6.36, which stats all
261 preceding entries in /proc/mounts; that makes df hang if even one
262 of the corresponding file systems is hard-mounted but not available. */
264 #if USE_STATVFS && ! (! defined STAT_STATVFS && defined STAT_STATVFS64)
268 #if ! (defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__))
271 static int statvfs_works_cache
= -1;
274 if (statvfs_works_cache
< 0)
275 statvfs_works_cache
= (uname (&name
) == 0 && 0 <= str_verscmp (name
.release
, "2.6.36"));
276 return statvfs_works_cache
;
281 /* --------------------------------------------------------------------------------------------- */
284 filegui__check_attrs_on_fs (const char *fs_path
)
288 #if USE_STATVFS && defined(STAT_STATVFS)
289 if (statvfs_works () && statvfs (fs_path
, &stfs
) != 0)
292 if (STATFS (fs_path
, &stfs
) != 0)
296 #if (USE_STATVFS && defined(HAVE_STRUCT_STATVFS_F_TYPE)) || \
297 (!USE_STATVFS && defined(HAVE_STRUCT_STATFS_F_TYPE))
298 switch ((filegui_nonattrs_fs_t
) stfs
.f_type
)
300 case MSDOS_SUPER_MAGIC
:
302 case PROC_SUPER_MAGIC
:
303 case SMB_SUPER_MAGIC
:
304 case NCP_SUPER_MAGIC
:
305 case USBDEVICE_SUPER_MAGIC
:
310 #elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
311 if (strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "msdos") == 0
312 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "msdosfs") == 0
313 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "ntfs") == 0
314 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "procfs") == 0
315 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "smbfs") == 0
316 || strstr (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "fusefs") != NULL
)
318 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
319 if (strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "pcfs") == 0
320 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "ntfs") == 0
321 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "proc") == 0
322 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "smbfs") == 0
323 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "fuse") == 0)
330 /* --------------------------------------------------------------------------------------------- */
333 file_frmt_time (char *buffer
, double eta_secs
)
335 int eta_hours
, eta_mins
, eta_s
;
337 eta_hours
= (int) (eta_secs
/ (60 * 60));
338 eta_mins
= (int) ((eta_secs
- (eta_hours
* 60 * 60)) / 60);
339 eta_s
= (int) (eta_secs
- (eta_hours
* 60 * 60 + eta_mins
* 60));
340 g_snprintf (buffer
, BUF_TINY
, _("%d:%02d:%02d"), eta_hours
, eta_mins
, eta_s
);
343 /* --------------------------------------------------------------------------------------------- */
346 file_eta_prepare_for_show (char *buffer
, double eta_secs
, gboolean always_show
)
348 char _fmt_buff
[BUF_TINY
];
361 file_frmt_time (_fmt_buff
, eta_secs
);
362 g_snprintf (buffer
, BUF_TINY
, _("ETA %s"), _fmt_buff
);
365 /* --------------------------------------------------------------------------------------------- */
368 file_bps_prepare_for_show (char *buffer
, long bps
)
370 if (bps
> 1024 * 1024)
371 g_snprintf (buffer
, BUF_TINY
, _("%.2f MB/s"), bps
/ (1024 * 1024.0));
373 g_snprintf (buffer
, BUF_TINY
, _("%.2f KB/s"), bps
/ 1024.0);
375 g_snprintf (buffer
, BUF_TINY
, _("%ld B/s"), bps
);
380 /* --------------------------------------------------------------------------------------------- */
383 file_ui_op_dlg_callback (Widget
*w
, Widget
*sender
, widget_msg_t msg
, int parm
, void *data
)
388 /* Do not close the dialog because the query dialog will be shown */
389 if (parm
== CK_Cancel
)
391 DIALOG (w
)->ret_value
= FILE_ABORT
; /* for file_progress_check_buttons() */
394 return MSG_NOT_HANDLED
;
397 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
401 /* --------------------------------------------------------------------------------------------- */
403 /* The dialog layout:
405 * +---------------------- File exists -----------------------+
406 * | New : /path/to/original_file_name | // 0, 1
407 * | 1234567 feb 4 2017 13:38 | // 2, 3
408 * | Existing: /path/to/target_file_name | // 4, 5
409 * | 1234567890 feb 4 2017 13:37 | // 6, 7
410 * +----------------------------------------------------------+
411 * | Overwrite this file? | // 8
412 * | [ Yes ] [ No ] [ Append ] [ Reget ] | // 9, 10, 11, 12
413 * +----------------------------------------------------------+
414 * | Overwrite all files? | // 13
415 * | [ ] Don't overwrite with zero length file | // 14
416 * | [ All ] [ Older ] [None] [ Smaller ] [ Size differs ] | // 15, 16, 17, 18, 19
417 * +----------------------------------------------------------|
418 * | [ Abort ] | // 20
419 * +----------------------------------------------------------+
422 static replace_action_t
423 overwrite_query_dialog (file_op_context_t
*ctx
, enum OperationMode mode
)
425 #define W(i) dlg_widgets[i].widget
426 #define WX(i) W(i)->rect.x
427 #define WY(i) W(i)->rect.y
428 #define WCOLS(i) W(i)->rect.cols
430 #define NEW_LABEL(i, text) \
431 W(i) = WIDGET (label_new (dlg_widgets[i].y, dlg_widgets[i].x, text))
433 #define ADD_LABEL(i) \
434 group_add_widget_autopos (g, W(i), dlg_widgets[i].pos_flags, \
435 g->current != NULL ? g->current->data : NULL)
437 #define NEW_BUTTON(i) \
438 W(i) = WIDGET (button_new (dlg_widgets[i].y, dlg_widgets[i].x, \
439 dlg_widgets[i].value, NORMAL_BUTTON, dlg_widgets[i].text, NULL))
441 #define ADD_BUTTON(i) \
442 group_add_widget_autopos (g, W(i), dlg_widgets[i].pos_flags, g->current->data)
445 const int dlg_height
= 17;
454 widget_pos_flags_t pos_flags
;
455 int value
; /* 0 for labels and checkbox */
459 { NULL
, N_("New :"), 2, 3, WPOS_KEEP_DEFAULT
, 0 },
460 /* 1 - label - name */
461 { NULL
, NULL
, 2, 14, WPOS_KEEP_DEFAULT
, 0 },
462 /* 2 - label - size */
463 { NULL
, NULL
, 3, 3, WPOS_KEEP_DEFAULT
, 0 },
464 /* 3 - label - date & time */
465 { NULL
, NULL
, 3, 43, WPOS_KEEP_TOP
| WPOS_KEEP_RIGHT
, 0 },
467 { NULL
, N_("Existing:"), 4, 3, WPOS_KEEP_DEFAULT
, 0 },
468 /* 5 - label - name */
469 { NULL
, NULL
, 4, 14, WPOS_KEEP_DEFAULT
, 0 },
470 /* 6 - label - size */
471 { NULL
, NULL
, 5, 3, WPOS_KEEP_DEFAULT
, 0 },
472 /* 7 - label - date & time */
473 { NULL
, NULL
, 5, 43, WPOS_KEEP_TOP
| WPOS_KEEP_RIGHT
, 0 },
474 /* --------------------------------------------------- */
476 { NULL
, N_("Overwrite this file?"), 7, 21, WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
, 0 },
478 { NULL
, N_("&Yes"), 8, 14, WPOS_KEEP_DEFAULT
, REPLACE_YES
},
480 { NULL
, N_("&No"), 8, 22, WPOS_KEEP_DEFAULT
, REPLACE_NO
},
482 { NULL
, N_("A&ppend"), 8, 29, WPOS_KEEP_DEFAULT
, REPLACE_APPEND
},
484 { NULL
, N_("&Reget"), 8, 40, WPOS_KEEP_DEFAULT
, REPLACE_REGET
},
485 /* --------------------------------------------------- */
487 { NULL
, N_("Overwrite all files?"), 10, 21, WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
, 0 },
489 { NULL
, N_("Don't overwrite with &zero length file"), 11, 3, WPOS_KEEP_DEFAULT
, 0 },
491 { NULL
, N_("A&ll"), 12, 12, WPOS_KEEP_DEFAULT
, REPLACE_ALL
},
493 { NULL
, N_("&Older"), 12, 12, WPOS_KEEP_DEFAULT
, REPLACE_OLDER
},
495 { NULL
, N_("Non&e"), 12, 12, WPOS_KEEP_DEFAULT
, REPLACE_NONE
},
497 { NULL
, N_("S&maller"), 12, 25, WPOS_KEEP_DEFAULT
, REPLACE_SMALLER
},
499 { NULL
, N_("&Size differs"), 12, 40, WPOS_KEEP_DEFAULT
, REPLACE_SIZE
},
500 /* --------------------------------------------------- */
502 { NULL
, N_("&Abort"), 14, 27, WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
, REPLACE_ABORT
}
508 file_progress_ui_t
*ui
= ctx
->ui
;
520 gboolean do_append
= FALSE
, do_reget
= FALSE
;
521 unsigned long yes_id
, no_id
;
524 const gint64 t
= g_get_monotonic_time ();
526 if (mode
== Foreground
)
527 title
= _("File exists");
529 title
= _("Background process: File exists");
533 const unsigned short num
= G_N_ELEMENTS (dlg_widgets
);
535 for (i
= 0; i
< num
; i
++)
536 if (dlg_widgets
[i
].text
!= NULL
)
537 dlg_widgets
[i
].text
= _(dlg_widgets
[i
].text
);
539 #endif /* ENABLE_NLS */
541 /* create widgets to get their real widths */
543 NEW_LABEL (0, dlg_widgets
[0].text
);
545 p
= vfs_path_from_str (ui
->src_filename
);
546 s1
= vfs_path_to_str_flags (p
, 0, VPF_STRIP_HOME
| VPF_STRIP_PASSWORD
);
548 vfs_path_free (p
, TRUE
);
551 size_trunc_len (s2
, sizeof (s2
), ui
->src_stat
->st_size
, 0, panels_options
.kilobyte_si
);
553 /* new file modification date & time */
554 cs1
= file_date (ui
->src_stat
->st_mtime
);
558 NEW_LABEL (4, dlg_widgets
[4].text
);
559 /* existing file name */
560 p
= vfs_path_from_str (ui
->tgt_filename
);
561 s1
= vfs_path_to_str_flags (p
, 0, VPF_STRIP_HOME
| VPF_STRIP_PASSWORD
);
563 vfs_path_free (p
, TRUE
);
565 /* existing file size */
566 size_trunc_len (s2
, sizeof (s2
), ui
->dst_stat
->st_size
, 0, panels_options
.kilobyte_si
);
568 /* existing file modification date & time */
569 cs1
= file_date (ui
->dst_stat
->st_mtime
);
572 /* will "Append" and "Reget" buttons be in the dialog? */
573 do_append
= !S_ISDIR (ui
->dst_stat
->st_mode
);
574 do_reget
= do_append
&& ui
->dst_stat
->st_size
!= 0
575 && ui
->src_stat
->st_size
> ui
->dst_stat
->st_size
;
577 NEW_LABEL (8, dlg_widgets
[8].text
);
585 NEW_LABEL (13, dlg_widgets
[13].text
);
586 dlg_widgets
[14].widget
=
587 WIDGET (check_new (dlg_widgets
[14].y
, dlg_widgets
[14].x
, FALSE
, dlg_widgets
[14].text
));
588 for (i
= 15; i
<= 20; i
++)
592 dlg_width
-= 2 * (2 + gap
); /* inside frame */
594 /* perhaps longest line is buttons */
595 bw1
= WCOLS (9) + gap
+ WCOLS (10);
597 bw1
+= gap
+ WCOLS (11);
599 bw1
+= gap
+ WCOLS (12);
600 dlg_width
= MAX (dlg_width
, bw1
);
603 for (i
= 16; i
<= 19; i
++)
604 bw2
+= gap
+ WCOLS (i
);
605 dlg_width
= MAX (dlg_width
, bw2
);
607 dlg_width
= MAX (dlg_width
, WCOLS (8));
608 dlg_width
= MAX (dlg_width
, WCOLS (13));
609 dlg_width
= MAX (dlg_width
, WCOLS (14));
611 /* truncate file names */
612 w
= WCOLS (0) + gap
+ WCOLS (1);
615 WLabel
*l
= LABEL (W (1));
617 w
= dlg_width
- gap
- WCOLS (0);
618 label_set_text (l
, str_trunc (l
->text
, w
));
621 w
= WCOLS (4) + gap
+ WCOLS (5);
624 WLabel
*l
= LABEL (W (5));
626 w
= dlg_width
- gap
- WCOLS (4);
627 label_set_text (l
, str_trunc (l
->text
, w
));
630 /* real dlalog width */
631 dlg_width
+= 2 * (2 + gap
);
633 WX (1) = WX (0) + WCOLS (0) + gap
;
634 WX (5) = WX (4) + WCOLS (4) + gap
;
636 /* sizes: right alignment */
637 WX (2) = dlg_width
/ 2 - WCOLS (2);
638 WX (6) = dlg_width
/ 2 - WCOLS (6);
640 w
= dlg_width
- (2 + gap
); /* right bound */
643 WX (3) = w
- WCOLS (3);
644 WX (7) = w
- WCOLS (7);
646 /* buttons: center alignment */
647 WX (9) = dlg_width
/ 2 - bw1
/ 2;
648 WX (10) = WX (9) + WCOLS (9) + gap
;
650 WX (11) = WX (10) + WCOLS (10) + gap
;
652 WX (12) = WX (11) + WCOLS (11) + gap
;
654 WX (15) = dlg_width
/ 2 - bw2
/ 2;
655 for (i
= 16; i
<= 19; i
++)
656 WX (i
) = WX (i
- 1) + WCOLS (i
- 1) + gap
;
658 /* TODO: write help (ticket #3970) */
660 dlg_create (TRUE
, 0, 0, dlg_height
, dlg_width
, WPOS_CENTER
, FALSE
, alarm_colors
, NULL
, NULL
,
662 wd
= WIDGET (ui
->replace_dlg
);
663 g
= GROUP (ui
->replace_dlg
);
666 for (i
= 0; i
<= 7; i
++)
668 group_add_widget (g
, hline_new (WY (7) - wd
->rect
.y
+ 1, -1, -1));
670 /* label & buttons */
671 ADD_LABEL (8); /* Overwrite this file? */
672 yes_id
= ADD_BUTTON (9); /* Yes */
673 no_id
= ADD_BUTTON (10); /* No */
675 ADD_BUTTON (11); /* Append */
677 ADD_BUTTON (12); /* Reget */
678 group_add_widget (g
, hline_new (WY (10) - wd
->rect
.y
+ 1, -1, -1));
680 /* label & buttons */
681 ADD_LABEL (13); /* Overwrite all files? */
682 group_add_widget (g
, dlg_widgets
[14].widget
);
683 for (i
= 15; i
<= 19; i
++)
685 group_add_widget (g
, hline_new (WY (19) - wd
->rect
.y
+ 1, -1, -1));
687 ADD_BUTTON (20); /* Abort */
689 group_select_widget_by_id (g
, safe_overwrite
? no_id
: yes_id
);
691 result
= dlg_run (ui
->replace_dlg
);
693 if (result
!= B_CANCEL
)
694 ui
->dont_overwrite_with_zero
= CHECK (dlg_widgets
[14].widget
)->state
;
698 ctx
->pauses
+= g_get_monotonic_time () - t
;
700 return (result
== B_CANCEL
) ? REPLACE_ABORT
: (replace_action_t
) result
;
711 /* --------------------------------------------------------------------------------------------- */
714 is_wildcarded (const char *p
)
716 gboolean escaped
= FALSE
;
718 for (; *p
!= '\0'; p
++)
722 if (p
[1] >= '1' && p
[1] <= '9' && !escaped
)
728 if ((*p
== '*' || *p
== '?') && !escaped
)
736 /* --------------------------------------------------------------------------------------------- */
739 place_progress_buttons (WDialog
*h
, gboolean suspended
)
741 const size_t i
= suspended
? 2 : 1;
742 Widget
*w
= WIDGET (h
);
745 buttons_width
= 2 + progress_buttons
[0].len
+ progress_buttons
[3].len
;
746 buttons_width
+= progress_buttons
[i
].len
;
747 button_set_text (BUTTON (progress_buttons
[i
].w
), progress_buttons
[i
].text
);
749 progress_buttons
[0].w
->rect
.x
= w
->rect
.x
+ (w
->rect
.cols
- buttons_width
) / 2;
750 progress_buttons
[i
].w
->rect
.x
= progress_buttons
[0].w
->rect
.x
+ progress_buttons
[0].len
+ 1;
751 progress_buttons
[3].w
->rect
.x
= progress_buttons
[i
].w
->rect
.x
+ progress_buttons
[i
].len
+ 1;
754 /* --------------------------------------------------------------------------------------------- */
757 progress_button_callback (WButton
*button
, int action
)
762 /* don't close dialog in any case */
766 /* --------------------------------------------------------------------------------------------- */
767 /*** public functions ****************************************************************************/
768 /* --------------------------------------------------------------------------------------------- */
770 * \fn file_op_context_t * file_op_context_new (FileOperation op)
771 * \param op file operation struct
772 * \return The newly-created context, filled with the default file mask values.
774 * Creates a new file operation context with the default values. If you later want
775 * to have a user interface for this, call file_progress_ui_create().
779 file_op_context_new (FileOperation op
)
781 file_op_context_t
*ctx
;
783 ctx
= g_new0 (file_op_context_t
, 1);
785 ctx
->preserve
= TRUE
;
786 ctx
->preserve_uidgid
= (geteuid () == 0);
787 ctx
->umask_kill
= (mode_t
) (~0);
788 ctx
->erase_at_end
= TRUE
;
790 ctx
->stat_func
= mc_lstat
;
791 ctx
->ask_overwrite
= TRUE
;
796 /* --------------------------------------------------------------------------------------------- */
798 * \fn void file_op_context_destroy (file_op_context_t *ctx)
799 * \param ctx The file operation context to destroy.
801 * Destroys the specified file operation context and its associated UI data, if
806 file_op_context_destroy (file_op_context_t
*ctx
)
810 file_progress_ui_destroy (ctx
);
811 mc_search_free (ctx
->search_handle
);
816 /* --------------------------------------------------------------------------------------------- */
819 file_progress_check_buttons (file_op_context_t
*ctx
)
823 file_progress_ui_t
*ui
;
825 if (ctx
== NULL
|| ctx
->ui
== NULL
)
831 event
.x
= -1; /* Don't show the GPM cursor */
832 c
= tty_get_event (&event
, FALSE
, ctx
->suspended
);
836 /* Reinitialize to avoid old values after events other than selecting a button */
837 ui
->op_dlg
->ret_value
= FILE_CONT
;
839 dlg_process_event (ui
->op_dlg
, c
, &event
);
840 switch (ui
->op_dlg
->ret_value
)
845 /* redraw dialog in case of Skip after Suspend */
846 place_progress_buttons (ui
->op_dlg
, FALSE
);
847 widget_draw (WIDGET (ui
->op_dlg
));
849 ctx
->suspended
= FALSE
;
853 ctx
->suspended
= FALSE
;
856 ctx
->suspended
= !ctx
->suspended
;
857 place_progress_buttons (ui
->op_dlg
, ctx
->suspended
);
858 widget_draw (WIDGET (ui
->op_dlg
));
867 /* --------------------------------------------------------------------------------------------- */
868 /* {{{ File progress display routines */
871 file_progress_ui_create (file_op_context_t
*ctx
, gboolean with_eta
,
872 filegui_dialog_type_t dialog_type
)
874 file_progress_ui_t
*ui
;
878 int dlg_width
= 58, dlg_height
= 17;
882 if (ctx
== NULL
|| ctx
->ui
!= NULL
)
886 if (progress_buttons
[0].len
== -1)
890 for (i
= 0; i
< G_N_ELEMENTS (progress_buttons
); i
++)
891 progress_buttons
[i
].text
= _(progress_buttons
[i
].text
);
895 ctx
->dialog_type
= dialog_type
;
896 ctx
->recursive_result
= RECURSIVE_YES
;
897 ctx
->ui
= g_new0 (file_progress_ui_t
, 1);
900 ui
->replace_result
= REPLACE_YES
;
902 ui
->op_dlg
= dlg_create (TRUE
, 0, 0, dlg_height
, dlg_width
, WPOS_CENTER
, FALSE
, dialog_colors
,
903 file_ui_op_dlg_callback
, NULL
, NULL
, op_names
[ctx
->operation
]);
904 w
= WIDGET (ui
->op_dlg
);
905 g
= GROUP (ui
->op_dlg
);
907 if (dialog_type
!= FILEGUI_DIALOG_DELETE_ITEM
)
909 ui
->showing_eta
= with_eta
&& ctx
->totals_computed
;
910 ui
->showing_bps
= with_eta
;
912 ui
->src_file_label
= label_new (y
++, x
, NULL
);
913 group_add_widget (g
, ui
->src_file_label
);
915 ui
->src_file
= label_new (y
++, x
, NULL
);
916 group_add_widget (g
, ui
->src_file
);
918 ui
->tgt_file_label
= label_new (y
++, x
, NULL
);
919 group_add_widget (g
, ui
->tgt_file_label
);
921 ui
->tgt_file
= label_new (y
++, x
, NULL
);
922 group_add_widget (g
, ui
->tgt_file
);
924 ui
->progress_file_gauge
= gauge_new (y
++, x
+ 3, dlg_width
- (x
+ 3) * 2, FALSE
, 100, 0);
925 if (!classic_progressbar
&& (current_panel
== right_panel
))
926 ui
->progress_file_gauge
->from_left_to_right
= FALSE
;
927 group_add_widget_autopos (g
, ui
->progress_file_gauge
, WPOS_KEEP_TOP
| WPOS_KEEP_HORZ
, NULL
);
929 ui
->progress_file_label
= label_new (y
++, x
, NULL
);
930 group_add_widget (g
, ui
->progress_file_label
);
932 if (verbose
&& dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
934 ui
->total_bytes_label
= hline_new (y
++, -1, -1);
935 group_add_widget (g
, ui
->total_bytes_label
);
937 if (ctx
->totals_computed
)
939 ui
->progress_total_gauge
=
940 gauge_new (y
++, x
+ 3, dlg_width
- (x
+ 3) * 2, FALSE
, 100, 0);
941 if (!classic_progressbar
&& (current_panel
== right_panel
))
942 ui
->progress_total_gauge
->from_left_to_right
= FALSE
;
943 group_add_widget_autopos (g
, ui
->progress_total_gauge
,
944 WPOS_KEEP_TOP
| WPOS_KEEP_HORZ
, NULL
);
947 ui
->total_files_processed_label
= label_new (y
++, x
, NULL
);
948 group_add_widget (g
, ui
->total_files_processed_label
);
950 ui
->time_label
= label_new (y
++, x
, NULL
);
951 group_add_widget (g
, ui
->time_label
);
956 ui
->src_file
= label_new (y
++, x
, NULL
);
957 group_add_widget (g
, ui
->src_file
);
959 ui
->total_files_processed_label
= label_new (y
++, x
, NULL
);
960 group_add_widget (g
, ui
->total_files_processed_label
);
963 group_add_widget (g
, hline_new (y
++, -1, -1));
965 progress_buttons
[0].w
= WIDGET (button_new (y
, 0, progress_buttons
[0].action
,
966 progress_buttons
[0].flags
, progress_buttons
[0].text
,
967 progress_button_callback
));
968 if (progress_buttons
[0].len
== -1)
969 progress_buttons
[0].len
= button_get_len (BUTTON (progress_buttons
[0].w
));
971 progress_buttons
[1].w
= WIDGET (button_new (y
, 0, progress_buttons
[1].action
,
972 progress_buttons
[1].flags
, progress_buttons
[1].text
,
973 progress_button_callback
));
974 if (progress_buttons
[1].len
== -1)
975 progress_buttons
[1].len
= button_get_len (BUTTON (progress_buttons
[1].w
));
977 if (progress_buttons
[2].len
== -1)
979 /* create and destroy button to get it length */
980 progress_buttons
[2].w
= WIDGET (button_new (y
, 0, progress_buttons
[2].action
,
981 progress_buttons
[2].flags
,
982 progress_buttons
[2].text
,
983 progress_button_callback
));
984 progress_buttons
[2].len
= button_get_len (BUTTON (progress_buttons
[2].w
));
985 widget_destroy (progress_buttons
[2].w
);
987 progress_buttons
[2].w
= progress_buttons
[1].w
;
989 progress_buttons
[3].w
= WIDGET (button_new (y
, 0, progress_buttons
[3].action
,
990 progress_buttons
[3].flags
, progress_buttons
[3].text
,
991 progress_button_callback
));
992 if (progress_buttons
[3].len
== -1)
993 progress_buttons
[3].len
= button_get_len (BUTTON (progress_buttons
[3].w
));
995 group_add_widget (g
, progress_buttons
[0].w
);
996 group_add_widget (g
, progress_buttons
[1].w
);
997 group_add_widget (g
, progress_buttons
[3].w
);
1000 progress_buttons
[0].len
+ MAX (progress_buttons
[1].len
, progress_buttons
[2].len
) +
1001 progress_buttons
[3].len
;
1003 /* adjust dialog sizes */
1006 r
.cols
= MAX (COLS
* 2 / 3, buttons_width
+ 6);
1007 widget_set_size_rect (w
, &r
);
1009 place_progress_buttons (ui
->op_dlg
, FALSE
);
1011 widget_select (progress_buttons
[0].w
);
1013 /* We will manage the dialog without any help, that's why
1014 we have to call dlg_init */
1015 dlg_init (ui
->op_dlg
);
1018 /* --------------------------------------------------------------------------------------------- */
1021 file_progress_ui_destroy (file_op_context_t
*ctx
)
1023 if (ctx
!= NULL
&& ctx
->ui
!= NULL
)
1025 file_progress_ui_t
*ui
= (file_progress_ui_t
*) ctx
->ui
;
1027 dlg_run_done (ui
->op_dlg
);
1028 widget_destroy (WIDGET (ui
->op_dlg
));
1029 MC_PTR_FREE (ctx
->ui
);
1033 /* --------------------------------------------------------------------------------------------- */
1035 show progressbar for file
1039 file_progress_show (file_op_context_t
*ctx
, off_t done
, off_t total
,
1040 const char *stalled_msg
, gboolean force_update
)
1042 file_progress_ui_t
*ui
;
1044 if (ctx
== NULL
|| ctx
->ui
== NULL
)
1051 gauge_show (ui
->progress_file_gauge
, FALSE
);
1055 gauge_set_value (ui
->progress_file_gauge
, 1024, (int) (1024 * done
/ total
));
1056 gauge_show (ui
->progress_file_gauge
, TRUE
);
1061 if (!ui
->showing_eta
|| ctx
->eta_secs
<= 0.5)
1062 label_set_text (ui
->progress_file_label
, stalled_msg
);
1065 char buffer2
[BUF_TINY
];
1067 file_eta_prepare_for_show (buffer2
, ctx
->eta_secs
, FALSE
);
1069 label_set_textv (ui
->progress_file_label
, "%s %s", buffer2
, stalled_msg
);
1072 char buffer3
[BUF_TINY
];
1074 file_bps_prepare_for_show (buffer3
, ctx
->bps
);
1075 label_set_textv (ui
->progress_file_label
, "%s (%s) %s", buffer2
, buffer3
, stalled_msg
);
1081 /* --------------------------------------------------------------------------------------------- */
1084 file_progress_show_count (file_op_context_t
*ctx
)
1086 file_progress_ui_t
*ui
;
1088 if (ctx
== NULL
|| ctx
->ui
== NULL
)
1093 if (ui
->total_files_processed_label
== NULL
)
1096 if (ctx
->totals_computed
)
1097 label_set_textv (ui
->total_files_processed_label
, _("Files processed: %zu / %zu"),
1098 ctx
->total_progress_count
, ctx
->total_count
);
1100 label_set_textv (ui
->total_files_processed_label
, _("Files processed: %zu"),
1101 ctx
->total_progress_count
);
1104 /* --------------------------------------------------------------------------------------------- */
1107 file_progress_show_total (file_op_context_t
*ctx
, uintmax_t copied_bytes
, gint64 tv_current
,
1108 gboolean show_summary
)
1110 char buffer2
[BUF_TINY
];
1111 char buffer3
[BUF_TINY
];
1112 file_progress_ui_t
*ui
;
1114 if (ctx
== NULL
|| ctx
->ui
== NULL
)
1119 if (ui
->progress_total_gauge
!= NULL
)
1121 if (ctx
->total_bytes
== 0)
1122 gauge_show (ui
->progress_total_gauge
, FALSE
);
1125 gauge_set_value (ui
->progress_total_gauge
, 1024,
1126 (int) (1024 * copied_bytes
/ ctx
->total_bytes
));
1127 gauge_show (ui
->progress_total_gauge
, TRUE
);
1131 if (!show_summary
&& ctx
->total_bps
== 0)
1134 if (ui
->time_label
!= NULL
)
1136 char buffer4
[BUF_TINY
];
1138 file_frmt_time (buffer2
,
1139 (tv_current
- ctx
->pauses
- ctx
->total_transfer_start
) / G_USEC_PER_SEC
);
1141 if (ctx
->totals_computed
)
1143 file_eta_prepare_for_show (buffer3
, ctx
->total_eta_secs
, TRUE
);
1144 if (ctx
->total_bps
== 0)
1145 label_set_textv (ui
->time_label
, _("Time: %s %s"), buffer2
, buffer3
);
1148 file_bps_prepare_for_show (buffer4
, ctx
->total_bps
);
1149 label_set_textv (ui
->time_label
, _("Time: %s %s (%s)"), buffer2
, buffer3
, buffer4
);
1154 if (ctx
->total_bps
== 0)
1155 label_set_textv (ui
->time_label
, _("Time: %s"), buffer2
);
1158 file_bps_prepare_for_show (buffer4
, ctx
->total_bps
);
1159 label_set_textv (ui
->time_label
, _("Time: %s (%s)"), buffer2
, buffer4
);
1164 if (ui
->total_bytes_label
!= NULL
)
1166 size_trunc_len (buffer2
, 5, copied_bytes
, 0, panels_options
.kilobyte_si
);
1168 if (!ctx
->totals_computed
)
1169 hline_set_textv (ui
->total_bytes_label
, _(" Total: %s "), buffer2
);
1172 size_trunc_len (buffer3
, 5, ctx
->total_bytes
, 0, panels_options
.kilobyte_si
);
1173 hline_set_textv (ui
->total_bytes_label
, _(" Total: %s / %s "), buffer2
, buffer3
);
1180 /* --------------------------------------------------------------------------------------------- */
1183 file_progress_show_source (file_op_context_t
*ctx
, const vfs_path_t
*vpath
)
1185 file_progress_ui_t
*ui
;
1187 if (ctx
== NULL
|| ctx
->ui
== NULL
)
1194 label_set_text (ui
->src_file_label
, _("Source"));
1195 label_set_text (ui
->src_file
, truncFileStringSecure (ui
->op_dlg
, vfs_path_as_str (vpath
)));
1199 label_set_text (ui
->src_file_label
, NULL
);
1200 label_set_text (ui
->src_file
, NULL
);
1204 /* --------------------------------------------------------------------------------------------- */
1207 file_progress_show_target (file_op_context_t
*ctx
, const vfs_path_t
*vpath
)
1209 file_progress_ui_t
*ui
;
1211 if (ctx
== NULL
|| ctx
->ui
== NULL
)
1218 label_set_text (ui
->tgt_file_label
, _("Target"));
1219 label_set_text (ui
->tgt_file
, truncFileStringSecure (ui
->op_dlg
, vfs_path_as_str (vpath
)));
1223 label_set_text (ui
->tgt_file_label
, NULL
);
1224 label_set_text (ui
->tgt_file
, NULL
);
1228 /* --------------------------------------------------------------------------------------------- */
1231 file_progress_show_deleting (file_op_context_t
*ctx
, const vfs_path_t
*vpath
, size_t *count
)
1233 static gint64 timestamp
= 0;
1234 /* update with 25 FPS rate */
1235 static const gint64 delay
= G_USEC_PER_SEC
/ 25;
1239 if (ctx
== NULL
|| ctx
->ui
== NULL
)
1242 ret
= mc_time_elapsed (×tamp
, delay
);
1246 file_progress_ui_t
*ui
;
1251 if (ui
->src_file_label
!= NULL
)
1252 label_set_text (ui
->src_file_label
, _("Deleting"));
1254 s
= vfs_path_as_str (vpath
);
1255 label_set_text (ui
->src_file
, truncFileStringSecure (ui
->op_dlg
, s
));
1264 /* --------------------------------------------------------------------------------------------- */
1267 file_progress_real_query_replace (file_op_context_t
*ctx
, enum OperationMode mode
,
1268 const char *src
, struct stat
*src_stat
,
1269 const char *dst
, struct stat
*dst_stat
)
1271 file_progress_ui_t
*ui
;
1272 FileProgressStatus replace_with_zero
;
1274 if (ctx
== NULL
|| ctx
->ui
== NULL
)
1279 if (ui
->replace_result
== REPLACE_YES
|| ui
->replace_result
== REPLACE_NO
1280 || ui
->replace_result
== REPLACE_APPEND
)
1282 ui
->src_filename
= src
;
1283 ui
->src_stat
= src_stat
;
1284 ui
->tgt_filename
= dst
;
1285 ui
->dst_stat
= dst_stat
;
1286 ui
->replace_result
= overwrite_query_dialog (ctx
, mode
);
1289 replace_with_zero
= (src_stat
->st_size
== 0
1290 && ui
->dont_overwrite_with_zero
) ? FILE_SKIP
: FILE_CONT
;
1292 switch (ui
->replace_result
)
1296 if (src_stat
->st_mtime
> dst_stat
->st_mtime
)
1297 return replace_with_zero
;
1303 if (src_stat
->st_size
== dst_stat
->st_size
)
1306 return replace_with_zero
;
1308 case REPLACE_SMALLER
:
1310 if (src_stat
->st_size
> dst_stat
->st_size
)
1317 return replace_with_zero
;
1320 /* Careful: we fall through and set do_append */
1321 ctx
->do_reget
= dst_stat
->st_size
;
1324 case REPLACE_APPEND
:
1325 ctx
->do_append
= TRUE
;
1343 /* --------------------------------------------------------------------------------------------- */
1346 file_mask_dialog (file_op_context_t
*ctx
, gboolean only_one
, const char *format
, const void *text
,
1347 const char *def_text
, gboolean
*do_bg
)
1352 gboolean source_easy_patterns
= easy_patterns
;
1353 char fmd_buf
[BUF_MEDIUM
];
1354 char *dest_dir
= NULL
;
1356 char *def_text_secure
;
1361 /* unselect checkbox if target filesystem doesn't support attributes */
1362 preserve
= copymove_persistent_attr
&& filegui__check_attrs_on_fs (def_text
);
1364 ctx
->stable_symlinks
= FALSE
;
1367 /* filter out a possible password from def_text */
1368 vpath
= vfs_path_from_str_flags (def_text
, only_one
? VPF_NO_CANON
: VPF_NONE
);
1369 tmp
= vfs_path_to_str_flags (vpath
, 0, VPF_STRIP_PASSWORD
);
1370 vfs_path_free (vpath
, TRUE
);
1372 if (source_easy_patterns
)
1373 def_text_secure
= str_glob_escape (tmp
);
1375 def_text_secure
= str_regex_escape (tmp
);
1380 int format_len
, text_len
;
1383 format_len
= str_term_width1 (format
);
1384 text_len
= str_term_width1 (text
);
1385 max_len
= COLS
- 2 - 6;
1387 if (format_len
+ text_len
<= max_len
)
1389 fmd_xlen
= format_len
+ text_len
+ 6;
1390 fmd_xlen
= MAX (fmd_xlen
, 68);
1394 text
= str_trunc ((const char *) text
, max_len
- format_len
);
1395 fmd_xlen
= max_len
+ 6;
1398 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, (const char *) text
);
1402 fmd_xlen
= COLS
* 2 / 3;
1403 fmd_xlen
= MAX (fmd_xlen
, 68);
1404 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, *(const int *) text
);
1408 char *source_mask
= NULL
;
1413 quick_widget_t quick_widgets
[] = {
1415 QUICK_LABELED_INPUT (fmd_buf
, input_label_above
, easy_patterns
? "*" : "^(.*)$",
1416 "input-def", &source_mask
, NULL
, FALSE
, FALSE
,
1417 INPUT_COMPLETE_FILENAMES
),
1418 QUICK_START_COLUMNS
,
1419 QUICK_SEPARATOR (FALSE
),
1421 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns
, NULL
),
1423 QUICK_LABELED_INPUT (N_("to:"), input_label_above
, def_text_secure
, "input2", &dest_dir
,
1424 NULL
, FALSE
, FALSE
, INPUT_COMPLETE_FILENAMES
),
1425 QUICK_SEPARATOR (TRUE
),
1426 QUICK_START_COLUMNS
,
1427 QUICK_CHECKBOX (N_("Follow &links"), &ctx
->follow_links
, NULL
),
1428 QUICK_CHECKBOX (N_("Preserve &attributes"), &preserve
, NULL
),
1430 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx
->dive_into_subdirs
, NULL
),
1431 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx
->stable_symlinks
, NULL
),
1433 QUICK_START_BUTTONS (TRUE
, TRUE
),
1434 QUICK_BUTTON (N_("&OK"), B_ENTER
, NULL
, NULL
),
1435 #ifdef ENABLE_BACKGROUND
1436 QUICK_BUTTON (N_("&Background"), B_USER
, NULL
, NULL
),
1437 #endif /* ENABLE_BACKGROUND */
1438 QUICK_BUTTON (N_("&Cancel"), B_CANCEL
, NULL
, NULL
),
1443 WRect r
= { -1, -1, 0, fmd_xlen
};
1445 quick_dialog_t qdlg
= {
1446 r
, op_names
[ctx
->operation
], "[Mask Copy/Rename]",
1447 quick_widgets
, NULL
, NULL
1452 val
= quick_dialog_skip (&qdlg
, 4);
1454 if (val
== B_CANCEL
)
1456 g_free (def_text_secure
);
1460 ctx
->stat_func
= ctx
->follow_links
? mc_stat
: mc_lstat
;
1464 ctx
->preserve
= TRUE
;
1465 ctx
->umask_kill
= (mode_t
) (~0);
1466 ctx
->preserve_uidgid
= (geteuid () == 0);
1472 ctx
->preserve
= ctx
->preserve_uidgid
= FALSE
;
1475 ctx
->umask_kill
= i2
^ ((mode_t
) (~0));
1478 if (*dest_dir
== '\0')
1480 g_free (def_text_secure
);
1481 g_free (source_mask
);
1486 ctx
->search_handle
= mc_search_new (source_mask
, NULL
);
1487 if (ctx
->search_handle
!= NULL
)
1490 message (D_ERROR
, MSG_ERROR
, _("Invalid source pattern '%s'"), source_mask
);
1491 MC_PTR_FREE (dest_dir
);
1492 MC_PTR_FREE (source_mask
);
1495 g_free (def_text_secure
);
1496 g_free (source_mask
);
1498 ctx
->search_handle
->is_case_sensitive
= TRUE
;
1499 if (source_easy_patterns
)
1500 ctx
->search_handle
->search_type
= MC_SEARCH_T_GLOB
;
1502 ctx
->search_handle
->search_type
= MC_SEARCH_T_REGEX
;
1505 dest_dir
= tilde_expand (tmp
);
1507 vpath
= vfs_path_from_str (dest_dir
);
1509 ctx
->dest_mask
= strrchr (dest_dir
, PATH_SEP
);
1510 if (ctx
->dest_mask
== NULL
)
1511 ctx
->dest_mask
= dest_dir
;
1515 orig_mask
= ctx
->dest_mask
;
1517 if (*ctx
->dest_mask
== '\0'
1518 || (!ctx
->dive_into_subdirs
&& !is_wildcarded (ctx
->dest_mask
)
1520 || (mc_stat (vpath
, &buf
) == 0 && S_ISDIR (buf
.st_mode
))))
1521 || (ctx
->dive_into_subdirs
1522 && ((!only_one
&& !is_wildcarded (ctx
->dest_mask
))
1523 || (only_one
&& mc_stat (vpath
, &buf
) == 0 && S_ISDIR (buf
.st_mode
)))))
1524 ctx
->dest_mask
= g_strdup ("\\0");
1527 ctx
->dest_mask
= g_strdup (ctx
->dest_mask
);
1531 if (*dest_dir
== '\0')
1534 dest_dir
= g_strdup ("./");
1537 vfs_path_free (vpath
, TRUE
);
1546 /* --------------------------------------------------------------------------------------------- */