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, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
14 2004, 2005, 2006, 2007, 2009, 2011, 2012
15 The Free Software Foundation, Inc.
18 Janne Kukonlehto, 1994, 1995
19 Fred Leeflang, 1994, 1995
20 Miguel de Icaza, 1994, 1995, 1996
21 Jakub Jelinek, 1995, 1996
24 Slava Zanko, 2009-2012
25 Andrew Borodin, 2009-2012
27 This file is part of the Midnight Commander.
29 The Midnight Commander is free software: you can redistribute it
30 and/or modify it under the terms of the GNU General Public License as
31 published by the Free Software Foundation, either version 3 of the License,
32 or (at your option) any later version.
34 The Midnight Commander is distributed in the hope that it will be useful,
35 but WITHOUT ANY WARRANTY; without even the implied warranty of
36 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 GNU General Public License for more details.
39 You should have received a copy of the GNU General Public License
40 along with this program. If not, see <http://www.gnu.org/licenses/>.
44 * Please note that all dialogs used here must be safe for background
49 * \brief Source: file management GUI for the text mode edition
52 /* {{{ Include files */
56 /* Keep this conditional in sync with the similar conditional in m4.include/mc-get-fs-info. */
57 #if ((STAT_STATVFS || STAT_STATVFS64) \
58 && (HAVE_STRUCT_STATVFS_F_BASETYPE || HAVE_STRUCT_STATVFS_F_FSTYPENAME \
59 || (! HAVE_STRUCT_STATFS_F_FSTYPENAME)))
69 #include <sys/types.h>
73 #include <sys/statvfs.h>
76 #elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
77 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
78 It does have statvfs.h, but shouldn't use it, since it doesn't
79 HAVE_STRUCT_STATVFS_F_BASETYPE. So find a clean way to fix it. */
80 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
81 #include <sys/param.h>
82 #include <sys/mount.h>
83 #if HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
84 /* Ultrix 4.4 needs these for the declaration of struct statfs. */
85 #include <netinet/in.h>
86 #include <nfs/nfs_clnt.h>
89 #elif HAVE_OS_H /* BeOS */
94 #if ! STAT_STATVFS && STAT_STATVFS64
95 #define STRUCT_STATVFS struct statvfs64
96 #define STATFS statvfs64
98 #define STRUCT_STATVFS struct statvfs
99 #define STATFS statvfs
100 /* Return true if statvfs works. This is false for statvfs on systems
101 with GNU libc on Linux kernels before 2.6.36, which stats all
102 preceding entries in /proc/mounts; that makes df hang if even one
103 of the corresponding file systems is hard-mounted but not available. */
104 #if ! (__linux__ && (__GLIBC__ || __UCLIBC__))
111 #include <string.h> /* for strverscmp */
112 #include <sys/utsname.h>
113 #include <sys/statfs.h>
114 #define STAT_STATFS2_BSIZE 1
119 static int statvfs_works_cache
= -1;
122 if (statvfs_works_cache
< 0)
123 statvfs_works_cache
= (uname (&name
) == 0 && 0 <= strverscmp (name
.release
, "2.6.36"));
124 return statvfs_works_cache
;
129 #define STATFS statfs
130 #define STRUCT_STATVFS struct statfs
131 #if HAVE_OS_H /* BeOS */
132 /* BeOS has a statvfs function, but it does not return sensible values
133 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
134 f_fstypename. Use 'struct fs_info' instead. */
136 statfs (char const *filename
, struct fs_info
*buf
)
138 dev_t device
= dev_for_path (filename
);
142 errno
= (device
== B_ENTRY_NOT_FOUND
? ENOENT
143 : device
== B_BAD_VALUE
? EINVAL
144 : device
== B_NAME_TOO_LONG
? ENAMETOOLONG
145 : device
== B_NO_MEMORY
? ENOMEM
: device
== B_FILE_ERROR
? EIO
: 0);
148 /* If successful, buf->dev will be == device. */
149 return fs_stat_dev (device
, buf
);
152 #define STRUCT_STATVFS struct fs_info
154 #define STRUCT_STATVFS struct statfs
158 #if HAVE_STRUCT_STATVFS_F_BASETYPE
159 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
161 #if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
162 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
163 #elif HAVE_OS_H /* BeOS */
164 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
170 #include "lib/global.h"
172 #include "lib/tty/key.h" /* tty_get_event */
173 #include "lib/mcconfig.h"
174 #include "lib/search.h"
175 #include "lib/vfs/vfs.h"
176 #include "lib/strescape.h"
177 #include "lib/strutil.h"
178 #include "lib/timefmt.h" /* file_date() */
179 #include "lib/util.h"
180 #include "lib/widget.h"
182 #include "src/setup.h" /* verbose */
184 #include "midnight.h"
185 #include "fileopctx.h" /* FILE_CONT */
191 /*** global variables ****************************************************************************/
193 int classic_progressbar
= 1;
195 /*** file scope macro definitions ****************************************************************/
197 /* Hack: the vfs code should not rely on this */
198 #define WITH_FULL_PATHS 1
200 #define truncFileString(ui, s) str_trunc (s, 52)
201 #define truncFileStringSecure(ui, s) path_trunc (s, 52)
203 /*** file scope type declarations ****************************************************************/
207 MSDOS_SUPER_MAGIC
= 0x4d44,
208 NTFS_SB_MAGIC
= 0x5346544e,
209 FUSE_MAGIC
= 0x65735546,
210 PROC_SUPER_MAGIC
= 0x9fa0,
211 SMB_SUPER_MAGIC
= 0x517B,
212 NCP_SUPER_MAGIC
= 0x564c,
213 USBDEVICE_SUPER_MAGIC
= 0x9fa2
214 } filegui_nonattrs_fs_t
;
217 /* Used for button result values */
220 REPLACE_YES
= B_USER
,
231 /* This structure describes the UI and internal data required by a file
237 gboolean showing_eta
;
238 gboolean showing_bps
;
240 /* Dialog and widgets for the operation progress window */
242 WLabel
*file_string
[2];
243 WLabel
*file_label
[2];
244 WGauge
*progress_file_gauge
;
245 WLabel
*progress_file_label
;
247 WGauge
*progress_total_gauge
;
249 WLabel
*total_files_processed_label
;
251 WLabel
*total_bytes_label
;
253 /* Query replace dialog */
254 WDialog
*replace_dlg
;
255 const char *replace_filename
;
256 replace_action_t replace_result
;
258 struct stat
*s_stat
, *d_stat
;
261 /*** file scope variables ************************************************************************/
263 /*** file scope functions ************************************************************************/
264 /* --------------------------------------------------------------------------------------------- */
267 filegui__check_attrs_on_fs (const char *fs_path
)
271 if (!setup_copymove_persistent_attr
)
274 #if USE_STATVFS && defined(STAT_STATVFS)
275 if (statvfs_works () && statvfs (fs_path
, &stfs
) != 0)
278 if (STATFS (fs_path
, &stfs
) != 0)
282 #if (USE_STATVFS && defined(HAVE_STRUCT_STATVFS_F_TYPE)) || \
283 (!USE_STATVFS && defined(HAVE_STRUCT_STATFS_F_TYPE))
284 switch ((filegui_nonattrs_fs_t
) stfs
.f_type
)
286 case MSDOS_SUPER_MAGIC
:
288 case PROC_SUPER_MAGIC
:
289 case SMB_SUPER_MAGIC
:
290 case NCP_SUPER_MAGIC
:
291 case USBDEVICE_SUPER_MAGIC
:
296 #elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
297 if (strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "msdos") == 0
298 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "msdosfs") == 0
299 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "ntfs") == 0
300 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "procfs") == 0
301 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "smbfs") == 0
302 || strstr (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "fusefs") != NULL
)
304 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
305 if (strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "pcfs") == 0
306 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "ntfs") == 0
307 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "proc") == 0
308 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "smbfs") == 0
309 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "fuse") == 0)
316 /* --------------------------------------------------------------------------------------------- */
319 file_frmt_time (char *buffer
, double eta_secs
)
321 int eta_hours
, eta_mins
, eta_s
;
322 eta_hours
= eta_secs
/ (60 * 60);
323 eta_mins
= (eta_secs
- (eta_hours
* 60 * 60)) / 60;
324 eta_s
= eta_secs
- (eta_hours
* 60 * 60 + eta_mins
* 60);
325 g_snprintf (buffer
, BUF_TINY
, _("%d:%02d.%02d"), eta_hours
, eta_mins
, eta_s
);
328 /* --------------------------------------------------------------------------------------------- */
331 file_eta_prepare_for_show (char *buffer
, double eta_secs
, gboolean always_show
)
333 char _fmt_buff
[BUF_TINY
];
334 if (eta_secs
<= 0.5 && !always_show
)
341 file_frmt_time (_fmt_buff
, eta_secs
);
342 g_snprintf (buffer
, BUF_TINY
, _("ETA %s"), _fmt_buff
);
345 /* --------------------------------------------------------------------------------------------- */
348 file_bps_prepare_for_show (char *buffer
, long bps
)
350 if (bps
> 1024 * 1024)
352 g_snprintf (buffer
, BUF_TINY
, _("%.2f MB/s"), bps
/ (1024 * 1024.0));
356 g_snprintf (buffer
, BUF_TINY
, _("%.2f KB/s"), bps
/ 1024.0);
360 g_snprintf (buffer
, BUF_TINY
, _("%ld B/s"), bps
);
366 /* --------------------------------------------------------------------------------------------- */
368 * FIXME: probably it is better to replace this with quick dialog machinery,
369 * but actually I'm not familiar with it and have not much time :(
372 static replace_action_t
373 overwrite_query_dialog (FileOpContext
* ctx
, enum OperationMode mode
)
375 #define ADD_RD_BUTTON(i, ypos) \
376 add_widget_autopos (ui->replace_dlg, \
377 button_new (ypos, rd_widgets [i].xpos, rd_widgets [i].value, \
378 NORMAL_BUTTON, rd_widgets [i].text, NULL), \
379 rd_widgets [i].pos_flags, ui->replace_dlg->current->data)
381 #define ADD_RD_LABEL(i, p1, p2, ypos) \
382 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2); \
383 add_widget_autopos (ui->replace_dlg, \
384 label_new (ypos, rd_widgets [i].xpos, buffer), \
385 rd_widgets [i].pos_flags, \
386 ui->replace_dlg->current != NULL ? ui->replace_dlg->current->data : NULL)
389 const int rd_ylen
= 1;
392 unsigned long yes_id
;
398 widget_pos_flags_t pos_flags
;
399 int value
; /* 0 for labels */
404 { N_("Target file already exists!"), 3, 4, WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
, 0 },
406 { "%s", 4, 4, WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
, 0 },
407 /* 2 */ /* cannot use PRIuMAX here; %llu is used instead */
408 { N_("New : %s, size %llu"), 6, 4, WPOS_KEEP_DEFAULT
, 0 },
409 /* 3 */ /* cannot use PRIuMAX here; %llu is used instead */
410 { N_("Existing: %s, size %llu"), 7, 4, WPOS_KEEP_DEFAULT
, 0 },
412 { N_("Overwrite this target?"), 9, 4, WPOS_KEEP_DEFAULT
, 0 },
414 { N_("&Yes"), 9, 28, WPOS_KEEP_DEFAULT
, REPLACE_YES
},
416 { N_("&No"), 9, 37, WPOS_KEEP_DEFAULT
, REPLACE_NO
},
418 { N_("A&ppend"), 9, 45, WPOS_KEEP_DEFAULT
, REPLACE_APPEND
},
420 { N_("&Reget"), 10, 28, WPOS_KEEP_DEFAULT
, REPLACE_REGET
},
422 { N_("Overwrite all targets?"), 11, 4, WPOS_KEEP_DEFAULT
, 0 },
424 { N_("A&ll"), 11, 28, WPOS_KEEP_DEFAULT
, REPLACE_ALWAYS
},
426 { N_("&Update"), 11, 36, WPOS_KEEP_DEFAULT
, REPLACE_UPDATE
},
428 { N_("Non&e"), 11, 47, WPOS_KEEP_DEFAULT
, REPLACE_NEVER
},
430 { N_("If &size differs"), 12, 28, WPOS_KEEP_DEFAULT
, REPLACE_SIZE
},
432 { N_("&Abort"), 14, 25, WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
, REPLACE_ABORT
}
436 const size_t num
= G_N_ELEMENTS (rd_widgets
);
439 FileOpContextUI
*ui
= ctx
->ui
;
441 char buffer
[BUF_SMALL
];
443 vfs_path_t
*stripped_vpath
;
444 const char *stripped_name
;
445 char *stripped_name_orig
;
448 widgets_len
= g_new0 (int, num
);
450 if (mode
== Foreground
)
451 title
= _("File exists");
453 title
= _("Background process: File exists");
455 stripped_vpath
= vfs_path_from_str (ui
->replace_filename
);
456 stripped_name
= stripped_name_orig
=
457 vfs_path_to_str_flags (stripped_vpath
, 0, VPF_STRIP_HOME
| VPF_STRIP_PASSWORD
);
458 vfs_path_free (stripped_vpath
);
463 int stripped_name_len
;
465 for (i
= 0; i
< num
; i
++)
468 if (i
!= 1) /* skip filename */
469 rd_widgets
[i
].text
= _(rd_widgets
[i
].text
);
470 #endif /* ENABLE_NLS */
471 widgets_len
[i
] = str_term_width1 (rd_widgets
[i
].text
);
475 * longest of "Overwrite..." labels
476 * (assume "Target date..." are short enough)
478 l1
= max (widgets_len
[9], widgets_len
[4]);
480 /* longest of button rows */
483 for (i
= 1; i
< num
- 1; i
++)
484 if (rd_widgets
[i
].value
!= 0)
486 if (row
!= rd_widgets
[i
].ypos
)
488 row
= rd_widgets
[i
].ypos
;
492 l
+= widgets_len
[i
] + 4;
495 l2
= max (l2
, l
); /* last row */
496 rd_xlen
= max (rd_xlen
, l1
+ l2
+ 8);
497 /* rd_xlen = max (rd_xlen, str_term_width1 (title) + 2); */
498 stripped_name_len
= str_term_width1 (stripped_name
);
499 rd_xlen
= max (rd_xlen
, min (COLS
, stripped_name_len
+ 8));
501 /* Now place widgets */
502 l1
+= 5; /* start of first button in the row */
505 for (i
= 2; i
< num
- 1; i
++)
506 if (rd_widgets
[i
].value
!= 0)
508 if (row
!= rd_widgets
[i
].ypos
)
510 row
= rd_widgets
[i
].ypos
;
513 rd_widgets
[i
].xpos
= l
;
514 l
+= widgets_len
[i
] + 4;
518 /* FIXME - missing help node */
520 create_dlg (TRUE
, 0, 0, rd_ylen
, rd_xlen
, alarm_colors
, NULL
, NULL
, "[Replace]", title
,
524 ADD_RD_LABEL (0, "", "", y
++);
526 ADD_RD_LABEL (1, str_trunc (stripped_name
, rd_xlen
- 8), "", y
++);
528 add_widget (ui
->replace_dlg
, hline_new (y
++, -1, -1));
530 /* source date and size */
531 ADD_RD_LABEL (2, file_date (ui
->s_stat
->st_mtime
), (unsigned long long) ui
->s_stat
->st_size
,
533 /* destination date and size */
534 ADD_RD_LABEL (3, file_date (ui
->d_stat
->st_mtime
), (unsigned long long) ui
->d_stat
->st_size
,
537 add_widget (ui
->replace_dlg
, hline_new (y
++, -1, -1));
539 ADD_RD_LABEL (4, 0, 0, y
); /* Overwrite this target? */
540 yes_id
= ADD_RD_BUTTON (5, y
); /* Yes */
541 ADD_RD_BUTTON (6, y
); /* No */
543 /* "this target..." widgets */
544 if (!S_ISDIR (ui
->d_stat
->st_mode
))
546 ADD_RD_BUTTON (7, y
++); /* Append */
548 if ((ctx
->operation
== OP_COPY
) && (ui
->d_stat
->st_size
!= 0)
549 && (ui
->s_stat
->st_size
> ui
->d_stat
->st_size
))
550 ADD_RD_BUTTON (8, y
++); /* Reget */
553 add_widget (ui
->replace_dlg
, hline_new (y
++, -1, -1));
555 ADD_RD_LABEL (9, 0, 0, y
); /* Overwrite all targets? */
556 ADD_RD_BUTTON (10, y
); /* All" */
557 ADD_RD_BUTTON (11, y
); /* Update */
558 ADD_RD_BUTTON (12, y
++); /* None */
559 ADD_RD_BUTTON (13, y
++); /* If size differs */
561 add_widget (ui
->replace_dlg
, hline_new (y
++, -1, -1));
563 ADD_RD_BUTTON (14, y
); /* Abort */
565 dlg_set_size (ui
->replace_dlg
, y
+ 3, rd_xlen
);
566 dlg_select_by_id (ui
->replace_dlg
, yes_id
);
567 result
= run_dlg (ui
->replace_dlg
);
568 destroy_dlg (ui
->replace_dlg
);
570 g_free (widgets_len
);
571 g_free (stripped_name_orig
);
573 return (result
== B_CANCEL
) ? REPLACE_ABORT
: (replace_action_t
) result
;
578 /* --------------------------------------------------------------------------------------------- */
581 is_wildcarded (char *p
)
587 if (*p
== '\\' && p
[1] >= '1' && p
[1] <= '9')
593 /* --------------------------------------------------------------------------------------------- */
594 /*** public functions ****************************************************************************/
595 /* --------------------------------------------------------------------------------------------- */
598 check_progress_buttons (FileOpContext
* ctx
)
604 g_return_val_if_fail (ctx
->ui
!= NULL
, FILE_CONT
);
608 event
.x
= -1; /* Don't show the GPM cursor */
609 c
= tty_get_event (&event
, FALSE
, FALSE
);
613 /* Reinitialize to avoid old values after events other than
614 selecting a button */
615 ui
->op_dlg
->ret_value
= FILE_CONT
;
617 dlg_process_event (ui
->op_dlg
, c
, &event
);
618 switch (ui
->op_dlg
->ret_value
)
631 /* --------------------------------------------------------------------------------------------- */
632 /* {{{ File progress display routines */
635 file_op_context_create_ui (FileOpContext
* ctx
, gboolean with_eta
,
636 filegui_dialog_type_t dialog_type
)
640 const char *abort_button_label
= N_("&Abort");
641 const char *skip_button_label
= N_("&Skip");
642 int abort_button_width
, skip_button_width
, buttons_width
;
643 int dlg_width
, dlg_height
;
647 g_return_if_fail (ctx
!= NULL
);
648 g_return_if_fail (ctx
->ui
== NULL
);
651 abort_button_label
= _(abort_button_label
);
652 skip_button_label
= _(skip_button_label
);
655 abort_button_width
= str_term_width1 (abort_button_label
) + 3;
656 skip_button_width
= str_term_width1 (skip_button_label
) + 3;
657 buttons_width
= abort_button_width
+ skip_button_width
+ 1;
659 dlg_width
= max (58, buttons_width
+ 6);
660 dlg_height
= 17; /* will be adjusted later */
662 ctx
->dialog_type
= dialog_type
;
663 ctx
->recursive_result
= RECURSIVE_YES
;
664 ctx
->ui
= g_new0 (FileOpContextUI
, 1);
667 ui
->replace_result
= REPLACE_YES
;
668 ui
->showing_eta
= with_eta
&& file_op_compute_totals
;
669 ui
->showing_bps
= with_eta
;
672 create_dlg (TRUE
, 0, 0, dlg_height
, dlg_width
, dialog_colors
, NULL
, NULL
, NULL
,
673 op_names
[ctx
->operation
], DLG_CENTER
);
675 ui
->file_label
[0] = label_new (y
++, x
, "");
676 add_widget (ui
->op_dlg
, ui
->file_label
[0]);
678 ui
->file_string
[0] = label_new (y
++, x
, "");
679 add_widget (ui
->op_dlg
, ui
->file_string
[0]);
681 ui
->file_label
[1] = label_new (y
++, x
, "");
682 add_widget (ui
->op_dlg
, ui
->file_label
[1]);
684 ui
->file_string
[1] = label_new (y
++, x
, "");
685 add_widget (ui
->op_dlg
, ui
->file_string
[1]);
687 ui
->progress_file_gauge
= gauge_new (y
++, x
+ 3, 0, 100, 0);
688 if (!classic_progressbar
&& (current_panel
== right_panel
))
689 ui
->progress_file_gauge
->from_left_to_right
= FALSE
;
690 add_widget (ui
->op_dlg
, ui
->progress_file_gauge
);
692 ui
->progress_file_label
= label_new (y
++, x
, "");
693 add_widget (ui
->op_dlg
, ui
->progress_file_label
);
695 if (verbose
&& dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
697 add_widget (ui
->op_dlg
, hline_new (y
, -1, -1));
699 ui
->total_bytes_label
= label_new (y
++, x
+ 15, "");
700 add_widget (ui
->op_dlg
, ui
->total_bytes_label
);
702 if (file_op_compute_totals
)
704 ui
->progress_total_gauge
= gauge_new (y
++, x
+ 3, 0, 100, 0);
705 if (!classic_progressbar
&& (current_panel
== right_panel
))
706 ui
->progress_total_gauge
->from_left_to_right
= FALSE
;
707 add_widget (ui
->op_dlg
, ui
->progress_total_gauge
);
710 ui
->total_files_processed_label
= label_new (y
++, x
, "");
711 add_widget (ui
->op_dlg
, ui
->total_files_processed_label
);
713 ui
->time_label
= label_new (y
++, x
, "");
714 add_widget (ui
->op_dlg
, ui
->time_label
);
717 add_widget (ui
->op_dlg
, hline_new (y
++, -1, -1));
719 x
= (dlg_width
- buttons_width
) / 2;
720 skip_button
= WIDGET (button_new (y
, x
, FILE_SKIP
, NORMAL_BUTTON
, skip_button_label
, NULL
));
721 add_widget (ui
->op_dlg
, skip_button
);
723 x
+= skip_button_width
+ 1;
724 add_widget (ui
->op_dlg
, button_new (y
, x
, FILE_ABORT
, NORMAL_BUTTON
, abort_button_label
, NULL
));
726 /* adjust dialog height */
727 dlg_set_size (ui
->op_dlg
, y
+ 3, dlg_width
);
729 dlg_select_widget (skip_button
);
731 /* We will manage the dialog without any help, that's why
732 we have to call init_dlg */
733 init_dlg (ui
->op_dlg
);
736 /* --------------------------------------------------------------------------------------------- */
739 file_op_context_destroy_ui (FileOpContext
* ctx
)
741 g_return_if_fail (ctx
!= NULL
);
745 FileOpContextUI
*ui
= (FileOpContextUI
*) ctx
->ui
;
747 dlg_run_done (ui
->op_dlg
);
748 destroy_dlg (ui
->op_dlg
);
754 /* --------------------------------------------------------------------------------------------- */
756 show progressbar for file
760 file_progress_show (FileOpContext
* ctx
, off_t done
, off_t total
,
761 const char *stalled_msg
, gboolean force_update
)
764 char buffer
[BUF_TINY
];
765 char buffer2
[BUF_TINY
];
766 char buffer3
[BUF_TINY
];
771 g_return_if_fail (ctx
!= NULL
);
772 g_return_if_fail (ctx
->ui
!= NULL
);
778 gauge_show (ui
->progress_file_gauge
, 0);
782 gauge_set_value (ui
->progress_file_gauge
, 1024, (int) (1024 * done
/ total
));
783 gauge_show (ui
->progress_file_gauge
, 1);
788 if (ui
->showing_eta
&& ctx
->eta_secs
> 0.5)
790 file_eta_prepare_for_show (buffer2
, ctx
->eta_secs
, FALSE
);
792 g_snprintf (buffer
, BUF_TINY
, "%s %s", buffer2
, stalled_msg
);
795 file_bps_prepare_for_show (buffer3
, ctx
->bps
);
796 g_snprintf (buffer
, BUF_TINY
, "%s (%s) %s", buffer2
, buffer3
, stalled_msg
);
801 g_snprintf (buffer
, BUF_TINY
, "%s", stalled_msg
);
804 label_set_text (ui
->progress_file_label
, buffer
);
807 /* --------------------------------------------------------------------------------------------- */
810 file_progress_show_count (FileOpContext
* ctx
, size_t done
, size_t total
)
812 char buffer
[BUF_TINY
];
815 g_return_if_fail (ctx
!= NULL
);
816 g_return_if_fail (ctx
->ui
!= NULL
);
819 if (file_op_compute_totals
)
820 g_snprintf (buffer
, BUF_TINY
, _("Files processed: %zu/%zu"), done
, total
);
822 g_snprintf (buffer
, BUF_TINY
, _("Files processed: %zu"), done
);
823 label_set_text (ui
->total_files_processed_label
, buffer
);
826 /* --------------------------------------------------------------------------------------------- */
829 file_progress_show_total (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, uintmax_t copied_bytes
,
830 gboolean show_summary
)
832 char buffer
[BUF_TINY
];
833 char buffer2
[BUF_TINY
];
834 char buffer3
[BUF_TINY
];
835 char buffer4
[BUF_TINY
];
836 struct timeval tv_current
;
839 g_return_if_fail (ctx
!= NULL
);
840 g_return_if_fail (ctx
->ui
!= NULL
);
844 if (file_op_compute_totals
)
846 if (ctx
->progress_bytes
== 0)
847 gauge_show (ui
->progress_total_gauge
, 0);
850 gauge_set_value (ui
->progress_total_gauge
, 1024,
851 (int) (1024 * copied_bytes
/ ctx
->progress_bytes
));
852 gauge_show (ui
->progress_total_gauge
, 1);
856 if (!show_summary
&& tctx
->bps
== 0)
859 gettimeofday (&tv_current
, NULL
);
860 file_frmt_time (buffer2
, tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
);
862 if (file_op_compute_totals
)
864 file_eta_prepare_for_show (buffer3
, tctx
->eta_secs
, TRUE
);
866 g_snprintf (buffer
, BUF_TINY
, _("Time: %s %s"), buffer2
, buffer3
);
869 file_bps_prepare_for_show (buffer4
, (long) tctx
->bps
);
870 g_snprintf (buffer
, BUF_TINY
, _("Time: %s %s (%s)"), buffer2
, buffer3
, buffer4
);
876 g_snprintf (buffer
, BUF_TINY
, _("Time: %s"), buffer2
);
879 file_bps_prepare_for_show (buffer4
, (long) tctx
->bps
);
880 g_snprintf (buffer
, BUF_TINY
, _("Time: %s (%s)"), buffer2
, buffer4
);
884 label_set_text (ui
->time_label
, buffer
);
886 size_trunc_len (buffer2
, 5, tctx
->copied_bytes
, 0, panels_options
.kilobyte_si
);
887 if (!file_op_compute_totals
)
888 g_snprintf (buffer
, BUF_TINY
, _(" Total: %s "), buffer2
);
891 size_trunc_len (buffer3
, 5, ctx
->progress_bytes
, 0, panels_options
.kilobyte_si
);
892 g_snprintf (buffer
, BUF_TINY
, _(" Total: %s/%s "), buffer2
, buffer3
);
895 label_set_text (ui
->total_bytes_label
, buffer
);
900 /* --------------------------------------------------------------------------------------------- */
903 file_progress_show_source (FileOpContext
* ctx
, const vfs_path_t
* s_vpath
)
907 g_return_if_fail (ctx
!= NULL
);
908 g_return_if_fail (ctx
->ui
!= NULL
);
916 s
= vfs_path_tokens_get (s_vpath
, -1, 1);
917 label_set_text (ui
->file_label
[0], _("Source"));
918 label_set_text (ui
->file_string
[0], truncFileString (ui
, s
));
923 label_set_text (ui
->file_label
[0], "");
924 label_set_text (ui
->file_string
[0], "");
928 /* --------------------------------------------------------------------------------------------- */
931 file_progress_show_target (FileOpContext
* ctx
, const vfs_path_t
* s_vpath
)
935 g_return_if_fail (ctx
!= NULL
);
936 g_return_if_fail (ctx
->ui
!= NULL
);
944 s
= vfs_path_to_str (s_vpath
);
945 label_set_text (ui
->file_label
[1], _("Target"));
946 label_set_text (ui
->file_string
[1], truncFileStringSecure (ui
, s
));
951 label_set_text (ui
->file_label
[1], "");
952 label_set_text (ui
->file_string
[1], "");
956 /* --------------------------------------------------------------------------------------------- */
959 file_progress_show_deleting (FileOpContext
* ctx
, const char *s
)
963 g_return_if_fail (ctx
!= NULL
);
964 g_return_if_fail (ctx
->ui
!= NULL
);
967 label_set_text (ui
->file_label
[0], _("Deleting"));
968 label_set_text (ui
->file_label
[0], truncFileStringSecure (ui
, s
));
971 /* --------------------------------------------------------------------------------------------- */
974 file_progress_real_query_replace (FileOpContext
* ctx
,
975 enum OperationMode mode
, const char *destname
,
976 struct stat
*_s_stat
, struct stat
*_d_stat
)
980 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
981 g_return_val_if_fail (ctx
->ui
!= NULL
, FILE_CONT
);
985 if (ui
->replace_result
< REPLACE_ALWAYS
)
987 ui
->replace_filename
= destname
;
988 ui
->s_stat
= _s_stat
;
989 ui
->d_stat
= _d_stat
;
990 ui
->replace_result
= overwrite_query_dialog (ctx
, mode
);
993 switch (ui
->replace_result
)
997 if (_s_stat
->st_mtime
> _d_stat
->st_mtime
)
1004 if (_s_stat
->st_size
== _d_stat
->st_size
)
1010 /* Careful: we fall through and set do_append */
1011 ctx
->do_reget
= _d_stat
->st_size
;
1013 case REPLACE_APPEND
:
1014 ctx
->do_append
= TRUE
;
1017 case REPLACE_ALWAYS
:
1030 /* --------------------------------------------------------------------------------------------- */
1033 file_mask_dialog (FileOpContext
* ctx
, FileOperation operation
,
1035 const char *format
, const void *text
, const char *def_text
, gboolean
* do_bg
)
1039 int source_easy_patterns
= easy_patterns
;
1040 char fmd_buf
[BUF_MEDIUM
];
1041 char *dest_dir
, *tmp
;
1042 char *def_text_secure
;
1044 g_return_val_if_fail (ctx
!= NULL
, NULL
);
1046 /* unselect checkbox if target filesystem don't support attributes */
1047 ctx
->op_preserve
= filegui__check_attrs_on_fs (def_text
);
1048 ctx
->stable_symlinks
= FALSE
;
1051 /* filter out a possible password from def_text */
1052 vpath
= vfs_path_from_str_flags (def_text
, only_one
? VPF_NO_CANON
: VPF_NONE
);
1053 tmp
= vfs_path_to_str_flags (vpath
, 0, VPF_STRIP_PASSWORD
);
1054 vfs_path_free (vpath
);
1056 if (source_easy_patterns
)
1057 def_text_secure
= strutils_glob_escape (tmp
);
1059 def_text_secure
= strutils_regex_escape (tmp
);
1064 int format_len
, text_len
;
1067 format_len
= str_term_width1 (format
);
1068 text_len
= str_term_width1 (text
);
1069 max_len
= COLS
- 2 - 6;
1071 if (format_len
+ text_len
<= max_len
)
1073 fmd_xlen
= format_len
+ text_len
+ 6;
1074 fmd_xlen
= max (fmd_xlen
, 68);
1078 text
= str_trunc ((const char *) text
, max_len
- format_len
);
1079 fmd_xlen
= max_len
+ 6;
1082 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, (const char *) text
);
1086 fmd_xlen
= COLS
* 2 / 3;
1087 fmd_xlen
= max (fmd_xlen
, 68);
1088 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, *(const int *) text
);
1092 char *source_mask
, *orig_mask
;
1096 quick_widget_t quick_widgets
[] = {
1098 QUICK_LABELED_INPUT (fmd_buf
, input_label_above
,
1099 easy_patterns
? "*" : "^(.*)$", 0, "input-def", &source_mask
,
1101 QUICK_START_COLUMNS
,
1102 QUICK_SEPARATOR (FALSE
),
1104 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns
, NULL
),
1106 QUICK_LABELED_INPUT (N_("to:"), input_label_above
,
1107 def_text_secure
, 0, "input2", &dest_dir
, NULL
),
1108 QUICK_SEPARATOR (TRUE
),
1109 QUICK_START_COLUMNS
,
1110 QUICK_CHECKBOX (N_("Follow &links"), &ctx
->follow_links
, NULL
),
1111 QUICK_CHECKBOX (N_("Preserve &attributes"), &ctx
->op_preserve
, NULL
),
1113 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx
->dive_into_subdirs
, NULL
),
1114 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx
->stable_symlinks
, NULL
),
1116 QUICK_START_BUTTONS (TRUE
, TRUE
),
1117 QUICK_BUTTON (N_("&OK"), B_ENTER
, NULL
, NULL
),
1118 #ifdef ENABLE_BACKGROUND
1119 QUICK_BUTTON (N_("&Background"), B_USER
, NULL
, NULL
),
1120 #endif /* ENABLE_BACKGROUND */
1121 QUICK_BUTTON (N_("&Cancel"), B_CANCEL
, NULL
, NULL
),
1126 quick_dialog_t qdlg
= {
1128 op_names
[operation
], "[Mask Copy/Rename]",
1129 quick_widgets
, NULL
, NULL
1133 val
= quick_dialog_skip (&qdlg
, 4);
1135 if (val
== B_CANCEL
)
1137 g_free (def_text_secure
);
1141 if (ctx
->follow_links
)
1142 ctx
->stat_func
= mc_stat
;
1144 ctx
->stat_func
= mc_lstat
;
1146 if (ctx
->op_preserve
)
1148 ctx
->preserve
= TRUE
;
1149 ctx
->umask_kill
= 0777777;
1150 ctx
->preserve_uidgid
= (geteuid () == 0);
1156 ctx
->preserve
= ctx
->preserve_uidgid
= FALSE
;
1159 ctx
->umask_kill
= i2
^ 0777777;
1162 if ((dest_dir
== NULL
) || (*dest_dir
== '\0'))
1164 g_free (def_text_secure
);
1165 g_free (source_mask
);
1169 ctx
->search_handle
= mc_search_new (source_mask
, -1);
1171 if (ctx
->search_handle
== NULL
)
1173 message (D_ERROR
, MSG_ERROR
, _("Invalid source pattern `%s'"), source_mask
);
1175 g_free (source_mask
);
1179 g_free (def_text_secure
);
1180 g_free (source_mask
);
1182 ctx
->search_handle
->is_case_sensitive
= TRUE
;
1183 if (source_easy_patterns
)
1184 ctx
->search_handle
->search_type
= MC_SEARCH_T_GLOB
;
1186 ctx
->search_handle
->search_type
= MC_SEARCH_T_REGEX
;
1189 dest_dir
= tilde_expand (tmp
);
1191 vpath
= vfs_path_from_str (dest_dir
);
1193 ctx
->dest_mask
= strrchr (dest_dir
, PATH_SEP
);
1194 if (ctx
->dest_mask
== NULL
)
1195 ctx
->dest_mask
= dest_dir
;
1198 orig_mask
= ctx
->dest_mask
;
1199 if (*ctx
->dest_mask
== '\0'
1200 || (!ctx
->dive_into_subdirs
&& !is_wildcarded (ctx
->dest_mask
)
1202 || (mc_stat (vpath
, &buf
) == 0 && S_ISDIR (buf
.st_mode
))))
1203 || (ctx
->dive_into_subdirs
1204 && ((!only_one
&& !is_wildcarded (ctx
->dest_mask
))
1205 || (only_one
&& mc_stat (vpath
, &buf
) == 0 && S_ISDIR (buf
.st_mode
)))))
1206 ctx
->dest_mask
= g_strdup ("\\0");
1209 ctx
->dest_mask
= g_strdup (ctx
->dest_mask
);
1212 if (*dest_dir
== '\0')
1215 dest_dir
= g_strdup ("./");
1217 vfs_path_free (vpath
);
1225 /* --------------------------------------------------------------------------------------------- */