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 #define STRUCT_STATVFS struct statvfs
95 #if ! STAT_STATVFS && STAT_STATVFS64
96 #define STATFS statvfs64
98 #define STATFS statvfs
101 #define STATFS statfs
102 #if HAVE_OS_H /* BeOS */
103 /* BeOS has a statvfs function, but it does not return sensible values
104 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
105 f_fstypename. Use 'struct fs_info' instead. */
107 statfs (char const *filename
, struct fs_info
*buf
)
109 dev_t device
= dev_for_path (filename
);
113 errno
= (device
== B_ENTRY_NOT_FOUND
? ENOENT
114 : device
== B_BAD_VALUE
? EINVAL
115 : device
== B_NAME_TOO_LONG
? ENAMETOOLONG
116 : device
== B_NO_MEMORY
? ENOMEM
: device
== B_FILE_ERROR
? EIO
: 0);
119 /* If successful, buf->dev will be == device. */
120 return fs_stat_dev (device
, buf
);
123 #define STRUCT_STATVFS struct fs_info
125 #define STRUCT_STATVFS struct statfs
129 #if HAVE_STRUCT_STATVFS_F_BASETYPE
130 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
132 #if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
133 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
134 #elif HAVE_OS_H /* BeOS */
135 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
141 #include "lib/global.h"
143 #include "lib/tty/key.h" /* tty_get_event */
144 #include "lib/mcconfig.h"
145 #include "lib/search.h"
146 #include "lib/vfs/vfs.h"
147 #include "lib/strescape.h"
148 #include "lib/strutil.h"
149 #include "lib/timefmt.h" /* file_date() */
150 #include "lib/util.h"
151 #include "lib/widget.h"
153 #include "src/setup.h" /* verbose */
155 #include "midnight.h"
156 #include "fileopctx.h" /* FILE_CONT */
162 /*** global variables ****************************************************************************/
164 int classic_progressbar
= 1;
166 /*** file scope macro definitions ****************************************************************/
168 /* Hack: the vfs code should not rely on this */
169 #define WITH_FULL_PATHS 1
171 #define truncFileString(ui, s) str_trunc (s, 52)
172 #define truncFileStringSecure(ui, s) path_trunc (s, 52)
174 /*** file scope type declarations ****************************************************************/
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
;
188 /* Used for button result values */
191 REPLACE_YES
= B_USER
,
202 /* This structure describes the UI and internal data required by a file
208 gboolean showing_eta
;
209 gboolean showing_bps
;
211 /* Dialog and widgets for the operation progress window */
213 WLabel
*file_string
[2];
214 WLabel
*file_label
[2];
215 WGauge
*progress_file_gauge
;
216 WLabel
*progress_file_label
;
218 WGauge
*progress_total_gauge
;
220 WLabel
*total_files_processed_label
;
222 WLabel
*total_bytes_label
;
224 /* Query replace dialog */
225 Dlg_head
*replace_dlg
;
226 const char *replace_filename
;
227 replace_action_t replace_result
;
229 struct stat
*s_stat
, *d_stat
;
232 /*** file scope variables ************************************************************************/
234 /*** file scope functions ************************************************************************/
235 /* --------------------------------------------------------------------------------------------- */
238 filegui__check_attrs_on_fs (const char *fs_path
)
243 if (!setup_copymove_persistent_attr
)
246 if (statfs (fs_path
, &stfs
) != 0)
250 switch ((filegui_nonattrs_fs_t
) stfs
.f_type
)
252 case MSDOS_SUPER_MAGIC
:
254 case PROC_SUPER_MAGIC
:
255 case SMB_SUPER_MAGIC
:
256 case NCP_SUPER_MAGIC
:
257 case USBDEVICE_SUPER_MAGIC
:
262 #elif defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) \
263 || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
264 if (strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "msdos") == 0
265 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "msdosfs") == 0
266 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "ntfs") == 0
267 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "procfs") == 0
268 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "smbfs") == 0
269 || strstr (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "fusefs") != NULL
)
271 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
272 if (strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "pcfs") == 0
273 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "ntfs") == 0
274 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "proc") == 0
275 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "smbfs") == 0
276 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "fuse") == 0)
279 #endif /* USE_STATVFS */
284 /* --------------------------------------------------------------------------------------------- */
287 file_frmt_time (char *buffer
, double eta_secs
)
289 int eta_hours
, eta_mins
, eta_s
;
290 eta_hours
= eta_secs
/ (60 * 60);
291 eta_mins
= (eta_secs
- (eta_hours
* 60 * 60)) / 60;
292 eta_s
= eta_secs
- (eta_hours
* 60 * 60 + eta_mins
* 60);
293 g_snprintf (buffer
, BUF_TINY
, _("%d:%02d.%02d"), eta_hours
, eta_mins
, eta_s
);
296 /* --------------------------------------------------------------------------------------------- */
299 file_eta_prepare_for_show (char *buffer
, double eta_secs
, gboolean always_show
)
301 char _fmt_buff
[BUF_TINY
];
302 if (eta_secs
<= 0.5 && !always_show
)
309 file_frmt_time (_fmt_buff
, eta_secs
);
310 g_snprintf (buffer
, BUF_TINY
, _("ETA %s"), _fmt_buff
);
313 /* --------------------------------------------------------------------------------------------- */
316 file_bps_prepare_for_show (char *buffer
, long bps
)
318 if (bps
> 1024 * 1024)
320 g_snprintf (buffer
, BUF_TINY
, _("%.2f MB/s"), bps
/ (1024 * 1024.0));
324 g_snprintf (buffer
, BUF_TINY
, _("%.2f KB/s"), bps
/ 1024.0);
328 g_snprintf (buffer
, BUF_TINY
, _("%ld B/s"), bps
);
334 /* --------------------------------------------------------------------------------------------- */
336 * FIXME: probably it is better to replace this with quick dialog machinery,
337 * but actually I'm not familiar with it and have not much time :(
340 static replace_action_t
341 overwrite_query_dialog (FileOpContext
* ctx
, enum OperationMode mode
)
343 #define ADD_RD_BUTTON(i) \
344 add_widget (ui->replace_dlg, \
345 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value, \
346 NORMAL_BUTTON, rd_widgets [i].text, 0))
348 #define ADD_RD_LABEL(i, p1, p2) \
349 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2); \
350 add_widget (ui->replace_dlg, label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer))
353 const int rd_ylen
= 17;
360 int value
; /* 0 for labels */
365 { N_("Target file already exists!"), 3, 4, 0 },
368 /* 2 */ /* cannot use PRIuMAX here; %llu is used instead */
369 { N_("Source date: %s, size %llu"), 6, 4, 0 },
370 /* 3 */ /* cannot use PRIuMAX here; %llu is used instead */
371 { N_("Target date: %s, size %llu"), 7, 4, 0 },
373 { N_("&Abort"), 14, 25, REPLACE_ABORT
},
375 { N_("If &size differs"), 12, 28, REPLACE_SIZE
},
377 { N_("Non&e"), 11, 47, REPLACE_NEVER
},
379 { N_("&Update"), 11, 36, REPLACE_UPDATE
},
381 { N_("A&ll"), 11, 28, REPLACE_ALWAYS
},
383 { N_("Overwrite all targets?"), 11, 4, 0 },
385 { N_("&Reget"), 10, 28, REPLACE_REGET
},
387 { N_("A&ppend"), 9, 45, REPLACE_APPEND
},
389 { N_("&No"), 9, 37, REPLACE_NO
},
391 { N_("&Yes"), 9, 28, REPLACE_YES
},
393 { N_("Overwrite this target?"), 9, 4, 0 }
397 const int num
= sizeof (rd_widgets
) / sizeof (rd_widgets
[0]);
400 FileOpContextUI
*ui
= ctx
->ui
;
402 char buffer
[BUF_SMALL
];
404 int stripped_name_len
;
405 vfs_path_t
*stripped_vpath
;
406 const char *stripped_name
;
407 char *stripped_name_orig
;
410 widgets_len
= g_new0 (int, num
);
412 if (mode
== Foreground
)
413 title
= _("File exists");
415 title
= _("Background process: File exists");
417 stripped_vpath
= vfs_path_from_str (ui
->replace_filename
);
418 stripped_name
= stripped_name_orig
=
419 vfs_path_to_str_flags (stripped_vpath
, 0, VPF_STRIP_HOME
| VPF_STRIP_PASSWORD
);
420 vfs_path_free (stripped_vpath
);
421 stripped_name_len
= str_term_width1 (stripped_name
);
424 int i
, l1
, l2
, l
, row
;
426 for (i
= 0; i
< num
; i
++)
429 if (i
!= 1) /* skip filename */
430 rd_widgets
[i
].text
= _(rd_widgets
[i
].text
);
431 #endif /* ENABLE_NLS */
432 widgets_len
[i
] = str_term_width1 (rd_widgets
[i
].text
);
436 * longest of "Overwrite..." labels
437 * (assume "Target date..." are short enough)
439 l1
= max (widgets_len
[9], widgets_len
[14]);
441 /* longest of button rows */
443 for (row
= l
= l2
= 0; i
--;)
444 if (rd_widgets
[i
].value
!= 0)
446 if (row
!= rd_widgets
[i
].ypos
)
448 row
= rd_widgets
[i
].ypos
;
452 l
+= widgets_len
[i
] + 4;
455 l2
= max (l2
, l
); /* last row */
456 rd_xlen
= max (rd_xlen
, l1
+ l2
+ 8);
457 rd_xlen
= max (rd_xlen
, str_term_width1 (title
) + 2);
458 rd_xlen
= max (rd_xlen
, min (COLS
, stripped_name_len
+ 8));
460 /* Now place widgets */
461 l1
+= 5; /* start of first button in the row */
463 for (l
= l1
, row
= 0; --i
> 1;)
464 if (rd_widgets
[i
].value
!= 0)
466 if (row
!= rd_widgets
[i
].ypos
)
468 row
= rd_widgets
[i
].ypos
;
471 rd_widgets
[i
].xpos
= l
;
472 l
+= widgets_len
[i
] + 4;
475 /* Abort button is centered */
476 rd_widgets
[4].xpos
= (rd_xlen
- widgets_len
[4] - 3) / 2;
479 /* FIXME - missing help node */
481 create_dlg (TRUE
, 0, 0, rd_ylen
, rd_xlen
, alarm_colors
, NULL
, NULL
, "[Replace]",
482 title
, DLG_CENTER
| DLG_REVERSE
);
484 /* prompt -- centered */
485 add_widget (ui
->replace_dlg
,
486 label_new (rd_widgets
[0].ypos
, (rd_xlen
- widgets_len
[0]) / 2, rd_widgets
[0].text
));
487 /* file name -- centered */
488 stripped_name
= str_trunc (stripped_name
, rd_xlen
- 8);
489 stripped_name_len
= str_term_width1 (stripped_name
);
490 add_widget (ui
->replace_dlg
,
491 label_new (rd_widgets
[1].ypos
, (rd_xlen
- stripped_name_len
) / 2, stripped_name
));
493 /* source date and size */
494 ADD_RD_LABEL (2, file_date (ui
->s_stat
->st_mtime
), (unsigned long long) ui
->s_stat
->st_size
);
495 /* destination date and size */
496 ADD_RD_LABEL (3, file_date (ui
->d_stat
->st_mtime
), (unsigned long long) ui
->d_stat
->st_size
);
498 ADD_RD_BUTTON (4); /* Abort */
499 ADD_RD_BUTTON (5); /* If size differs */
500 ADD_RD_BUTTON (6); /* None */
501 ADD_RD_BUTTON (7); /* Update */
502 ADD_RD_BUTTON (8); /* All" */
503 ADD_RD_LABEL (9, 0, 0); /* Overwrite all targets? */
505 /* "this target..." widgets */
506 if (!S_ISDIR (ui
->d_stat
->st_mode
))
508 if ((ctx
->operation
== OP_COPY
) && (ui
->d_stat
->st_size
!= 0)
509 && (ui
->s_stat
->st_size
> ui
->d_stat
->st_size
))
510 ADD_RD_BUTTON (10); /* Reget */
512 ADD_RD_BUTTON (11); /* Append */
514 ADD_RD_BUTTON (12); /* No */
515 ADD_RD_BUTTON (13); /* Yes */
516 ADD_RD_LABEL (14, 0, 0); /* Overwrite this target? */
518 result
= run_dlg (ui
->replace_dlg
);
519 destroy_dlg (ui
->replace_dlg
);
521 g_free (widgets_len
);
522 g_free (stripped_name_orig
);
524 return (result
== B_CANCEL
) ? REPLACE_ABORT
: (replace_action_t
) result
;
529 /* --------------------------------------------------------------------------------------------- */
532 is_wildcarded (char *p
)
538 if (*p
== '\\' && p
[1] >= '1' && p
[1] <= '9')
544 /* --------------------------------------------------------------------------------------------- */
545 /*** public functions ****************************************************************************/
546 /* --------------------------------------------------------------------------------------------- */
549 check_progress_buttons (FileOpContext
* ctx
)
555 g_return_val_if_fail (ctx
->ui
!= NULL
, FILE_CONT
);
559 event
.x
= -1; /* Don't show the GPM cursor */
560 c
= tty_get_event (&event
, FALSE
, FALSE
);
564 /* Reinitialize to avoid old values after events other than
565 selecting a button */
566 ui
->op_dlg
->ret_value
= FILE_CONT
;
568 dlg_process_event (ui
->op_dlg
, c
, &event
);
569 switch (ui
->op_dlg
->ret_value
)
582 /* --------------------------------------------------------------------------------------------- */
583 /* {{{ File progress display routines */
586 file_op_context_create_ui_without_init (FileOpContext
* ctx
, gboolean with_eta
,
587 filegui_dialog_type_t dialog_type
)
590 const char *abort_button_label
= N_("&Abort");
591 const char *skip_button_label
= N_("&Skip");
592 int abort_button_width
, skip_button_width
, buttons_width
;
593 int dlg_width
, dlg_height
;
595 g_return_if_fail (ctx
!= NULL
);
596 g_return_if_fail (ctx
->ui
== NULL
);
599 abort_button_label
= _(abort_button_label
);
600 skip_button_label
= _(skip_button_label
);
603 abort_button_width
= str_term_width1 (abort_button_label
) + 3;
604 skip_button_width
= str_term_width1 (skip_button_label
) + 3;
605 buttons_width
= abort_button_width
+ skip_button_width
+ 2;
607 dlg_width
= max (58, buttons_width
+ 6);
608 dlg_height
= 17; /* to make compiler happy :) */
610 ui
= g_new0 (FileOpContextUI
, 1);
613 ctx
->dialog_type
= dialog_type
;
617 case FILEGUI_DIALOG_ONE_ITEM
:
618 dlg_height
= verbose
? 12 : 10;
620 case FILEGUI_DIALOG_MULTI_ITEM
:
621 dlg_height
= !verbose
? 10 : file_op_compute_totals
? 17 : 15;
623 case FILEGUI_DIALOG_DELETE_ITEM
:
628 ctx
->recursive_result
= RECURSIVE_YES
;
630 ui
->replace_result
= REPLACE_YES
;
631 ui
->showing_eta
= with_eta
&& file_op_compute_totals
;
632 ui
->showing_bps
= with_eta
;
635 create_dlg (TRUE
, 0, 0, dlg_height
, dlg_width
,
636 dialog_colors
, NULL
, NULL
, NULL
, op_names
[ctx
->operation
],
637 DLG_CENTER
| DLG_REVERSE
);
639 add_widget (ui
->op_dlg
,
640 button_new (dlg_height
- 3, dlg_width
/ 2 + 1, FILE_ABORT
,
641 NORMAL_BUTTON
, abort_button_label
, NULL
));
642 add_widget (ui
->op_dlg
,
643 button_new (dlg_height
- 3, dlg_width
/ 2 - 1 - skip_button_width
, FILE_SKIP
,
644 NORMAL_BUTTON
, skip_button_label
, NULL
));
646 if (verbose
&& dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
648 int dy
= file_op_compute_totals
? 2 : 0;
650 if (file_op_compute_totals
)
651 add_widget (ui
->op_dlg
, ui
->progress_total_gauge
=
652 gauge_new (7 + dy
, 3 + 3, 0, 100, 0));
654 add_widget (ui
->op_dlg
, ui
->total_files_processed_label
= label_new (9 + dy
, 3, ""));
656 add_widget (ui
->op_dlg
, ui
->time_label
= label_new (10 + dy
, 3, ""));
658 add_widget (ui
->op_dlg
, ui
->total_bytes_label
= label_new (8, 3 + 15, ""));
659 add_widget (ui
->op_dlg
, hline_new (8, 1, dlg_width
- 2));
662 add_widget (ui
->op_dlg
, ui
->progress_file_label
= label_new (7, 3, ""));
664 add_widget (ui
->op_dlg
, ui
->progress_file_gauge
= gauge_new (6, 3 + 3, 0, 100, 0));
666 add_widget (ui
->op_dlg
, ui
->file_string
[1] = label_new (5, 3, ""));
668 add_widget (ui
->op_dlg
, ui
->file_label
[1] = label_new (4, 3, ""));
669 add_widget (ui
->op_dlg
, ui
->file_string
[0] = label_new (3, 3, ""));
670 add_widget (ui
->op_dlg
, ui
->file_label
[0] = label_new (2, 3, ""));
672 if ((right_panel
== current_panel
) && !classic_progressbar
)
674 ui
->progress_file_gauge
->from_left_to_right
= FALSE
;
675 if (verbose
&& file_op_compute_totals
&& dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
676 ui
->progress_total_gauge
->from_left_to_right
= FALSE
;
680 /* --------------------------------------------------------------------------------------------- */
683 file_op_context_create_ui (FileOpContext
* ctx
, gboolean with_eta
,
684 filegui_dialog_type_t dialog_type
)
688 g_return_if_fail (ctx
!= NULL
);
689 g_return_if_fail (ctx
->ui
== NULL
);
691 file_op_context_create_ui_without_init (ctx
, with_eta
, dialog_type
);
694 /* We will manage the dialog without any help, that's why
695 we have to call init_dlg */
696 init_dlg (ui
->op_dlg
);
699 /* --------------------------------------------------------------------------------------------- */
702 file_op_context_destroy_ui (FileOpContext
* ctx
)
704 g_return_if_fail (ctx
!= NULL
);
708 FileOpContextUI
*ui
= (FileOpContextUI
*) ctx
->ui
;
710 dlg_run_done (ui
->op_dlg
);
711 destroy_dlg (ui
->op_dlg
);
717 /* --------------------------------------------------------------------------------------------- */
719 show progressbar for file
723 file_progress_show (FileOpContext
* ctx
, off_t done
, off_t total
,
724 const char *stalled_msg
, gboolean force_update
)
727 char buffer
[BUF_TINY
];
728 char buffer2
[BUF_TINY
];
729 char buffer3
[BUF_TINY
];
734 g_return_if_fail (ctx
!= NULL
);
735 g_return_if_fail (ctx
->ui
!= NULL
);
741 gauge_show (ui
->progress_file_gauge
, 0);
745 gauge_set_value (ui
->progress_file_gauge
, 1024, (int) (1024 * done
/ total
));
746 gauge_show (ui
->progress_file_gauge
, 1);
751 if (ui
->showing_eta
&& ctx
->eta_secs
> 0.5)
753 file_eta_prepare_for_show (buffer2
, ctx
->eta_secs
, FALSE
);
755 g_snprintf (buffer
, BUF_TINY
, "%s %s", buffer2
, stalled_msg
);
758 file_bps_prepare_for_show (buffer3
, ctx
->bps
);
759 g_snprintf (buffer
, BUF_TINY
, "%s (%s) %s", buffer2
, buffer3
, stalled_msg
);
764 g_snprintf (buffer
, BUF_TINY
, "%s", stalled_msg
);
767 label_set_text (ui
->progress_file_label
, buffer
);
770 /* --------------------------------------------------------------------------------------------- */
773 file_progress_show_count (FileOpContext
* ctx
, size_t done
, size_t total
)
775 char buffer
[BUF_TINY
];
778 g_return_if_fail (ctx
!= NULL
);
779 g_return_if_fail (ctx
->ui
!= NULL
);
782 if (file_op_compute_totals
)
783 g_snprintf (buffer
, BUF_TINY
, _("Files processed: %zu/%zu"), done
, total
);
785 g_snprintf (buffer
, BUF_TINY
, _("Files processed: %zu"), done
);
786 label_set_text (ui
->total_files_processed_label
, buffer
);
789 /* --------------------------------------------------------------------------------------------- */
792 file_progress_show_total (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, uintmax_t copied_bytes
,
793 gboolean show_summary
)
795 char buffer
[BUF_TINY
];
796 char buffer2
[BUF_TINY
];
797 char buffer3
[BUF_TINY
];
798 char buffer4
[BUF_TINY
];
799 struct timeval tv_current
;
802 g_return_if_fail (ctx
!= NULL
);
803 g_return_if_fail (ctx
->ui
!= NULL
);
807 if (file_op_compute_totals
)
809 if (ctx
->progress_bytes
== 0)
810 gauge_show (ui
->progress_total_gauge
, 0);
813 gauge_set_value (ui
->progress_total_gauge
, 1024,
814 (int) (1024 * copied_bytes
/ ctx
->progress_bytes
));
815 gauge_show (ui
->progress_total_gauge
, 1);
819 if (!show_summary
&& tctx
->bps
== 0)
822 gettimeofday (&tv_current
, NULL
);
823 file_frmt_time (buffer2
, tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
);
825 if (file_op_compute_totals
)
827 file_eta_prepare_for_show (buffer3
, tctx
->eta_secs
, TRUE
);
829 g_snprintf (buffer
, BUF_TINY
, _("Time: %s %s"), buffer2
, buffer3
);
832 file_bps_prepare_for_show (buffer4
, (long) tctx
->bps
);
833 g_snprintf (buffer
, BUF_TINY
, _("Time: %s %s (%s)"), buffer2
, buffer3
, buffer4
);
839 g_snprintf (buffer
, BUF_TINY
, _("Time: %s"), buffer2
);
842 file_bps_prepare_for_show (buffer4
, (long) tctx
->bps
);
843 g_snprintf (buffer
, BUF_TINY
, _("Time: %s (%s)"), buffer2
, buffer4
);
847 label_set_text (ui
->time_label
, buffer
);
849 size_trunc_len (buffer2
, 5, tctx
->copied_bytes
, 0, panels_options
.kilobyte_si
);
850 if (!file_op_compute_totals
)
851 g_snprintf (buffer
, BUF_TINY
, _(" Total: %s "), buffer2
);
854 size_trunc_len (buffer3
, 5, ctx
->progress_bytes
, 0, panels_options
.kilobyte_si
);
855 g_snprintf (buffer
, BUF_TINY
, _(" Total: %s/%s "), buffer2
, buffer3
);
858 label_set_text (ui
->total_bytes_label
, buffer
);
863 /* --------------------------------------------------------------------------------------------- */
866 file_progress_show_source (FileOpContext
* ctx
, const vfs_path_t
* s_vpath
)
870 g_return_if_fail (ctx
!= NULL
);
871 g_return_if_fail (ctx
->ui
!= NULL
);
879 s
= vfs_path_tokens_get (s_vpath
, -1, 1);
880 label_set_text (ui
->file_label
[0], _("Source"));
881 label_set_text (ui
->file_string
[0], truncFileString (ui
, s
));
886 label_set_text (ui
->file_label
[0], "");
887 label_set_text (ui
->file_string
[0], "");
891 /* --------------------------------------------------------------------------------------------- */
894 file_progress_show_target (FileOpContext
* ctx
, const vfs_path_t
* s_vpath
)
898 g_return_if_fail (ctx
!= NULL
);
899 g_return_if_fail (ctx
->ui
!= NULL
);
907 s
= vfs_path_to_str (s_vpath
);
908 label_set_text (ui
->file_label
[1], _("Target"));
909 label_set_text (ui
->file_string
[1], truncFileStringSecure (ui
, s
));
914 label_set_text (ui
->file_label
[1], "");
915 label_set_text (ui
->file_string
[1], "");
919 /* --------------------------------------------------------------------------------------------- */
922 file_progress_show_deleting (FileOpContext
* ctx
, const char *s
)
926 g_return_if_fail (ctx
!= NULL
);
927 g_return_if_fail (ctx
->ui
!= NULL
);
930 label_set_text (ui
->file_label
[0], _("Deleting"));
931 label_set_text (ui
->file_label
[0], truncFileStringSecure (ui
, s
));
934 /* --------------------------------------------------------------------------------------------- */
937 file_progress_real_query_replace (FileOpContext
* ctx
,
938 enum OperationMode mode
, const char *destname
,
939 struct stat
*_s_stat
, struct stat
*_d_stat
)
943 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
944 g_return_val_if_fail (ctx
->ui
!= NULL
, FILE_CONT
);
948 if (ui
->replace_result
< REPLACE_ALWAYS
)
950 ui
->replace_filename
= destname
;
951 ui
->s_stat
= _s_stat
;
952 ui
->d_stat
= _d_stat
;
953 ui
->replace_result
= overwrite_query_dialog (ctx
, mode
);
956 switch (ui
->replace_result
)
960 if (_s_stat
->st_mtime
> _d_stat
->st_mtime
)
967 if (_s_stat
->st_size
== _d_stat
->st_size
)
973 /* Careful: we fall through and set do_append */
974 ctx
->do_reget
= _d_stat
->st_size
;
977 ctx
->do_append
= TRUE
;
993 /* --------------------------------------------------------------------------------------------- */
996 file_mask_dialog (FileOpContext
* ctx
, FileOperation operation
,
998 const char *format
, const void *text
, const char *def_text
, gboolean
* do_bg
)
1000 const size_t FMDY
= 13;
1001 const size_t FMDX
= 68;
1005 const size_t gap
= 1;
1006 size_t b0_len
, b2_len
;
1009 int source_easy_patterns
= easy_patterns
;
1011 char fmd_buf
[BUF_MEDIUM
];
1012 char *source_mask
, *orig_mask
, *dest_dir
, *tmp
;
1013 char *def_text_secure
;
1016 QuickWidget fmd_widgets
[] = {
1017 /* 0 */ QUICK_BUTTON (42, 64, 10, FMDY
, N_("&Cancel"), B_CANCEL
, NULL
),
1018 #ifdef ENABLE_BACKGROUND
1019 /* 1 */ QUICK_BUTTON (25, 64, 10, FMDY
, N_("&Background"), B_USER
, NULL
),
1023 #endif /* ENABLE_BACKGROUND */
1025 QUICK_BUTTON (14, FMDX
, 10, FMDY
, N_("&OK"), B_ENTER
, NULL
),
1027 QUICK_CHECKBOX (42, FMDX
, 8, FMDY
, N_("&Stable Symlinks"), &ctx
->stable_symlinks
),
1029 QUICK_CHECKBOX (31, FMDX
, 7, FMDY
, N_("Di&ve into subdir if exists"),
1030 &ctx
->dive_into_subdirs
),
1032 QUICK_CHECKBOX (3, FMDX
, 8, FMDY
, N_("Preserve &attributes"), &ctx
->op_preserve
),
1034 QUICK_CHECKBOX (3, FMDX
, 7, FMDY
, N_("Follow &links"), &ctx
->follow_links
),
1036 QUICK_INPUT (3, FMDX
, 6, FMDY
, "", 58, 0, "input2", &dest_dir
),
1038 QUICK_LABEL (3, FMDX
, 5, FMDY
, N_("to:")),
1040 QUICK_CHECKBOX (37, FMDX
, 4, FMDY
, N_("&Using shell patterns"), &source_easy_patterns
),
1042 QUICK_INPUT (3, FMDX
, 3, FMDY
, easy_patterns
? "*" : "^(.*)$", 58, 0, "input-def",
1045 QUICK_LABEL (3, FMDX
, 2, FMDY
, fmd_buf
),
1049 g_return_val_if_fail (ctx
!= NULL
, NULL
);
1053 for (i
= 0; i
<= 2 - OFFSET
; i
++)
1054 fmd_widgets
[i
].u
.button
.text
= _(fmd_widgets
[i
].u
.button
.text
);
1057 for (i
= 3 - OFFSET
; i
<= 9 - OFFSET
; i
++)
1058 if (i
!= 7 - OFFSET
)
1059 fmd_widgets
[i
].u
.checkbox
.text
= _(fmd_widgets
[i
].u
.checkbox
.text
);
1060 #endif /* !ENABLE_NLS */
1062 fmd_xlen
= max (FMDX
, (size_t) COLS
* 2 / 3);
1064 len
= str_term_width1 (fmd_widgets
[6 - OFFSET
].u
.checkbox
.text
)
1065 + str_term_width1 (fmd_widgets
[4 - OFFSET
].u
.checkbox
.text
) + 15;
1066 fmd_xlen
= max (fmd_xlen
, len
);
1068 len
= str_term_width1 (fmd_widgets
[5 - OFFSET
].u
.checkbox
.text
)
1069 + str_term_width1 (fmd_widgets
[3 - OFFSET
].u
.checkbox
.text
) + 15;
1070 fmd_xlen
= max (fmd_xlen
, len
);
1073 b2_len
= str_term_width1 (fmd_widgets
[2 - OFFSET
].u
.button
.text
) + 6 + gap
; /* OK */
1074 #ifdef ENABLE_BACKGROUND
1075 b1_len
= str_term_width1 (fmd_widgets
[1].u
.button
.text
) + 4 + gap
; /* Background */
1077 b0_len
= str_term_width1 (fmd_widgets
[0].u
.button
.text
) + 4; /* Cancel */
1078 len
= b0_len
+ b1_len
+ b2_len
;
1079 fmd_xlen
= min (max (fmd_xlen
, len
+ 6), (size_t) COLS
);
1085 flen
= str_term_width1 (format
);
1086 i
= fmd_xlen
- flen
- 4; /* FIXME */
1087 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc ((const char *) text
, i
));
1091 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, *(const int *) text
);
1092 fmd_xlen
= max (fmd_xlen
, (size_t) str_term_width1 (fmd_buf
) + 6);
1095 for (i
= sizeof (fmd_widgets
) / sizeof (fmd_widgets
[0]); i
> 0;)
1096 fmd_widgets
[--i
].x_divisions
= fmd_xlen
;
1098 i
= (fmd_xlen
- len
) / 2;
1100 fmd_widgets
[2 - OFFSET
].relative_x
= i
;
1102 #ifdef ENABLE_BACKGROUND
1103 /* Background button */
1104 fmd_widgets
[1].relative_x
= i
;
1108 fmd_widgets
[0].relative_x
= i
;
1110 #define chkbox_xpos(i) \
1111 fmd_widgets [i].relative_x = fmd_xlen - str_term_width1 (fmd_widgets [i].u.checkbox.text) - 6
1112 chkbox_xpos (3 - OFFSET
);
1113 chkbox_xpos (4 - OFFSET
);
1114 chkbox_xpos (9 - OFFSET
);
1118 fmd_widgets
[7 - OFFSET
].u
.input
.len
= fmd_widgets
[10 - OFFSET
].u
.input
.len
= fmd_xlen
- 6;
1120 /* unselect checkbox if target filesystem don't support attributes */
1121 ctx
->op_preserve
= filegui__check_attrs_on_fs (def_text
);
1123 /* filter out a possible password from def_text */
1127 vpath
= vfs_path_from_str_flags (def_text
, (only_one
) ? VPF_NO_CANON
: VPF_NONE
);
1128 tmp
= vfs_path_to_str_flags (vpath
, 0, VPF_STRIP_PASSWORD
);
1129 vfs_path_free (vpath
);
1131 if (source_easy_patterns
)
1132 def_text_secure
= strutils_glob_escape (tmp
);
1134 def_text_secure
= strutils_regex_escape (tmp
);
1138 fmd_widgets
[7 - OFFSET
].u
.input
.text
= def_text_secure
;
1140 ctx
->stable_symlinks
= FALSE
;
1147 QuickDialog Quick_input
= {
1148 fmd_xlen
, FMDY
, -1, -1, op_names
[operation
],
1149 "[Mask Copy/Rename]", fmd_widgets
, NULL
, NULL
, TRUE
1153 val
= quick_dialog_skip (&Quick_input
, 4);
1155 if (val
== B_CANCEL
)
1157 g_free (def_text_secure
);
1161 if (ctx
->follow_links
)
1162 ctx
->stat_func
= mc_stat
;
1164 ctx
->stat_func
= mc_lstat
;
1166 if (ctx
->op_preserve
)
1168 ctx
->preserve
= TRUE
;
1169 ctx
->umask_kill
= 0777777;
1170 ctx
->preserve_uidgid
= (geteuid () == 0);
1175 ctx
->preserve
= ctx
->preserve_uidgid
= FALSE
;
1178 ctx
->umask_kill
= i2
^ 0777777;
1181 if ((dest_dir
== NULL
) || (*dest_dir
== '\0'))
1183 g_free (def_text_secure
);
1184 g_free (source_mask
);
1188 ctx
->search_handle
= mc_search_new (source_mask
, -1);
1190 if (ctx
->search_handle
== NULL
)
1192 message (D_ERROR
, MSG_ERROR
, _("Invalid source pattern `%s'"), source_mask
);
1194 g_free (source_mask
);
1198 g_free (def_text_secure
);
1199 g_free (source_mask
);
1201 ctx
->search_handle
->is_case_sensitive
= TRUE
;
1202 if (source_easy_patterns
)
1203 ctx
->search_handle
->search_type
= MC_SEARCH_T_GLOB
;
1205 ctx
->search_handle
->search_type
= MC_SEARCH_T_REGEX
;
1208 dest_dir
= tilde_expand (tmp
);
1210 vpath
= vfs_path_from_str (dest_dir
);
1212 ctx
->dest_mask
= strrchr (dest_dir
, PATH_SEP
);
1213 if (ctx
->dest_mask
== NULL
)
1214 ctx
->dest_mask
= dest_dir
;
1217 orig_mask
= ctx
->dest_mask
;
1218 if (*ctx
->dest_mask
== '\0'
1219 || (!ctx
->dive_into_subdirs
&& !is_wildcarded (ctx
->dest_mask
)
1221 || (mc_stat (vpath
, &buf
) == 0 && S_ISDIR (buf
.st_mode
))))
1222 || (ctx
->dive_into_subdirs
1223 && ((!only_one
&& !is_wildcarded (ctx
->dest_mask
))
1224 || (only_one
&& mc_stat (vpath
, &buf
) == 0 && S_ISDIR (buf
.st_mode
)))))
1225 ctx
->dest_mask
= g_strdup ("\\0");
1228 ctx
->dest_mask
= g_strdup (ctx
->dest_mask
);
1234 dest_dir
= g_strdup ("./");
1236 vfs_path_free (vpath
);
1244 /* --------------------------------------------------------------------------------------------- */