5837ed89bcc3f56f8e8150cad2e3d8ac0c7bd0cf
[midnight-commander.git] / src / filemanager / filegui.c
blob5837ed89bcc3f56f8e8150cad2e3d8ac0c7bd0cf
1 /*
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.
17 Written by:
18 Janne Kukonlehto, 1994, 1995
19 Fred Leeflang, 1994, 1995
20 Miguel de Icaza, 1994, 1995, 1996
21 Jakub Jelinek, 1995, 1996
22 Norbert Warmuth, 1997
23 Pavel Machek, 1998
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
45 * operations.
48 /** \file filegui.c
49 * \brief Source: file management GUI for the text mode edition
52 /* {{{ Include files */
54 #include <config.h>
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)))
60 #define USE_STATVFS 1
61 #else
62 #define USE_STATVFS 0
63 #endif
65 #include <errno.h>
66 #include <ctype.h>
67 #include <stdio.h>
68 #include <string.h>
69 #include <sys/types.h>
70 #include <sys/stat.h>
72 #if USE_STATVFS
73 #include <sys/statvfs.h>
74 #elif HAVE_SYS_VFS_H
75 #include <sys/vfs.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>
87 #include <nfs/vfs.h>
88 #endif
89 #elif HAVE_OS_H /* BeOS */
90 #include <fs_info.h>
91 #endif
93 #if USE_STATVFS
94 #if ! STAT_STATVFS && STAT_STATVFS64
95 #define STRUCT_STATVFS struct statvfs64
96 #define STATFS statvfs64
97 #else
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__))
105 static int
106 statvfs_works (void)
108 return 1;
110 #else
111 #include <string.h> /* for strverscmp */
112 #include <sys/utsname.h>
113 #include <sys/statfs.h>
114 #define STAT_STATFS2_BSIZE 1
116 static int
117 statvfs_works (void)
119 static int statvfs_works_cache = -1;
120 struct utsname name;
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;
126 #endif
127 #endif
128 #else
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. */
135 static int
136 statfs (char const *filename, struct fs_info *buf)
138 dev_t device = dev_for_path (filename);
140 if (device < 0)
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);
146 return -1;
148 /* If successful, buf->dev will be == device. */
149 return fs_stat_dev (device, buf);
152 #define STRUCT_STATVFS struct fs_info
153 #else
154 #define STRUCT_STATVFS struct statfs
155 #endif
156 #endif
158 #if HAVE_STRUCT_STATVFS_F_BASETYPE
159 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
160 #else
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
165 #endif
166 #endif
168 #include <unistd.h>
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 */
187 #include "filegui.h"
189 /* }}} */
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 ****************************************************************/
205 /* *INDENT-OFF* */
206 typedef enum {
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;
215 /* *INDENT-ON* */
217 /* Used for button result values */
218 typedef enum
220 REPLACE_YES = B_USER,
221 REPLACE_NO,
222 REPLACE_APPEND,
223 REPLACE_ALWAYS,
224 REPLACE_UPDATE,
225 REPLACE_NEVER,
226 REPLACE_ABORT,
227 REPLACE_SIZE,
228 REPLACE_REGET
229 } replace_action_t;
231 /* This structure describes the UI and internal data required by a file
232 * operation context.
234 typedef struct
236 /* ETA and bps */
237 gboolean showing_eta;
238 gboolean showing_bps;
240 /* Dialog and widgets for the operation progress window */
241 WDialog *op_dlg;
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;
250 WLabel *time_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;
259 } FileOpContextUI;
261 /*** file scope variables ************************************************************************/
263 /*** file scope functions ************************************************************************/
264 /* --------------------------------------------------------------------------------------------- */
266 static gboolean
267 filegui__check_attrs_on_fs (const char *fs_path)
269 STRUCT_STATVFS stfs;
271 if (!setup_copymove_persistent_attr)
272 return FALSE;
274 #if USE_STATVFS && defined(STAT_STATVFS)
275 if (statvfs_works () && statvfs (fs_path, &stfs) != 0)
276 return TRUE;
277 #else
278 if (STATFS (fs_path, &stfs) != 0)
279 return TRUE;
280 #endif
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:
287 case NTFS_SB_MAGIC:
288 case PROC_SUPER_MAGIC:
289 case SMB_SUPER_MAGIC:
290 case NCP_SUPER_MAGIC:
291 case USBDEVICE_SUPER_MAGIC:
292 return FALSE;
293 default:
294 break;
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)
303 return FALSE;
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)
310 return FALSE;
311 #endif
313 return TRUE;
316 /* --------------------------------------------------------------------------------------------- */
318 static void
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 /* --------------------------------------------------------------------------------------------- */
330 static void
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)
336 *buffer = '\0';
337 return;
339 if (eta_secs <= 0.5)
340 eta_secs = 1;
341 file_frmt_time (_fmt_buff, eta_secs);
342 g_snprintf (buffer, BUF_TINY, _("ETA %s"), _fmt_buff);
345 /* --------------------------------------------------------------------------------------------- */
347 static void
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));
354 else if (bps > 1024)
356 g_snprintf (buffer, BUF_TINY, _("%.2f KB/s"), bps / 1024.0);
358 else if (bps > 1)
360 g_snprintf (buffer, BUF_TINY, _("%ld B/s"), bps);
362 else
363 *buffer = '\0';
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 :(
370 * alex
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)
388 /* dialog sizes */
389 const int rd_ylen = 1;
390 int rd_xlen = 60;
391 int y = 2;
392 unsigned long yes_id;
394 struct
396 const char *text;
397 int ypos, xpos;
398 widget_pos_flags_t pos_flags;
399 int value; /* 0 for labels */
400 } rd_widgets[] =
402 /* *INDENT-OFF* */
403 /* 0 */
404 { N_("Target file already exists!"), 3, 4, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
405 /* 1 */
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 },
411 /* 4 */
412 { N_("Overwrite this target?"), 9, 4, WPOS_KEEP_DEFAULT, 0 },
413 /* 5 */
414 { N_("&Yes"), 9, 28, WPOS_KEEP_DEFAULT, REPLACE_YES },
415 /* 6 */
416 { N_("&No"), 9, 37, WPOS_KEEP_DEFAULT, REPLACE_NO },
417 /* 7 */
418 { N_("A&ppend"), 9, 45, WPOS_KEEP_DEFAULT, REPLACE_APPEND },
419 /* 8 */
420 { N_("&Reget"), 10, 28, WPOS_KEEP_DEFAULT, REPLACE_REGET },
421 /* 9 */
422 { N_("Overwrite all targets?"), 11, 4, WPOS_KEEP_DEFAULT, 0 },
423 /* 10 */
424 { N_("A&ll"), 11, 28, WPOS_KEEP_DEFAULT, REPLACE_ALWAYS },
425 /* 11 */
426 { N_("&Update"), 11, 36, WPOS_KEEP_DEFAULT, REPLACE_UPDATE },
427 /* 12 */
428 { N_("Non&e"), 11, 47, WPOS_KEEP_DEFAULT, REPLACE_NEVER },
429 /* 13 */
430 { N_("If &size differs"), 12, 28, WPOS_KEEP_DEFAULT, REPLACE_SIZE },
431 /* 14 */
432 { N_("&Abort"), 14, 25, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, REPLACE_ABORT }
433 /* *INDENT-ON* */
436 const size_t num = G_N_ELEMENTS (rd_widgets);
437 int *widgets_len;
439 FileOpContextUI *ui = ctx->ui;
441 char buffer[BUF_SMALL];
442 const char *title;
443 vfs_path_t *stripped_vpath;
444 const char *stripped_name;
445 char *stripped_name_orig;
446 int result;
448 widgets_len = g_new0 (int, num);
450 if (mode == Foreground)
451 title = _("File exists");
452 else
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);
461 size_t i;
462 int l1, l2, l, row;
463 int stripped_name_len;
465 for (i = 0; i < num; i++)
467 #ifdef ENABLE_NLS
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 */
481 l = l2 = 0;
482 row = 0;
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;
489 l2 = max (l2, l);
490 l = 0;
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 */
503 l = l1;
504 row = 0;
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;
511 l = l1;
513 rd_widgets[i].xpos = l;
514 l += widgets_len[i] + 4;
518 /* FIXME - missing help node */
519 ui->replace_dlg =
520 create_dlg (TRUE, 0, 0, rd_ylen, rd_xlen, alarm_colors, NULL, NULL, "[Replace]", title,
521 DLG_CENTER);
523 /* prompt */
524 ADD_RD_LABEL (0, "", "", y++);
525 /* file name */
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,
532 y++);
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,
535 y++);
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;
574 #undef ADD_RD_LABEL
575 #undef ADD_RD_BUTTON
578 /* --------------------------------------------------------------------------------------------- */
580 static gboolean
581 is_wildcarded (char *p)
583 for (; *p; p++)
585 if (*p == '*')
586 return TRUE;
587 if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
588 return TRUE;
590 return FALSE;
593 /* --------------------------------------------------------------------------------------------- */
594 /*** public functions ****************************************************************************/
595 /* --------------------------------------------------------------------------------------------- */
597 FileProgressStatus
598 check_progress_buttons (FileOpContext * ctx)
600 int c;
601 Gpm_Event event;
602 FileOpContextUI *ui;
604 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
606 ui = ctx->ui;
608 event.x = -1; /* Don't show the GPM cursor */
609 c = tty_get_event (&event, FALSE, FALSE);
610 if (c == EV_NONE)
611 return FILE_CONT;
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)
620 case FILE_SKIP:
621 return FILE_SKIP;
622 case B_CANCEL:
623 case FILE_ABORT:
624 return FILE_ABORT;
625 default:
626 return FILE_CONT;
631 /* --------------------------------------------------------------------------------------------- */
632 /* {{{ File progress display routines */
634 void
635 file_op_context_create_ui (FileOpContext * ctx, gboolean with_eta, filegui_dialog_type_t dialog_type)
637 FileOpContextUI *ui;
639 const char *abort_button_label = N_("&Abort");
640 const char *skip_button_label = N_("&Skip");
641 int abort_button_width, skip_button_width, buttons_width;
642 int dlg_width, dlg_height;
643 int y = 2, x = 3;
644 Widget *skip_button;
646 g_return_if_fail (ctx != NULL);
647 g_return_if_fail (ctx->ui == NULL);
649 #ifdef ENABLE_NLS
650 abort_button_label = _(abort_button_label);
651 skip_button_label = _(skip_button_label);
652 #endif
654 abort_button_width = str_term_width1 (abort_button_label) + 3;
655 skip_button_width = str_term_width1 (skip_button_label) + 3;
656 buttons_width = abort_button_width + skip_button_width + 1;
658 dlg_width = max (58, buttons_width + 6);
659 dlg_height = 17; /* will be adjusted later */
661 ctx->dialog_type = dialog_type;
662 ctx->recursive_result = RECURSIVE_YES;
663 ctx->ui = g_new0 (FileOpContextUI, 1);
665 ui = ctx->ui;
666 ui->replace_result = REPLACE_YES;
667 ui->showing_eta = with_eta && file_op_compute_totals;
668 ui->showing_bps = with_eta;
670 ui->op_dlg =
671 create_dlg (TRUE, 0, 0, dlg_height, dlg_width, dialog_colors, NULL, NULL, NULL,
672 op_names[ctx->operation], DLG_CENTER);
674 ui->file_label[0] = label_new (y++, x, "");
675 add_widget (ui->op_dlg, ui->file_label[0]);
677 ui->file_string[0] = label_new (y++, x, "");
678 add_widget (ui->op_dlg, ui->file_string[0]);
680 ui->file_label[1] = label_new (y++, x, "");
681 add_widget (ui->op_dlg, ui->file_label[1]);
683 ui->file_string[1] = label_new (y++, x, "");
684 add_widget (ui->op_dlg, ui->file_string[1]);
686 ui->progress_file_gauge = gauge_new (y++, x + 3, 0, 100, 0);
687 if (!classic_progressbar && (current_panel == right_panel))
688 ui->progress_file_gauge->from_left_to_right = FALSE;
689 add_widget (ui->op_dlg, ui->progress_file_gauge);
691 ui->progress_file_label = label_new (y++, x, "");
692 add_widget (ui->op_dlg, ui->progress_file_label);
694 if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
696 add_widget (ui->op_dlg, hline_new (y, -1, -1));
698 ui->total_bytes_label = label_new (y++, x + 15, "");
699 add_widget (ui->op_dlg, ui->total_bytes_label);
701 if (file_op_compute_totals)
703 ui->progress_total_gauge = gauge_new (y++, x + 3, 0, 100, 0);
704 if (!classic_progressbar && (current_panel == right_panel))
705 ui->progress_total_gauge->from_left_to_right = FALSE;
706 add_widget (ui->op_dlg, ui->progress_total_gauge);
709 ui->total_files_processed_label = label_new (y++, x, "");
710 add_widget (ui->op_dlg, ui->total_files_processed_label);
712 ui->time_label = label_new (y++, x, "");
713 add_widget (ui->op_dlg, ui->time_label);
716 add_widget (ui->op_dlg, hline_new (y++, -1, -1));
718 x = (dlg_width - buttons_width) / 2;
719 skip_button = WIDGET (button_new (y, x, FILE_SKIP, NORMAL_BUTTON, skip_button_label, NULL));
720 add_widget (ui->op_dlg, skip_button);
722 x += skip_button_width + 1;
723 add_widget (ui->op_dlg, button_new (y, x, FILE_ABORT, NORMAL_BUTTON, abort_button_label, NULL));
725 /* adjust dialog height */
726 dlg_set_size (ui->op_dlg, y + 3, dlg_width);
728 dlg_select_widget (skip_button);
730 /* We will manage the dialog without any help, that's why
731 we have to call init_dlg */
732 init_dlg (ui->op_dlg);
735 /* --------------------------------------------------------------------------------------------- */
737 void
738 file_op_context_destroy_ui (FileOpContext * ctx)
740 g_return_if_fail (ctx != NULL);
742 if (ctx->ui != NULL)
744 FileOpContextUI *ui = (FileOpContextUI *) ctx->ui;
746 dlg_run_done (ui->op_dlg);
747 destroy_dlg (ui->op_dlg);
748 g_free (ui);
749 ctx->ui = NULL;
753 /* --------------------------------------------------------------------------------------------- */
755 show progressbar for file
758 void
759 file_progress_show (FileOpContext * ctx, off_t done, off_t total,
760 const char *stalled_msg, gboolean force_update)
762 FileOpContextUI *ui;
763 char buffer[BUF_TINY];
764 char buffer2[BUF_TINY];
765 char buffer3[BUF_TINY];
767 if (!verbose)
768 return;
770 g_return_if_fail (ctx != NULL);
771 g_return_if_fail (ctx->ui != NULL);
773 ui = ctx->ui;
775 if (total == 0)
777 gauge_show (ui->progress_file_gauge, 0);
778 return;
781 gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
782 gauge_show (ui->progress_file_gauge, 1);
784 if (!force_update)
785 return;
787 if (ui->showing_eta && ctx->eta_secs > 0.5)
789 file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
790 if (ctx->bps == 0)
791 g_snprintf (buffer, BUF_TINY, "%s %s", buffer2, stalled_msg);
792 else
794 file_bps_prepare_for_show (buffer3, ctx->bps);
795 g_snprintf (buffer, BUF_TINY, "%s (%s) %s", buffer2, buffer3, stalled_msg);
798 else
800 g_snprintf (buffer, BUF_TINY, "%s", stalled_msg);
803 label_set_text (ui->progress_file_label, buffer);
806 /* --------------------------------------------------------------------------------------------- */
808 void
809 file_progress_show_count (FileOpContext * ctx, size_t done, size_t total)
811 char buffer[BUF_TINY];
812 FileOpContextUI *ui;
814 g_return_if_fail (ctx != NULL);
815 g_return_if_fail (ctx->ui != NULL);
817 ui = ctx->ui;
818 if (file_op_compute_totals)
819 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu/%zu"), done, total);
820 else
821 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu"), done);
822 label_set_text (ui->total_files_processed_label, buffer);
825 /* --------------------------------------------------------------------------------------------- */
827 void
828 file_progress_show_total (FileOpTotalContext * tctx, FileOpContext * ctx, uintmax_t copied_bytes,
829 gboolean show_summary)
831 char buffer[BUF_TINY];
832 char buffer2[BUF_TINY];
833 char buffer3[BUF_TINY];
834 char buffer4[BUF_TINY];
835 struct timeval tv_current;
836 FileOpContextUI *ui;
838 g_return_if_fail (ctx != NULL);
839 g_return_if_fail (ctx->ui != NULL);
841 ui = ctx->ui;
843 if (file_op_compute_totals)
845 if (ctx->progress_bytes == 0)
846 gauge_show (ui->progress_total_gauge, 0);
847 else
849 gauge_set_value (ui->progress_total_gauge, 1024,
850 (int) (1024 * copied_bytes / ctx->progress_bytes));
851 gauge_show (ui->progress_total_gauge, 1);
855 if (!show_summary && tctx->bps == 0)
856 return;
858 gettimeofday (&tv_current, NULL);
859 file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec);
861 if (file_op_compute_totals)
863 file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
864 if (tctx->bps == 0)
865 g_snprintf (buffer, BUF_TINY, _("Time: %s %s"), buffer2, buffer3);
866 else
868 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
869 g_snprintf (buffer, BUF_TINY, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
872 else
874 if (tctx->bps == 0)
875 g_snprintf (buffer, BUF_TINY, _("Time: %s"), buffer2);
876 else
878 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
879 g_snprintf (buffer, BUF_TINY, _("Time: %s (%s)"), buffer2, buffer4);
883 label_set_text (ui->time_label, buffer);
885 size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
886 if (!file_op_compute_totals)
887 g_snprintf (buffer, BUF_TINY, _(" Total: %s "), buffer2);
888 else
890 size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
891 g_snprintf (buffer, BUF_TINY, _(" Total: %s/%s "), buffer2, buffer3);
894 label_set_text (ui->total_bytes_label, buffer);
897 /* }}} */
899 /* --------------------------------------------------------------------------------------------- */
901 void
902 file_progress_show_source (FileOpContext * ctx, const vfs_path_t * s_vpath)
904 FileOpContextUI *ui;
906 g_return_if_fail (ctx != NULL);
907 g_return_if_fail (ctx->ui != NULL);
909 ui = ctx->ui;
911 if (s_vpath != NULL)
913 char *s;
915 s = vfs_path_tokens_get (s_vpath, -1, 1);
916 label_set_text (ui->file_label[0], _("Source"));
917 label_set_text (ui->file_string[0], truncFileString (ui, s));
918 g_free (s);
920 else
922 label_set_text (ui->file_label[0], "");
923 label_set_text (ui->file_string[0], "");
927 /* --------------------------------------------------------------------------------------------- */
929 void
930 file_progress_show_target (FileOpContext * ctx, const vfs_path_t * s_vpath)
932 FileOpContextUI *ui;
934 g_return_if_fail (ctx != NULL);
935 g_return_if_fail (ctx->ui != NULL);
937 ui = ctx->ui;
939 if (s_vpath != NULL)
941 char *s;
943 s = vfs_path_to_str (s_vpath);
944 label_set_text (ui->file_label[1], _("Target"));
945 label_set_text (ui->file_string[1], truncFileStringSecure (ui, s));
946 g_free (s);
948 else
950 label_set_text (ui->file_label[1], "");
951 label_set_text (ui->file_string[1], "");
955 /* --------------------------------------------------------------------------------------------- */
957 void
958 file_progress_show_deleting (FileOpContext * ctx, const char *s)
960 FileOpContextUI *ui;
962 g_return_if_fail (ctx != NULL);
963 g_return_if_fail (ctx->ui != NULL);
965 ui = ctx->ui;
966 label_set_text (ui->file_label[0], _("Deleting"));
967 label_set_text (ui->file_label[0], truncFileStringSecure (ui, s));
970 /* --------------------------------------------------------------------------------------------- */
972 FileProgressStatus
973 file_progress_real_query_replace (FileOpContext * ctx,
974 enum OperationMode mode, const char *destname,
975 struct stat *_s_stat, struct stat *_d_stat)
977 FileOpContextUI *ui;
979 g_return_val_if_fail (ctx != NULL, FILE_CONT);
980 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
982 ui = ctx->ui;
984 if (ui->replace_result < REPLACE_ALWAYS)
986 ui->replace_filename = destname;
987 ui->s_stat = _s_stat;
988 ui->d_stat = _d_stat;
989 ui->replace_result = overwrite_query_dialog (ctx, mode);
992 switch (ui->replace_result)
994 case REPLACE_UPDATE:
995 do_refresh ();
996 if (_s_stat->st_mtime > _d_stat->st_mtime)
997 return FILE_CONT;
998 else
999 return FILE_SKIP;
1001 case REPLACE_SIZE:
1002 do_refresh ();
1003 if (_s_stat->st_size == _d_stat->st_size)
1004 return FILE_SKIP;
1005 else
1006 return FILE_CONT;
1008 case REPLACE_REGET:
1009 /* Careful: we fall through and set do_append */
1010 ctx->do_reget = _d_stat->st_size;
1012 case REPLACE_APPEND:
1013 ctx->do_append = TRUE;
1015 case REPLACE_YES:
1016 case REPLACE_ALWAYS:
1017 do_refresh ();
1018 return FILE_CONT;
1019 case REPLACE_NO:
1020 case REPLACE_NEVER:
1021 do_refresh ();
1022 return FILE_SKIP;
1023 case REPLACE_ABORT:
1024 default:
1025 return FILE_ABORT;
1029 /* --------------------------------------------------------------------------------------------- */
1031 char *
1032 file_mask_dialog (FileOpContext * ctx, FileOperation operation,
1033 gboolean only_one,
1034 const char *format, const void *text, const char *def_text, gboolean * do_bg)
1036 size_t fmd_xlen;
1037 vfs_path_t *vpath;
1038 int source_easy_patterns = easy_patterns;
1039 char fmd_buf[BUF_MEDIUM];
1040 char *dest_dir, *tmp;
1041 char *def_text_secure;
1043 g_return_val_if_fail (ctx != NULL, NULL);
1045 /* unselect checkbox if target filesystem don't support attributes */
1046 ctx->op_preserve = filegui__check_attrs_on_fs (def_text);
1047 ctx->stable_symlinks = FALSE;
1048 *do_bg = FALSE;
1050 /* filter out a possible password from def_text */
1051 vpath = vfs_path_from_str_flags (def_text, only_one ? VPF_NO_CANON : VPF_NONE);
1052 tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1053 vfs_path_free (vpath);
1055 if (source_easy_patterns)
1056 def_text_secure = strutils_glob_escape (tmp);
1057 else
1058 def_text_secure = strutils_regex_escape (tmp);
1059 g_free (tmp);
1061 if (only_one)
1063 int format_len, text_len;
1064 int max_len;
1066 format_len = str_term_width1 (format);
1067 text_len = str_term_width1 (text);
1068 max_len = COLS - 2 - 6;
1070 if (format_len + text_len <= max_len)
1072 fmd_xlen = format_len + text_len + 6;
1073 fmd_xlen = max (fmd_xlen, 68);
1075 else
1077 text = str_trunc ((const char *) text, max_len - format_len);
1078 fmd_xlen = max_len + 6;
1081 g_snprintf (fmd_buf, sizeof (fmd_buf), format, (const char *) text);
1083 else
1085 fmd_xlen = COLS * 2 / 3;
1086 fmd_xlen = max (fmd_xlen, 68);
1087 g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1091 char *source_mask, *orig_mask;
1092 int val;
1093 struct stat buf;
1095 quick_widget_t quick_widgets[] = {
1096 /* *INDENT-OFF* */
1097 QUICK_LABELED_INPUT (fmd_buf, input_label_above,
1098 easy_patterns ? "*" : "^(.*)$", 0, "input-def", &source_mask,
1099 NULL),
1100 QUICK_START_COLUMNS,
1101 QUICK_SEPARATOR (FALSE),
1102 QUICK_NEXT_COLUMN,
1103 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns, NULL),
1104 QUICK_STOP_COLUMNS,
1105 QUICK_LABELED_INPUT (N_("to:"), input_label_above,
1106 def_text_secure, 0, "input2", &dest_dir, NULL),
1107 QUICK_SEPARATOR (TRUE),
1108 QUICK_START_COLUMNS,
1109 QUICK_CHECKBOX (N_("Follow &links"), &ctx->follow_links, NULL),
1110 QUICK_CHECKBOX (N_("Preserve &attributes"), &ctx->op_preserve, NULL),
1111 QUICK_NEXT_COLUMN,
1112 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx->dive_into_subdirs, NULL),
1113 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx->stable_symlinks, NULL),
1114 QUICK_STOP_COLUMNS,
1115 QUICK_START_BUTTONS (TRUE, TRUE),
1116 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
1117 #ifdef ENABLE_BACKGROUND
1118 QUICK_BUTTON (N_("&Background"), B_USER, NULL, NULL),
1119 #endif /* ENABLE_BACKGROUND */
1120 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1121 QUICK_END
1122 /* *INDENT-ON* */
1125 quick_dialog_t qdlg = {
1126 -1, -1, fmd_xlen,
1127 op_names[operation], "[Mask Copy/Rename]",
1128 quick_widgets, NULL, NULL
1131 ask_file_mask:
1132 val = quick_dialog_skip (&qdlg, 4);
1134 if (val == B_CANCEL)
1136 g_free (def_text_secure);
1137 return NULL;
1140 if (ctx->follow_links)
1141 ctx->stat_func = mc_stat;
1142 else
1143 ctx->stat_func = mc_lstat;
1145 if (ctx->op_preserve)
1147 ctx->preserve = TRUE;
1148 ctx->umask_kill = 0777777;
1149 ctx->preserve_uidgid = (geteuid () == 0);
1151 else
1153 int i2;
1155 ctx->preserve = ctx->preserve_uidgid = FALSE;
1156 i2 = umask (0);
1157 umask (i2);
1158 ctx->umask_kill = i2 ^ 0777777;
1161 if ((dest_dir == NULL) || (*dest_dir == '\0'))
1163 g_free (def_text_secure);
1164 g_free (source_mask);
1165 return dest_dir;
1168 ctx->search_handle = mc_search_new (source_mask, -1);
1170 if (ctx->search_handle == NULL)
1172 message (D_ERROR, MSG_ERROR, _("Invalid source pattern `%s'"), source_mask);
1173 g_free (dest_dir);
1174 g_free (source_mask);
1175 goto ask_file_mask;
1178 g_free (def_text_secure);
1179 g_free (source_mask);
1181 ctx->search_handle->is_case_sensitive = TRUE;
1182 if (source_easy_patterns)
1183 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1184 else
1185 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1187 tmp = dest_dir;
1188 dest_dir = tilde_expand (tmp);
1189 g_free (tmp);
1190 vpath = vfs_path_from_str (dest_dir);
1192 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1193 if (ctx->dest_mask == NULL)
1194 ctx->dest_mask = dest_dir;
1195 else
1196 ctx->dest_mask++;
1197 orig_mask = ctx->dest_mask;
1198 if (*ctx->dest_mask == '\0'
1199 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1200 && (!only_one
1201 || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1202 || (ctx->dive_into_subdirs
1203 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1204 || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1205 ctx->dest_mask = g_strdup ("\\0");
1206 else
1208 ctx->dest_mask = g_strdup (ctx->dest_mask);
1209 *orig_mask = '\0';
1211 if (*dest_dir == '\0')
1213 g_free (dest_dir);
1214 dest_dir = g_strdup ("./");
1216 vfs_path_free (vpath);
1217 if (val == B_USER)
1218 *do_bg = TRUE;
1221 return dest_dir;
1224 /* --------------------------------------------------------------------------------------------- */