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(dlg, s) str_trunc (s, WIDGET (dlg)->cols - 10)
201 #define truncFileStringSecure(dlg, s) path_trunc (s, WIDGET (dlg)->cols - 10)
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 ************************************************************************/
266 FileProgressStatus action
;
268 button_flags_t flags
;
270 } progress_buttons
[] =
273 { NULL
, FILE_SKIP
, N_("&Skip"), NORMAL_BUTTON
, -1 },
274 { NULL
, FILE_SUSPEND
, N_("S&uspend"), NORMAL_BUTTON
, -1 },
275 { NULL
, FILE_SUSPEND
, N_("Con&tinue"), NORMAL_BUTTON
, -1 },
276 { NULL
, FILE_ABORT
, N_("&Abort"), NORMAL_BUTTON
, -1 }
280 /* --------------------------------------------------------------------------------------------- */
281 /*** file scope functions ************************************************************************/
282 /* --------------------------------------------------------------------------------------------- */
285 filegui__check_attrs_on_fs (const char *fs_path
)
289 if (!setup_copymove_persistent_attr
)
292 #if USE_STATVFS && defined(STAT_STATVFS)
293 if (statvfs_works () && statvfs (fs_path
, &stfs
) != 0)
296 if (STATFS (fs_path
, &stfs
) != 0)
300 #if (USE_STATVFS && defined(HAVE_STRUCT_STATVFS_F_TYPE)) || \
301 (!USE_STATVFS && defined(HAVE_STRUCT_STATFS_F_TYPE))
302 switch ((filegui_nonattrs_fs_t
) stfs
.f_type
)
304 case MSDOS_SUPER_MAGIC
:
306 case PROC_SUPER_MAGIC
:
307 case SMB_SUPER_MAGIC
:
308 case NCP_SUPER_MAGIC
:
309 case USBDEVICE_SUPER_MAGIC
:
314 #elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
315 if (strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "msdos") == 0
316 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "msdosfs") == 0
317 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "ntfs") == 0
318 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "procfs") == 0
319 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "smbfs") == 0
320 || strstr (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "fusefs") != NULL
)
322 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
323 if (strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "pcfs") == 0
324 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "ntfs") == 0
325 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "proc") == 0
326 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "smbfs") == 0
327 || strcmp (stfs
.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
, "fuse") == 0)
334 /* --------------------------------------------------------------------------------------------- */
337 file_frmt_time (char *buffer
, double eta_secs
)
339 int eta_hours
, eta_mins
, eta_s
;
340 eta_hours
= eta_secs
/ (60 * 60);
341 eta_mins
= (eta_secs
- (eta_hours
* 60 * 60)) / 60;
342 eta_s
= eta_secs
- (eta_hours
* 60 * 60 + eta_mins
* 60);
343 g_snprintf (buffer
, BUF_TINY
, _("%d:%02d.%02d"), eta_hours
, eta_mins
, eta_s
);
346 /* --------------------------------------------------------------------------------------------- */
349 file_eta_prepare_for_show (char *buffer
, double eta_secs
, gboolean always_show
)
351 char _fmt_buff
[BUF_TINY
];
352 if (eta_secs
<= 0.5 && !always_show
)
359 file_frmt_time (_fmt_buff
, eta_secs
);
360 g_snprintf (buffer
, BUF_TINY
, _("ETA %s"), _fmt_buff
);
363 /* --------------------------------------------------------------------------------------------- */
366 file_bps_prepare_for_show (char *buffer
, long bps
)
368 if (bps
> 1024 * 1024)
370 g_snprintf (buffer
, BUF_TINY
, _("%.2f MB/s"), bps
/ (1024 * 1024.0));
374 g_snprintf (buffer
, BUF_TINY
, _("%.2f KB/s"), bps
/ 1024.0);
378 g_snprintf (buffer
, BUF_TINY
, _("%ld B/s"), bps
);
384 /* --------------------------------------------------------------------------------------------- */
386 * FIXME: probably it is better to replace this with quick dialog machinery,
387 * but actually I'm not familiar with it and have not much time :(
390 static replace_action_t
391 overwrite_query_dialog (FileOpContext
* ctx
, enum OperationMode mode
)
393 #define ADD_RD_BUTTON(i, ypos) \
394 add_widget_autopos (ui->replace_dlg, \
395 button_new (ypos, rd_widgets [i].xpos, rd_widgets [i].value, \
396 NORMAL_BUTTON, rd_widgets [i].text, NULL), \
397 rd_widgets [i].pos_flags, ui->replace_dlg->current->data)
399 #define ADD_RD_LABEL(i, p1, p2, ypos) \
400 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2); \
401 add_widget_autopos (ui->replace_dlg, \
402 label_new (ypos, rd_widgets [i].xpos, buffer), \
403 rd_widgets [i].pos_flags, \
404 ui->replace_dlg->current != NULL ? ui->replace_dlg->current->data : NULL)
407 const int rd_ylen
= 1;
410 unsigned long yes_id
;
416 widget_pos_flags_t pos_flags
;
417 int value
; /* 0 for labels */
422 { N_("Target file already exists!"), 3, 4, WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
, 0 },
424 { "%s", 4, 4, WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
, 0 },
425 /* 2 */ /* cannot use PRIuMAX here; %llu is used instead */
426 { N_("New : %s, size %llu"), 6, 4, WPOS_KEEP_DEFAULT
, 0 },
427 /* 3 */ /* cannot use PRIuMAX here; %llu is used instead */
428 { N_("Existing: %s, size %llu"), 7, 4, WPOS_KEEP_DEFAULT
, 0 },
430 { N_("Overwrite this target?"), 9, 4, WPOS_KEEP_DEFAULT
, 0 },
432 { N_("&Yes"), 9, 28, WPOS_KEEP_DEFAULT
, REPLACE_YES
},
434 { N_("&No"), 9, 37, WPOS_KEEP_DEFAULT
, REPLACE_NO
},
436 { N_("A&ppend"), 9, 45, WPOS_KEEP_DEFAULT
, REPLACE_APPEND
},
438 { N_("&Reget"), 10, 28, WPOS_KEEP_DEFAULT
, REPLACE_REGET
},
440 { N_("Overwrite all targets?"), 11, 4, WPOS_KEEP_DEFAULT
, 0 },
442 { N_("A&ll"), 11, 28, WPOS_KEEP_DEFAULT
, REPLACE_ALWAYS
},
444 { N_("&Update"), 11, 36, WPOS_KEEP_DEFAULT
, REPLACE_UPDATE
},
446 { N_("Non&e"), 11, 47, WPOS_KEEP_DEFAULT
, REPLACE_NEVER
},
448 { N_("If &size differs"), 12, 28, WPOS_KEEP_DEFAULT
, REPLACE_SIZE
},
450 { N_("&Abort"), 14, 25, WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
, REPLACE_ABORT
}
454 const size_t num
= G_N_ELEMENTS (rd_widgets
);
457 FileOpContextUI
*ui
= ctx
->ui
;
459 char buffer
[BUF_SMALL
];
461 vfs_path_t
*stripped_vpath
;
462 const char *stripped_name
;
463 char *stripped_name_orig
;
466 widgets_len
= g_new0 (int, num
);
468 if (mode
== Foreground
)
469 title
= _("File exists");
471 title
= _("Background process: File exists");
473 stripped_vpath
= vfs_path_from_str (ui
->replace_filename
);
474 stripped_name
= stripped_name_orig
=
475 vfs_path_to_str_flags (stripped_vpath
, 0, VPF_STRIP_HOME
| VPF_STRIP_PASSWORD
);
476 vfs_path_free (stripped_vpath
);
481 int stripped_name_len
;
483 for (i
= 0; i
< num
; i
++)
486 if (i
!= 1) /* skip filename */
487 rd_widgets
[i
].text
= _(rd_widgets
[i
].text
);
488 #endif /* ENABLE_NLS */
489 widgets_len
[i
] = str_term_width1 (rd_widgets
[i
].text
);
493 * longest of "Overwrite..." labels
494 * (assume "Target date..." are short enough)
496 l1
= max (widgets_len
[9], widgets_len
[4]);
498 /* longest of button rows */
501 for (i
= 1; i
< num
- 1; i
++)
502 if (rd_widgets
[i
].value
!= 0)
504 if (row
!= rd_widgets
[i
].ypos
)
506 row
= rd_widgets
[i
].ypos
;
510 l
+= widgets_len
[i
] + 4;
513 l2
= max (l2
, l
); /* last row */
514 rd_xlen
= max (rd_xlen
, l1
+ l2
+ 8);
515 /* rd_xlen = max (rd_xlen, str_term_width1 (title) + 2); */
516 stripped_name_len
= str_term_width1 (stripped_name
);
517 rd_xlen
= max (rd_xlen
, min (COLS
, stripped_name_len
+ 8));
519 /* Now place widgets */
520 l1
+= 5; /* start of first button in the row */
523 for (i
= 2; i
< num
- 1; i
++)
524 if (rd_widgets
[i
].value
!= 0)
526 if (row
!= rd_widgets
[i
].ypos
)
528 row
= rd_widgets
[i
].ypos
;
531 rd_widgets
[i
].xpos
= l
;
532 l
+= widgets_len
[i
] + 4;
536 /* FIXME - missing help node */
538 create_dlg (TRUE
, 0, 0, rd_ylen
, rd_xlen
, alarm_colors
, NULL
, NULL
, "[Replace]", title
,
542 ADD_RD_LABEL (0, "", "", y
++);
544 ADD_RD_LABEL (1, str_trunc (stripped_name
, rd_xlen
- 8), "", y
++);
546 add_widget (ui
->replace_dlg
, hline_new (y
++, -1, -1));
548 /* source date and size */
549 ADD_RD_LABEL (2, file_date (ui
->s_stat
->st_mtime
), (unsigned long long) ui
->s_stat
->st_size
,
551 /* destination date and size */
552 ADD_RD_LABEL (3, file_date (ui
->d_stat
->st_mtime
), (unsigned long long) ui
->d_stat
->st_size
,
555 add_widget (ui
->replace_dlg
, hline_new (y
++, -1, -1));
557 ADD_RD_LABEL (4, 0, 0, y
); /* Overwrite this target? */
558 yes_id
= ADD_RD_BUTTON (5, y
); /* Yes */
559 ADD_RD_BUTTON (6, y
); /* No */
561 /* "this target..." widgets */
562 if (!S_ISDIR (ui
->d_stat
->st_mode
))
564 ADD_RD_BUTTON (7, y
++); /* Append */
566 if ((ctx
->operation
== OP_COPY
) && (ui
->d_stat
->st_size
!= 0)
567 && (ui
->s_stat
->st_size
> ui
->d_stat
->st_size
))
568 ADD_RD_BUTTON (8, y
++); /* Reget */
571 add_widget (ui
->replace_dlg
, hline_new (y
++, -1, -1));
573 ADD_RD_LABEL (9, 0, 0, y
); /* Overwrite all targets? */
574 ADD_RD_BUTTON (10, y
); /* All" */
575 ADD_RD_BUTTON (11, y
); /* Update */
576 ADD_RD_BUTTON (12, y
++); /* None */
577 ADD_RD_BUTTON (13, y
++); /* If size differs */
579 add_widget (ui
->replace_dlg
, hline_new (y
++, -1, -1));
581 ADD_RD_BUTTON (14, y
); /* Abort */
583 dlg_set_size (ui
->replace_dlg
, y
+ 3, rd_xlen
);
584 dlg_select_by_id (ui
->replace_dlg
, yes_id
);
585 result
= run_dlg (ui
->replace_dlg
);
586 destroy_dlg (ui
->replace_dlg
);
588 g_free (widgets_len
);
589 g_free (stripped_name_orig
);
591 return (result
== B_CANCEL
) ? REPLACE_ABORT
: (replace_action_t
) result
;
596 /* --------------------------------------------------------------------------------------------- */
599 is_wildcarded (char *p
)
605 if (*p
== '\\' && p
[1] >= '1' && p
[1] <= '9')
611 /* --------------------------------------------------------------------------------------------- */
614 place_progress_buttons (WDialog
* h
, gboolean suspended
)
616 const size_t i
= suspended
? 2 : 1;
617 Widget
*w
= WIDGET (h
);
620 buttons_width
= 2 + progress_buttons
[0].len
+ progress_buttons
[3].len
;
621 buttons_width
+= progress_buttons
[i
].len
;
622 button_set_text (BUTTON (progress_buttons
[i
].w
), progress_buttons
[i
].text
);
624 progress_buttons
[0].w
->x
= w
->x
+ (w
->cols
- buttons_width
) / 2;
625 progress_buttons
[i
].w
->x
= progress_buttons
[0].w
->x
+ progress_buttons
[0].len
+ 1;
626 progress_buttons
[3].w
->x
= progress_buttons
[i
].w
->x
+ progress_buttons
[i
].len
+ 1;
629 /* --------------------------------------------------------------------------------------------- */
632 progress_start_stop (WButton
* button
, int action
)
637 /* don't close dialog in any case */
641 /* --------------------------------------------------------------------------------------------- */
642 /*** public functions ****************************************************************************/
643 /* --------------------------------------------------------------------------------------------- */
646 check_progress_buttons (FileOpContext
* ctx
)
652 g_return_val_if_fail (ctx
->ui
!= NULL
, FILE_CONT
);
657 event
.x
= -1; /* Don't show the GPM cursor */
658 c
= tty_get_event (&event
, FALSE
, ctx
->suspended
);
662 /* Reinitialize to avoid old values after events other than selecting a button */
663 ui
->op_dlg
->ret_value
= FILE_CONT
;
665 dlg_process_event (ui
->op_dlg
, c
, &event
);
666 switch (ui
->op_dlg
->ret_value
)
669 ctx
->suspended
= FALSE
;
673 ctx
->suspended
= FALSE
;
676 ctx
->suspended
= !ctx
->suspended
;
677 place_progress_buttons (ui
->op_dlg
, ctx
->suspended
);
678 dlg_redraw (ui
->op_dlg
);
687 /* --------------------------------------------------------------------------------------------- */
688 /* {{{ File progress display routines */
691 file_op_context_create_ui (FileOpContext
* ctx
, gboolean with_eta
,
692 filegui_dialog_type_t dialog_type
)
696 int dlg_width
= 58, dlg_height
= 17;
699 g_return_if_fail (ctx
!= NULL
);
700 g_return_if_fail (ctx
->ui
== NULL
);
703 if (progress_buttons
[0].len
== -1)
707 for (i
= 0; i
< G_N_ELEMENTS (progress_buttons
); i
++)
708 progress_buttons
[i
].text
= _(progress_buttons
[i
].text
);
713 ctx
->dialog_type
= dialog_type
;
714 ctx
->recursive_result
= RECURSIVE_YES
;
715 ctx
->ui
= g_new0 (FileOpContextUI
, 1);
718 ui
->replace_result
= REPLACE_YES
;
719 ui
->showing_eta
= with_eta
&& file_op_compute_totals
;
720 ui
->showing_bps
= with_eta
;
723 create_dlg (TRUE
, 0, 0, dlg_height
, dlg_width
, dialog_colors
, NULL
, NULL
, NULL
,
724 op_names
[ctx
->operation
], DLG_CENTER
);
726 ui
->file_label
[0] = label_new (y
++, x
, "");
727 add_widget (ui
->op_dlg
, ui
->file_label
[0]);
729 ui
->file_string
[0] = label_new (y
++, x
, "");
730 add_widget (ui
->op_dlg
, ui
->file_string
[0]);
732 ui
->file_label
[1] = label_new (y
++, x
, "");
733 add_widget (ui
->op_dlg
, ui
->file_label
[1]);
735 ui
->file_string
[1] = label_new (y
++, x
, "");
736 add_widget (ui
->op_dlg
, ui
->file_string
[1]);
738 ui
->progress_file_gauge
= gauge_new (y
++, x
+ 3, dlg_width
- (x
+ 3) * 2, FALSE
, 100, 0);
739 if (!classic_progressbar
&& (current_panel
== right_panel
))
740 ui
->progress_file_gauge
->from_left_to_right
= FALSE
;
741 add_widget_autopos (ui
->op_dlg
, ui
->progress_file_gauge
, WPOS_KEEP_TOP
| WPOS_KEEP_HORZ
, NULL
);
743 ui
->progress_file_label
= label_new (y
++, x
, "");
744 add_widget (ui
->op_dlg
, ui
->progress_file_label
);
746 if (verbose
&& dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
748 add_widget (ui
->op_dlg
, hline_new (y
, -1, -1));
750 ui
->total_bytes_label
= label_new (y
++, x
+ 15, "");
751 add_widget (ui
->op_dlg
, ui
->total_bytes_label
);
753 if (file_op_compute_totals
)
755 ui
->progress_total_gauge
=
756 gauge_new (y
++, x
+ 3, dlg_width
- (x
+ 3) * 2, FALSE
, 100, 0);
757 if (!classic_progressbar
&& (current_panel
== right_panel
))
758 ui
->progress_total_gauge
->from_left_to_right
= FALSE
;
759 add_widget_autopos (ui
->op_dlg
, ui
->progress_total_gauge
,
760 WPOS_KEEP_TOP
| WPOS_KEEP_HORZ
, NULL
);
763 ui
->total_files_processed_label
= label_new (y
++, x
, "");
764 add_widget (ui
->op_dlg
, ui
->total_files_processed_label
);
766 ui
->time_label
= label_new (y
++, x
, "");
767 add_widget (ui
->op_dlg
, ui
->time_label
);
770 add_widget (ui
->op_dlg
, hline_new (y
++, -1, -1));
772 progress_buttons
[0].w
= WIDGET (button_new (y
, 0, progress_buttons
[0].action
,
773 progress_buttons
[0].flags
, progress_buttons
[0].text
,
775 if (progress_buttons
[0].len
== -1)
776 progress_buttons
[0].len
= button_get_len (BUTTON (progress_buttons
[0].w
));
778 progress_buttons
[1].w
= WIDGET (button_new (y
, 0, progress_buttons
[1].action
,
779 progress_buttons
[1].flags
, progress_buttons
[1].text
,
780 progress_start_stop
));
781 if (progress_buttons
[1].len
== -1)
782 progress_buttons
[1].len
= button_get_len (BUTTON (progress_buttons
[1].w
));
784 if (progress_buttons
[2].len
== -1)
786 /* create and destroy button to get it length */
787 progress_buttons
[2].w
= WIDGET (button_new (y
, 0, progress_buttons
[2].action
,
788 progress_buttons
[2].flags
,
789 progress_buttons
[2].text
, progress_start_stop
));
790 progress_buttons
[2].len
= button_get_len (BUTTON (progress_buttons
[2].w
));
791 send_message (progress_buttons
[2].w
, NULL
, MSG_DESTROY
, 0, NULL
);
792 g_free (progress_buttons
[2].w
);
794 progress_buttons
[2].w
= progress_buttons
[1].w
;
796 progress_buttons
[3].w
= WIDGET (button_new (y
, 0, progress_buttons
[3].action
,
797 progress_buttons
[3].flags
, progress_buttons
[3].text
,
799 if (progress_buttons
[3].len
== -1)
800 progress_buttons
[3].len
= button_get_len (BUTTON (progress_buttons
[3].w
));
802 add_widget (ui
->op_dlg
, progress_buttons
[0].w
);
803 add_widget (ui
->op_dlg
, progress_buttons
[1].w
);
804 add_widget (ui
->op_dlg
, progress_buttons
[3].w
);
807 progress_buttons
[0].len
+ max (progress_buttons
[1].len
, progress_buttons
[2].len
) +
808 progress_buttons
[3].len
;
810 /* adjust dialog sizes */
811 dlg_set_size (ui
->op_dlg
, y
+ 3, max (COLS
* 2 / 3, buttons_width
+ 6));
813 place_progress_buttons (ui
->op_dlg
, FALSE
);
815 dlg_select_widget (progress_buttons
[0].w
);
817 /* We will manage the dialog without any help, that's why
818 we have to call init_dlg */
819 init_dlg (ui
->op_dlg
);
822 /* --------------------------------------------------------------------------------------------- */
825 file_op_context_destroy_ui (FileOpContext
* ctx
)
827 g_return_if_fail (ctx
!= NULL
);
831 FileOpContextUI
*ui
= (FileOpContextUI
*) ctx
->ui
;
833 dlg_run_done (ui
->op_dlg
);
834 destroy_dlg (ui
->op_dlg
);
840 /* --------------------------------------------------------------------------------------------- */
842 show progressbar for file
846 file_progress_show (FileOpContext
* ctx
, off_t done
, off_t total
,
847 const char *stalled_msg
, gboolean force_update
)
850 char buffer
[BUF_TINY
];
851 char buffer2
[BUF_TINY
];
852 char buffer3
[BUF_TINY
];
857 g_return_if_fail (ctx
!= NULL
);
858 g_return_if_fail (ctx
->ui
!= NULL
);
864 gauge_show (ui
->progress_file_gauge
, 0);
868 gauge_set_value (ui
->progress_file_gauge
, 1024, (int) (1024 * done
/ total
));
869 gauge_show (ui
->progress_file_gauge
, 1);
874 if (ui
->showing_eta
&& ctx
->eta_secs
> 0.5)
876 file_eta_prepare_for_show (buffer2
, ctx
->eta_secs
, FALSE
);
878 g_snprintf (buffer
, BUF_TINY
, "%s %s", buffer2
, stalled_msg
);
881 file_bps_prepare_for_show (buffer3
, ctx
->bps
);
882 g_snprintf (buffer
, BUF_TINY
, "%s (%s) %s", buffer2
, buffer3
, stalled_msg
);
887 g_snprintf (buffer
, BUF_TINY
, "%s", stalled_msg
);
890 label_set_text (ui
->progress_file_label
, buffer
);
893 /* --------------------------------------------------------------------------------------------- */
896 file_progress_show_count (FileOpContext
* ctx
, size_t done
, size_t total
)
898 char buffer
[BUF_TINY
];
901 g_return_if_fail (ctx
!= NULL
);
902 g_return_if_fail (ctx
->ui
!= NULL
);
905 if (file_op_compute_totals
)
906 g_snprintf (buffer
, BUF_TINY
, _("Files processed: %zu/%zu"), done
, total
);
908 g_snprintf (buffer
, BUF_TINY
, _("Files processed: %zu"), done
);
909 label_set_text (ui
->total_files_processed_label
, buffer
);
912 /* --------------------------------------------------------------------------------------------- */
915 file_progress_show_total (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, uintmax_t copied_bytes
,
916 gboolean show_summary
)
918 char buffer
[BUF_TINY
];
919 char buffer2
[BUF_TINY
];
920 char buffer3
[BUF_TINY
];
921 char buffer4
[BUF_TINY
];
922 struct timeval tv_current
;
925 g_return_if_fail (ctx
!= NULL
);
926 g_return_if_fail (ctx
->ui
!= NULL
);
930 if (file_op_compute_totals
)
932 if (ctx
->progress_bytes
== 0)
933 gauge_show (ui
->progress_total_gauge
, 0);
936 gauge_set_value (ui
->progress_total_gauge
, 1024,
937 (int) (1024 * copied_bytes
/ ctx
->progress_bytes
));
938 gauge_show (ui
->progress_total_gauge
, 1);
942 if (!show_summary
&& tctx
->bps
== 0)
945 gettimeofday (&tv_current
, NULL
);
946 file_frmt_time (buffer2
, tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
);
948 if (file_op_compute_totals
)
950 file_eta_prepare_for_show (buffer3
, tctx
->eta_secs
, TRUE
);
952 g_snprintf (buffer
, BUF_TINY
, _("Time: %s %s"), buffer2
, buffer3
);
955 file_bps_prepare_for_show (buffer4
, (long) tctx
->bps
);
956 g_snprintf (buffer
, BUF_TINY
, _("Time: %s %s (%s)"), buffer2
, buffer3
, buffer4
);
962 g_snprintf (buffer
, BUF_TINY
, _("Time: %s"), buffer2
);
965 file_bps_prepare_for_show (buffer4
, (long) tctx
->bps
);
966 g_snprintf (buffer
, BUF_TINY
, _("Time: %s (%s)"), buffer2
, buffer4
);
970 label_set_text (ui
->time_label
, buffer
);
972 size_trunc_len (buffer2
, 5, tctx
->copied_bytes
, 0, panels_options
.kilobyte_si
);
973 if (!file_op_compute_totals
)
974 g_snprintf (buffer
, BUF_TINY
, _(" Total: %s "), buffer2
);
977 size_trunc_len (buffer3
, 5, ctx
->progress_bytes
, 0, panels_options
.kilobyte_si
);
978 g_snprintf (buffer
, BUF_TINY
, _(" Total: %s/%s "), buffer2
, buffer3
);
981 label_set_text (ui
->total_bytes_label
, buffer
);
986 /* --------------------------------------------------------------------------------------------- */
989 file_progress_show_source (FileOpContext
* ctx
, const vfs_path_t
* s_vpath
)
993 g_return_if_fail (ctx
!= NULL
);
994 g_return_if_fail (ctx
->ui
!= NULL
);
1002 s
= vfs_path_tokens_get (s_vpath
, -1, 1);
1003 label_set_text (ui
->file_label
[0], _("Source"));
1004 label_set_text (ui
->file_string
[0], truncFileString (ui
->op_dlg
, s
));
1009 label_set_text (ui
->file_label
[0], "");
1010 label_set_text (ui
->file_string
[0], "");
1014 /* --------------------------------------------------------------------------------------------- */
1017 file_progress_show_target (FileOpContext
* ctx
, const vfs_path_t
* s_vpath
)
1019 FileOpContextUI
*ui
;
1021 g_return_if_fail (ctx
!= NULL
);
1022 g_return_if_fail (ctx
->ui
!= NULL
);
1026 if (s_vpath
!= NULL
)
1030 s
= vfs_path_to_str (s_vpath
);
1031 label_set_text (ui
->file_label
[1], _("Target"));
1032 label_set_text (ui
->file_string
[1], truncFileStringSecure (ui
->op_dlg
, s
));
1037 label_set_text (ui
->file_label
[1], "");
1038 label_set_text (ui
->file_string
[1], "");
1042 /* --------------------------------------------------------------------------------------------- */
1045 file_progress_show_deleting (FileOpContext
* ctx
, const char *s
)
1047 FileOpContextUI
*ui
;
1049 g_return_if_fail (ctx
!= NULL
);
1050 g_return_if_fail (ctx
->ui
!= NULL
);
1053 label_set_text (ui
->file_label
[0], _("Deleting"));
1054 label_set_text (ui
->file_label
[0], truncFileStringSecure (ui
->op_dlg
, s
));
1057 /* --------------------------------------------------------------------------------------------- */
1060 file_progress_real_query_replace (FileOpContext
* ctx
,
1061 enum OperationMode mode
, const char *destname
,
1062 struct stat
*_s_stat
, struct stat
*_d_stat
)
1064 FileOpContextUI
*ui
;
1066 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
1067 g_return_val_if_fail (ctx
->ui
!= NULL
, FILE_CONT
);
1071 if (ui
->replace_result
< REPLACE_ALWAYS
)
1073 ui
->replace_filename
= destname
;
1074 ui
->s_stat
= _s_stat
;
1075 ui
->d_stat
= _d_stat
;
1076 ui
->replace_result
= overwrite_query_dialog (ctx
, mode
);
1079 switch (ui
->replace_result
)
1081 case REPLACE_UPDATE
:
1083 if (_s_stat
->st_mtime
> _d_stat
->st_mtime
)
1090 if (_s_stat
->st_size
== _d_stat
->st_size
)
1096 /* Careful: we fall through and set do_append */
1097 ctx
->do_reget
= _d_stat
->st_size
;
1099 case REPLACE_APPEND
:
1100 ctx
->do_append
= TRUE
;
1103 case REPLACE_ALWAYS
:
1116 /* --------------------------------------------------------------------------------------------- */
1119 file_mask_dialog (FileOpContext
* ctx
, FileOperation operation
,
1121 const char *format
, const void *text
, const char *def_text
, gboolean
* do_bg
)
1125 int source_easy_patterns
= easy_patterns
;
1126 char fmd_buf
[BUF_MEDIUM
];
1127 char *dest_dir
, *tmp
;
1128 char *def_text_secure
;
1130 g_return_val_if_fail (ctx
!= NULL
, NULL
);
1132 /* unselect checkbox if target filesystem don't support attributes */
1133 ctx
->op_preserve
= filegui__check_attrs_on_fs (def_text
);
1134 ctx
->stable_symlinks
= FALSE
;
1137 /* filter out a possible password from def_text */
1138 vpath
= vfs_path_from_str_flags (def_text
, only_one
? VPF_NO_CANON
: VPF_NONE
);
1139 tmp
= vfs_path_to_str_flags (vpath
, 0, VPF_STRIP_PASSWORD
);
1140 vfs_path_free (vpath
);
1142 if (source_easy_patterns
)
1143 def_text_secure
= strutils_glob_escape (tmp
);
1145 def_text_secure
= strutils_regex_escape (tmp
);
1150 int format_len
, text_len
;
1153 format_len
= str_term_width1 (format
);
1154 text_len
= str_term_width1 (text
);
1155 max_len
= COLS
- 2 - 6;
1157 if (format_len
+ text_len
<= max_len
)
1159 fmd_xlen
= format_len
+ text_len
+ 6;
1160 fmd_xlen
= max (fmd_xlen
, 68);
1164 text
= str_trunc ((const char *) text
, max_len
- format_len
);
1165 fmd_xlen
= max_len
+ 6;
1168 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, (const char *) text
);
1172 fmd_xlen
= COLS
* 2 / 3;
1173 fmd_xlen
= max (fmd_xlen
, 68);
1174 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, *(const int *) text
);
1178 char *source_mask
, *orig_mask
;
1182 quick_widget_t quick_widgets
[] = {
1184 QUICK_LABELED_INPUT (fmd_buf
, input_label_above
,
1185 easy_patterns
? "*" : "^(.*)$", 0, "input-def", &source_mask
,
1187 QUICK_START_COLUMNS
,
1188 QUICK_SEPARATOR (FALSE
),
1190 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns
, NULL
),
1192 QUICK_LABELED_INPUT (N_("to:"), input_label_above
,
1193 def_text_secure
, 0, "input2", &dest_dir
, NULL
),
1194 QUICK_SEPARATOR (TRUE
),
1195 QUICK_START_COLUMNS
,
1196 QUICK_CHECKBOX (N_("Follow &links"), &ctx
->follow_links
, NULL
),
1197 QUICK_CHECKBOX (N_("Preserve &attributes"), &ctx
->op_preserve
, NULL
),
1199 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx
->dive_into_subdirs
, NULL
),
1200 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx
->stable_symlinks
, NULL
),
1202 QUICK_START_BUTTONS (TRUE
, TRUE
),
1203 QUICK_BUTTON (N_("&OK"), B_ENTER
, NULL
, NULL
),
1204 #ifdef ENABLE_BACKGROUND
1205 QUICK_BUTTON (N_("&Background"), B_USER
, NULL
, NULL
),
1206 #endif /* ENABLE_BACKGROUND */
1207 QUICK_BUTTON (N_("&Cancel"), B_CANCEL
, NULL
, NULL
),
1212 quick_dialog_t qdlg
= {
1214 op_names
[operation
], "[Mask Copy/Rename]",
1215 quick_widgets
, NULL
, NULL
1219 val
= quick_dialog_skip (&qdlg
, 4);
1221 if (val
== B_CANCEL
)
1223 g_free (def_text_secure
);
1227 if (ctx
->follow_links
)
1228 ctx
->stat_func
= mc_stat
;
1230 ctx
->stat_func
= mc_lstat
;
1232 if (ctx
->op_preserve
)
1234 ctx
->preserve
= TRUE
;
1235 ctx
->umask_kill
= 0777777;
1236 ctx
->preserve_uidgid
= (geteuid () == 0);
1242 ctx
->preserve
= ctx
->preserve_uidgid
= FALSE
;
1245 ctx
->umask_kill
= i2
^ 0777777;
1248 if ((dest_dir
== NULL
) || (*dest_dir
== '\0'))
1250 g_free (def_text_secure
);
1251 g_free (source_mask
);
1255 ctx
->search_handle
= mc_search_new (source_mask
, -1);
1257 if (ctx
->search_handle
== NULL
)
1259 message (D_ERROR
, MSG_ERROR
, _("Invalid source pattern `%s'"), source_mask
);
1261 g_free (source_mask
);
1265 g_free (def_text_secure
);
1266 g_free (source_mask
);
1268 ctx
->search_handle
->is_case_sensitive
= TRUE
;
1269 if (source_easy_patterns
)
1270 ctx
->search_handle
->search_type
= MC_SEARCH_T_GLOB
;
1272 ctx
->search_handle
->search_type
= MC_SEARCH_T_REGEX
;
1275 dest_dir
= tilde_expand (tmp
);
1277 vpath
= vfs_path_from_str (dest_dir
);
1279 ctx
->dest_mask
= strrchr (dest_dir
, PATH_SEP
);
1280 if (ctx
->dest_mask
== NULL
)
1281 ctx
->dest_mask
= dest_dir
;
1284 orig_mask
= ctx
->dest_mask
;
1285 if (*ctx
->dest_mask
== '\0'
1286 || (!ctx
->dive_into_subdirs
&& !is_wildcarded (ctx
->dest_mask
)
1288 || (mc_stat (vpath
, &buf
) == 0 && S_ISDIR (buf
.st_mode
))))
1289 || (ctx
->dive_into_subdirs
1290 && ((!only_one
&& !is_wildcarded (ctx
->dest_mask
))
1291 || (only_one
&& mc_stat (vpath
, &buf
) == 0 && S_ISDIR (buf
.st_mode
)))))
1292 ctx
->dest_mask
= g_strdup ("\\0");
1295 ctx
->dest_mask
= g_strdup (ctx
->dest_mask
);
1298 if (*dest_dir
== '\0')
1301 dest_dir
= g_strdup ("./");
1303 vfs_path_free (vpath
);
1311 /* --------------------------------------------------------------------------------------------- */