Ticket #2825: obtain FS name from stat info: sync with coreutils.
[midnight-commander.git] / src / filemanager / filegui.c
blob198f7d780269caa7975b7a713b99f7b21d41cfb2
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 # define STRUCT_STATVFS struct statvfs
95 # if ! STAT_STATVFS && STAT_STATVFS64
96 # define STATFS statvfs64
97 # else
98 # define STATFS statvfs
99 # endif
100 #else
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. */
106 static int
107 statfs (char const *filename, struct fs_info *buf)
109 dev_t device = dev_for_path (filename);
111 if (device < 0)
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
117 : device == B_FILE_ERROR ? EIO
118 : 0);
119 return -1;
121 /* If successful, buf->dev will be == device. */
122 return fs_stat_dev (device, buf);
125 # define STRUCT_STATVFS struct fs_info
126 # else
127 # define STRUCT_STATVFS struct statfs
128 # endif
129 #endif
131 #if HAVE_STRUCT_STATVFS_F_BASETYPE
132 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
133 #else
134 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
135 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
136 # elif HAVE_OS_H /* BeOS */
137 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
138 # endif
139 #endif
141 #include <unistd.h>
143 #include "lib/global.h"
145 #include "lib/tty/key.h" /* tty_get_event */
146 #include "lib/mcconfig.h"
147 #include "lib/search.h"
148 #include "lib/vfs/vfs.h"
149 #include "lib/strescape.h"
150 #include "lib/strutil.h"
151 #include "lib/timefmt.h" /* file_date() */
152 #include "lib/util.h"
153 #include "lib/widget.h"
155 #include "src/setup.h" /* verbose */
157 #include "midnight.h"
158 #include "fileopctx.h" /* FILE_CONT */
160 #include "filegui.h"
162 /* }}} */
164 /*** global variables ****************************************************************************/
166 int classic_progressbar = 1;
168 /*** file scope macro definitions ****************************************************************/
170 /* Hack: the vfs code should not rely on this */
171 #define WITH_FULL_PATHS 1
173 #define truncFileString(ui, s) str_trunc (s, 52)
174 #define truncFileStringSecure(ui, s) path_trunc (s, 52)
176 /*** file scope type declarations ****************************************************************/
178 /* *INDENT-OFF* */
179 typedef enum {
180 MSDOS_SUPER_MAGIC = 0x4d44,
181 NTFS_SB_MAGIC = 0x5346544e,
182 FUSE_MAGIC = 0x65735546,
183 PROC_SUPER_MAGIC = 0x9fa0,
184 SMB_SUPER_MAGIC = 0x517B,
185 NCP_SUPER_MAGIC = 0x564c,
186 USBDEVICE_SUPER_MAGIC = 0x9fa2
187 } filegui_nonattrs_fs_t;
188 /* *INDENT-ON* */
190 /* Used for button result values */
191 typedef enum
193 REPLACE_YES = B_USER,
194 REPLACE_NO,
195 REPLACE_APPEND,
196 REPLACE_ALWAYS,
197 REPLACE_UPDATE,
198 REPLACE_NEVER,
199 REPLACE_ABORT,
200 REPLACE_SIZE,
201 REPLACE_REGET
202 } replace_action_t;
204 /* This structure describes the UI and internal data required by a file
205 * operation context.
207 typedef struct
209 /* ETA and bps */
210 gboolean showing_eta;
211 gboolean showing_bps;
213 /* Dialog and widgets for the operation progress window */
214 Dlg_head *op_dlg;
215 WLabel *file_string[2];
216 WLabel *file_label[2];
217 WGauge *progress_file_gauge;
218 WLabel *progress_file_label;
220 WGauge *progress_total_gauge;
222 WLabel *total_files_processed_label;
223 WLabel *time_label;
224 WLabel *total_bytes_label;
226 /* Query replace dialog */
227 Dlg_head *replace_dlg;
228 const char *replace_filename;
229 replace_action_t replace_result;
231 struct stat *s_stat, *d_stat;
232 } FileOpContextUI;
234 /*** file scope variables ************************************************************************/
236 /*** file scope functions ************************************************************************/
237 /* --------------------------------------------------------------------------------------------- */
239 static gboolean
240 filegui__check_attrs_on_fs (const char *fs_path)
242 #ifdef USE_STATVFS
243 STRUCT_STATVFS stfs;
245 if (!setup_copymove_persistent_attr)
246 return FALSE;
248 if (statfs (fs_path, &stfs) != 0)
249 return TRUE;
251 #ifdef __linux__
252 switch ((filegui_nonattrs_fs_t) stfs.f_type)
254 case MSDOS_SUPER_MAGIC:
255 case NTFS_SB_MAGIC:
256 case PROC_SUPER_MAGIC:
257 case SMB_SUPER_MAGIC:
258 case NCP_SUPER_MAGIC:
259 case USBDEVICE_SUPER_MAGIC:
260 return FALSE;
261 default:
262 break;
264 #elif defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) \
265 || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
266 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdos") == 0
267 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdosfs") == 0
268 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
269 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "procfs") == 0
270 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
271 || strstr (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fusefs") != NULL)
272 return FALSE;
273 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
274 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "pcfs") == 0
275 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
276 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "proc") == 0
277 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
278 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fuse") == 0)
279 return FALSE;
280 #endif
281 #endif /* USE_STATVFS */
283 return TRUE;
286 /* --------------------------------------------------------------------------------------------- */
288 static void
289 file_frmt_time (char *buffer, double eta_secs)
291 int eta_hours, eta_mins, eta_s;
292 eta_hours = eta_secs / (60 * 60);
293 eta_mins = (eta_secs - (eta_hours * 60 * 60)) / 60;
294 eta_s = eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
295 g_snprintf (buffer, BUF_TINY, _("%d:%02d.%02d"), eta_hours, eta_mins, eta_s);
298 /* --------------------------------------------------------------------------------------------- */
300 static void
301 file_eta_prepare_for_show (char *buffer, double eta_secs, gboolean always_show)
303 char _fmt_buff[BUF_TINY];
304 if (eta_secs <= 0.5 && !always_show)
306 *buffer = '\0';
307 return;
309 if (eta_secs <= 0.5)
310 eta_secs = 1;
311 file_frmt_time (_fmt_buff, eta_secs);
312 g_snprintf (buffer, BUF_TINY, _("ETA %s"), _fmt_buff);
315 /* --------------------------------------------------------------------------------------------- */
317 static void
318 file_bps_prepare_for_show (char *buffer, long bps)
320 if (bps > 1024 * 1024)
322 g_snprintf (buffer, BUF_TINY, _("%.2f MB/s"), bps / (1024 * 1024.0));
324 else if (bps > 1024)
326 g_snprintf (buffer, BUF_TINY, _("%.2f KB/s"), bps / 1024.0);
328 else if (bps > 1)
330 g_snprintf (buffer, BUF_TINY, _("%ld B/s"), bps);
332 else
333 *buffer = '\0';
336 /* --------------------------------------------------------------------------------------------- */
338 * FIXME: probably it is better to replace this with quick dialog machinery,
339 * but actually I'm not familiar with it and have not much time :(
340 * alex
342 static replace_action_t
343 overwrite_query_dialog (FileOpContext * ctx, enum OperationMode mode)
345 #define ADD_RD_BUTTON(i) \
346 add_widget (ui->replace_dlg, \
347 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value, \
348 NORMAL_BUTTON, rd_widgets [i].text, 0))
350 #define ADD_RD_LABEL(i, p1, p2) \
351 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2); \
352 add_widget (ui->replace_dlg, label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer))
354 /* dialog sizes */
355 const int rd_ylen = 17;
356 int rd_xlen = 60;
358 struct
360 const char *text;
361 int ypos, xpos;
362 int value; /* 0 for labels */
363 } rd_widgets[] =
365 /* *INDENT-OFF* */
366 /* 0 */
367 { N_("Target file already exists!"), 3, 4, 0 },
368 /* 1 */
369 { "%s", 4, 4, 0 },
370 /* 2 */ /* cannot use PRIuMAX here; %llu is used instead */
371 { N_("Source date: %s, size %llu"), 6, 4, 0 },
372 /* 3 */ /* cannot use PRIuMAX here; %llu is used instead */
373 { N_("Target date: %s, size %llu"), 7, 4, 0 },
374 /* 4 */
375 { N_("&Abort"), 14, 25, REPLACE_ABORT },
376 /* 5 */
377 { N_("If &size differs"), 12, 28, REPLACE_SIZE },
378 /* 6 */
379 { N_("Non&e"), 11, 47, REPLACE_NEVER },
380 /* 7 */
381 { N_("&Update"), 11, 36, REPLACE_UPDATE },
382 /* 8 */
383 { N_("A&ll"), 11, 28, REPLACE_ALWAYS },
384 /* 9 */
385 { N_("Overwrite all targets?"), 11, 4, 0 },
386 /* 10 */
387 { N_("&Reget"), 10, 28, REPLACE_REGET },
388 /* 11 */
389 { N_("A&ppend"), 9, 45, REPLACE_APPEND },
390 /* 12 */
391 { N_("&No"), 9, 37, REPLACE_NO },
392 /* 13 */
393 { N_("&Yes"), 9, 28, REPLACE_YES },
394 /* 14 */
395 { N_("Overwrite this target?"), 9, 4, 0 }
396 /* *INDENT-ON* */
399 const int num = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
400 int *widgets_len;
402 FileOpContextUI *ui = ctx->ui;
404 char buffer[BUF_SMALL];
405 const char *title;
406 int stripped_name_len;
407 vfs_path_t *stripped_vpath;
408 const char *stripped_name;
409 char *stripped_name_orig;
410 int result;
412 widgets_len = g_new0 (int, num);
414 if (mode == Foreground)
415 title = _("File exists");
416 else
417 title = _("Background process: File exists");
419 stripped_vpath = vfs_path_from_str (ui->replace_filename);
420 stripped_name = stripped_name_orig =
421 vfs_path_to_str_flags (stripped_vpath, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
422 vfs_path_free (stripped_vpath);
423 stripped_name_len = str_term_width1 (stripped_name);
426 int i, l1, l2, l, row;
428 for (i = 0; i < num; i++)
430 #ifdef ENABLE_NLS
431 if (i != 1) /* skip filename */
432 rd_widgets[i].text = _(rd_widgets[i].text);
433 #endif /* ENABLE_NLS */
434 widgets_len[i] = str_term_width1 (rd_widgets[i].text);
438 * longest of "Overwrite..." labels
439 * (assume "Target date..." are short enough)
441 l1 = max (widgets_len[9], widgets_len[14]);
443 /* longest of button rows */
444 i = num;
445 for (row = l = l2 = 0; i--;)
446 if (rd_widgets[i].value != 0)
448 if (row != rd_widgets[i].ypos)
450 row = rd_widgets[i].ypos;
451 l2 = max (l2, l);
452 l = 0;
454 l += widgets_len[i] + 4;
457 l2 = max (l2, l); /* last row */
458 rd_xlen = max (rd_xlen, l1 + l2 + 8);
459 rd_xlen = max (rd_xlen, str_term_width1 (title) + 2);
460 rd_xlen = max (rd_xlen, min (COLS, stripped_name_len + 8));
462 /* Now place widgets */
463 l1 += 5; /* start of first button in the row */
464 i = num;
465 for (l = l1, row = 0; --i > 1;)
466 if (rd_widgets[i].value != 0)
468 if (row != rd_widgets[i].ypos)
470 row = rd_widgets[i].ypos;
471 l = l1;
473 rd_widgets[i].xpos = l;
474 l += widgets_len[i] + 4;
477 /* Abort button is centered */
478 rd_widgets[4].xpos = (rd_xlen - widgets_len[4] - 3) / 2;
481 /* FIXME - missing help node */
482 ui->replace_dlg =
483 create_dlg (TRUE, 0, 0, rd_ylen, rd_xlen, alarm_colors, NULL, "[Replace]",
484 title, DLG_CENTER | DLG_REVERSE);
486 /* prompt -- centered */
487 add_widget (ui->replace_dlg,
488 label_new (rd_widgets[0].ypos, (rd_xlen - widgets_len[0]) / 2, rd_widgets[0].text));
489 /* file name -- centered */
490 stripped_name = str_trunc (stripped_name, rd_xlen - 8);
491 stripped_name_len = str_term_width1 (stripped_name);
492 add_widget (ui->replace_dlg,
493 label_new (rd_widgets[1].ypos, (rd_xlen - stripped_name_len) / 2, stripped_name));
495 /* source date and size */
496 ADD_RD_LABEL (2, file_date (ui->s_stat->st_mtime), (unsigned long long) ui->s_stat->st_size);
497 /* destination date and size */
498 ADD_RD_LABEL (3, file_date (ui->d_stat->st_mtime), (unsigned long long) ui->d_stat->st_size);
500 ADD_RD_BUTTON (4); /* Abort */
501 ADD_RD_BUTTON (5); /* If size differs */
502 ADD_RD_BUTTON (6); /* None */
503 ADD_RD_BUTTON (7); /* Update */
504 ADD_RD_BUTTON (8); /* All" */
505 ADD_RD_LABEL (9, 0, 0); /* Overwrite all targets? */
507 /* "this target..." widgets */
508 if (!S_ISDIR (ui->d_stat->st_mode))
510 if ((ctx->operation == OP_COPY) && (ui->d_stat->st_size != 0)
511 && (ui->s_stat->st_size > ui->d_stat->st_size))
512 ADD_RD_BUTTON (10); /* Reget */
514 ADD_RD_BUTTON (11); /* Append */
516 ADD_RD_BUTTON (12); /* No */
517 ADD_RD_BUTTON (13); /* Yes */
518 ADD_RD_LABEL (14, 0, 0); /* Overwrite this target? */
520 result = run_dlg (ui->replace_dlg);
521 destroy_dlg (ui->replace_dlg);
523 g_free (widgets_len);
524 g_free (stripped_name_orig);
526 return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
527 #undef ADD_RD_LABEL
528 #undef ADD_RD_BUTTON
531 /* --------------------------------------------------------------------------------------------- */
533 static gboolean
534 is_wildcarded (char *p)
536 for (; *p; p++)
538 if (*p == '*')
539 return TRUE;
540 if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
541 return TRUE;
543 return FALSE;
546 /* --------------------------------------------------------------------------------------------- */
547 /*** public functions ****************************************************************************/
548 /* --------------------------------------------------------------------------------------------- */
550 FileProgressStatus
551 check_progress_buttons (FileOpContext * ctx)
553 int c;
554 Gpm_Event event;
555 FileOpContextUI *ui;
557 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
559 ui = ctx->ui;
561 event.x = -1; /* Don't show the GPM cursor */
562 c = tty_get_event (&event, FALSE, FALSE);
563 if (c == EV_NONE)
564 return FILE_CONT;
566 /* Reinitialize to avoid old values after events other than
567 selecting a button */
568 ui->op_dlg->ret_value = FILE_CONT;
570 dlg_process_event (ui->op_dlg, c, &event);
571 switch (ui->op_dlg->ret_value)
573 case FILE_SKIP:
574 return FILE_SKIP;
575 case B_CANCEL:
576 case FILE_ABORT:
577 return FILE_ABORT;
578 default:
579 return FILE_CONT;
584 /* --------------------------------------------------------------------------------------------- */
585 /* {{{ File progress display routines */
587 void
588 file_op_context_create_ui_without_init (FileOpContext * ctx, gboolean with_eta,
589 filegui_dialog_type_t dialog_type)
591 FileOpContextUI *ui;
592 const char *abort_button_label = N_("&Abort");
593 const char *skip_button_label = N_("&Skip");
594 int abort_button_width, skip_button_width, buttons_width;
595 int dlg_width, dlg_height;
597 g_return_if_fail (ctx != NULL);
598 g_return_if_fail (ctx->ui == NULL);
600 #ifdef ENABLE_NLS
601 abort_button_label = _(abort_button_label);
602 skip_button_label = _(skip_button_label);
603 #endif
605 abort_button_width = str_term_width1 (abort_button_label) + 3;
606 skip_button_width = str_term_width1 (skip_button_label) + 3;
607 buttons_width = abort_button_width + skip_button_width + 2;
609 dlg_width = max (58, buttons_width + 6);
610 dlg_height = 17; /* to make compiler happy :) */
612 ui = g_new0 (FileOpContextUI, 1);
613 ctx->ui = ui;
615 ctx->dialog_type = dialog_type;
617 switch (dialog_type)
619 case FILEGUI_DIALOG_ONE_ITEM:
620 dlg_height = verbose ? 12 : 10;
621 break;
622 case FILEGUI_DIALOG_MULTI_ITEM:
623 dlg_height = !verbose ? 10 : file_op_compute_totals ? 17 : 15;
624 break;
625 case FILEGUI_DIALOG_DELETE_ITEM:
626 dlg_height = 7;
627 break;
630 ctx->recursive_result = RECURSIVE_YES;
632 ui->replace_result = REPLACE_YES;
633 ui->showing_eta = with_eta && file_op_compute_totals;
634 ui->showing_bps = with_eta;
636 ui->op_dlg =
637 create_dlg (TRUE, 0, 0, dlg_height, dlg_width,
638 dialog_colors, NULL, NULL, op_names[ctx->operation], DLG_CENTER | DLG_REVERSE);
640 add_widget (ui->op_dlg,
641 button_new (dlg_height - 3, dlg_width / 2 + 1, FILE_ABORT,
642 NORMAL_BUTTON, abort_button_label, NULL));
643 add_widget (ui->op_dlg,
644 button_new (dlg_height - 3, dlg_width / 2 - 1 - skip_button_width, FILE_SKIP,
645 NORMAL_BUTTON, skip_button_label, NULL));
647 if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
649 int dy = file_op_compute_totals ? 2 : 0;
651 if (file_op_compute_totals)
652 add_widget (ui->op_dlg, ui->progress_total_gauge =
653 gauge_new (7 + dy, 3 + 3, 0, 100, 0));
655 add_widget (ui->op_dlg, ui->total_files_processed_label = label_new (9 + dy, 3, ""));
657 add_widget (ui->op_dlg, ui->time_label = label_new (10 + dy, 3, ""));
659 add_widget (ui->op_dlg, ui->total_bytes_label = label_new (8, 3 + 15, ""));
660 add_widget (ui->op_dlg, hline_new (8, 1, dlg_width - 2));
663 add_widget (ui->op_dlg, ui->progress_file_label = label_new (7, 3, ""));
665 add_widget (ui->op_dlg, ui->progress_file_gauge = gauge_new (6, 3 + 3, 0, 100, 0));
667 add_widget (ui->op_dlg, ui->file_string[1] = label_new (5, 3, ""));
669 add_widget (ui->op_dlg, ui->file_label[1] = label_new (4, 3, ""));
670 add_widget (ui->op_dlg, ui->file_string[0] = label_new (3, 3, ""));
671 add_widget (ui->op_dlg, ui->file_label[0] = label_new (2, 3, ""));
673 if ((right_panel == current_panel) && !classic_progressbar)
675 ui->progress_file_gauge->from_left_to_right = FALSE;
676 if (verbose && file_op_compute_totals && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
677 ui->progress_total_gauge->from_left_to_right = FALSE;
681 /* --------------------------------------------------------------------------------------------- */
683 void
684 file_op_context_create_ui (FileOpContext * ctx, gboolean with_eta,
685 filegui_dialog_type_t dialog_type)
687 FileOpContextUI *ui;
689 g_return_if_fail (ctx != NULL);
690 g_return_if_fail (ctx->ui == NULL);
692 file_op_context_create_ui_without_init (ctx, with_eta, dialog_type);
693 ui = ctx->ui;
695 /* We will manage the dialog without any help, that's why
696 we have to call init_dlg */
697 init_dlg (ui->op_dlg);
700 /* --------------------------------------------------------------------------------------------- */
702 void
703 file_op_context_destroy_ui (FileOpContext * ctx)
705 g_return_if_fail (ctx != NULL);
707 if (ctx->ui != NULL)
709 FileOpContextUI *ui = (FileOpContextUI *) ctx->ui;
711 dlg_run_done (ui->op_dlg);
712 destroy_dlg (ui->op_dlg);
713 g_free (ui);
714 ctx->ui = NULL;
718 /* --------------------------------------------------------------------------------------------- */
720 show progressbar for file
723 void
724 file_progress_show (FileOpContext * ctx, off_t done, off_t total,
725 const char *stalled_msg, gboolean force_update)
727 FileOpContextUI *ui;
728 char buffer[BUF_TINY];
729 char buffer2[BUF_TINY];
730 char buffer3[BUF_TINY];
732 if (!verbose)
733 return;
735 g_return_if_fail (ctx != NULL);
736 g_return_if_fail (ctx->ui != NULL);
738 ui = ctx->ui;
740 if (total == 0)
742 gauge_show (ui->progress_file_gauge, 0);
743 return;
746 gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
747 gauge_show (ui->progress_file_gauge, 1);
749 if (!force_update)
750 return;
752 if (ui->showing_eta && ctx->eta_secs > 0.5)
754 file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
755 if (ctx->bps == 0)
756 g_snprintf (buffer, BUF_TINY, "%s %s", buffer2, stalled_msg);
757 else
759 file_bps_prepare_for_show (buffer3, ctx->bps);
760 g_snprintf (buffer, BUF_TINY, "%s (%s) %s", buffer2, buffer3, stalled_msg);
763 else
765 g_snprintf (buffer, BUF_TINY, "%s", stalled_msg);
768 label_set_text (ui->progress_file_label, buffer);
771 /* --------------------------------------------------------------------------------------------- */
773 void
774 file_progress_show_count (FileOpContext * ctx, size_t done, size_t total)
776 char buffer[BUF_TINY];
777 FileOpContextUI *ui;
779 g_return_if_fail (ctx != NULL);
780 g_return_if_fail (ctx->ui != NULL);
782 ui = ctx->ui;
783 if (file_op_compute_totals)
784 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu/%zu"), done, total);
785 else
786 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu"), done);
787 label_set_text (ui->total_files_processed_label, buffer);
790 /* --------------------------------------------------------------------------------------------- */
792 void
793 file_progress_show_total (FileOpTotalContext * tctx, FileOpContext * ctx, uintmax_t copied_bytes,
794 gboolean show_summary)
796 char buffer[BUF_TINY];
797 char buffer2[BUF_TINY];
798 char buffer3[BUF_TINY];
799 char buffer4[BUF_TINY];
800 struct timeval tv_current;
801 FileOpContextUI *ui;
803 g_return_if_fail (ctx != NULL);
804 g_return_if_fail (ctx->ui != NULL);
806 ui = ctx->ui;
808 if (file_op_compute_totals)
810 if (ctx->progress_bytes == 0)
811 gauge_show (ui->progress_total_gauge, 0);
812 else
814 gauge_set_value (ui->progress_total_gauge, 1024,
815 (int) (1024 * copied_bytes / ctx->progress_bytes));
816 gauge_show (ui->progress_total_gauge, 1);
820 if (!show_summary && tctx->bps == 0)
821 return;
823 gettimeofday (&tv_current, NULL);
824 file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec);
826 if (file_op_compute_totals)
828 file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
829 if (tctx->bps == 0)
830 g_snprintf (buffer, BUF_TINY, _("Time: %s %s"), buffer2, buffer3);
831 else
833 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
834 g_snprintf (buffer, BUF_TINY, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
837 else
839 if (tctx->bps == 0)
840 g_snprintf (buffer, BUF_TINY, _("Time: %s"), buffer2);
841 else
843 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
844 g_snprintf (buffer, BUF_TINY, _("Time: %s (%s)"), buffer2, buffer4);
848 label_set_text (ui->time_label, buffer);
850 size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
851 if (!file_op_compute_totals)
852 g_snprintf (buffer, BUF_TINY, _(" Total: %s "), buffer2);
853 else
855 size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
856 g_snprintf (buffer, BUF_TINY, _(" Total: %s/%s "), buffer2, buffer3);
859 label_set_text (ui->total_bytes_label, buffer);
862 /* }}} */
864 /* --------------------------------------------------------------------------------------------- */
866 void
867 file_progress_show_source (FileOpContext * ctx, const vfs_path_t * s_vpath)
869 FileOpContextUI *ui;
871 g_return_if_fail (ctx != NULL);
872 g_return_if_fail (ctx->ui != NULL);
874 ui = ctx->ui;
876 if (s_vpath != NULL)
878 char *s;
880 s = vfs_path_tokens_get (s_vpath, -1, 1);
881 label_set_text (ui->file_label[0], _("Source"));
882 label_set_text (ui->file_string[0], truncFileString (ui, s));
883 g_free (s);
885 else
887 label_set_text (ui->file_label[0], "");
888 label_set_text (ui->file_string[0], "");
892 /* --------------------------------------------------------------------------------------------- */
894 void
895 file_progress_show_target (FileOpContext * ctx, const vfs_path_t * s_vpath)
897 FileOpContextUI *ui;
899 g_return_if_fail (ctx != NULL);
900 g_return_if_fail (ctx->ui != NULL);
902 ui = ctx->ui;
904 if (s_vpath != NULL)
906 char *s;
908 s = vfs_path_to_str (s_vpath);
909 label_set_text (ui->file_label[1], _("Target"));
910 label_set_text (ui->file_string[1], truncFileStringSecure (ui, s));
911 g_free (s);
913 else
915 label_set_text (ui->file_label[1], "");
916 label_set_text (ui->file_string[1], "");
920 /* --------------------------------------------------------------------------------------------- */
922 void
923 file_progress_show_deleting (FileOpContext * ctx, const char *s)
925 FileOpContextUI *ui;
927 g_return_if_fail (ctx != NULL);
928 g_return_if_fail (ctx->ui != NULL);
930 ui = ctx->ui;
931 label_set_text (ui->file_label[0], _("Deleting"));
932 label_set_text (ui->file_label[0], truncFileStringSecure (ui, s));
935 /* --------------------------------------------------------------------------------------------- */
937 FileProgressStatus
938 file_progress_real_query_replace (FileOpContext * ctx,
939 enum OperationMode mode, const char *destname,
940 struct stat *_s_stat, struct stat *_d_stat)
942 FileOpContextUI *ui;
944 g_return_val_if_fail (ctx != NULL, FILE_CONT);
945 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
947 ui = ctx->ui;
949 if (ui->replace_result < REPLACE_ALWAYS)
951 ui->replace_filename = destname;
952 ui->s_stat = _s_stat;
953 ui->d_stat = _d_stat;
954 ui->replace_result = overwrite_query_dialog (ctx, mode);
957 switch (ui->replace_result)
959 case REPLACE_UPDATE:
960 do_refresh ();
961 if (_s_stat->st_mtime > _d_stat->st_mtime)
962 return FILE_CONT;
963 else
964 return FILE_SKIP;
966 case REPLACE_SIZE:
967 do_refresh ();
968 if (_s_stat->st_size == _d_stat->st_size)
969 return FILE_SKIP;
970 else
971 return FILE_CONT;
973 case REPLACE_REGET:
974 /* Careful: we fall through and set do_append */
975 ctx->do_reget = _d_stat->st_size;
977 case REPLACE_APPEND:
978 ctx->do_append = TRUE;
980 case REPLACE_YES:
981 case REPLACE_ALWAYS:
982 do_refresh ();
983 return FILE_CONT;
984 case REPLACE_NO:
985 case REPLACE_NEVER:
986 do_refresh ();
987 return FILE_SKIP;
988 case REPLACE_ABORT:
989 default:
990 return FILE_ABORT;
994 /* --------------------------------------------------------------------------------------------- */
996 char *
997 file_mask_dialog (FileOpContext * ctx, FileOperation operation,
998 gboolean only_one,
999 const char *format, const void *text, const char *def_text, gboolean * do_bg)
1001 const size_t FMDY = 13;
1002 const size_t FMDX = 68;
1003 size_t fmd_xlen;
1005 /* buttons */
1006 const size_t gap = 1;
1007 size_t b0_len, b2_len;
1008 size_t b1_len = 0;
1010 int source_easy_patterns = easy_patterns;
1011 size_t i, len;
1012 char fmd_buf[BUF_MEDIUM];
1013 char *source_mask, *orig_mask, *dest_dir, *tmp;
1014 char *def_text_secure;
1015 int val;
1017 QuickWidget fmd_widgets[] = {
1018 /* 0 */ QUICK_BUTTON (42, 64, 10, FMDY, N_("&Cancel"), B_CANCEL, NULL),
1019 #ifdef ENABLE_BACKGROUND
1020 /* 1 */ QUICK_BUTTON (25, 64, 10, FMDY, N_("&Background"), B_USER, NULL),
1021 #define OFFSET 0
1022 #else
1023 #define OFFSET 1
1024 #endif /* ENABLE_BACKGROUND */
1025 /* 2 - OFFSET */
1026 QUICK_BUTTON (14, FMDX, 10, FMDY, N_("&OK"), B_ENTER, NULL),
1027 /* 3 - OFFSET */
1028 QUICK_CHECKBOX (42, FMDX, 8, FMDY, N_("&Stable Symlinks"), &ctx->stable_symlinks),
1029 /* 4 - OFFSET */
1030 QUICK_CHECKBOX (31, FMDX, 7, FMDY, N_("Di&ve into subdir if exists"),
1031 &ctx->dive_into_subdirs),
1032 /* 5 - OFFSET */
1033 QUICK_CHECKBOX (3, FMDX, 8, FMDY, N_("Preserve &attributes"), &ctx->op_preserve),
1034 /* 6 - OFFSET */
1035 QUICK_CHECKBOX (3, FMDX, 7, FMDY, N_("Follow &links"), &ctx->follow_links),
1036 /* 7 - OFFSET */
1037 QUICK_INPUT (3, FMDX, 6, FMDY, "", 58, 0, "input2", &dest_dir),
1038 /* 8 - OFFSET */
1039 QUICK_LABEL (3, FMDX, 5, FMDY, N_("to:")),
1040 /* 9 - OFFSET */
1041 QUICK_CHECKBOX (37, FMDX, 4, FMDY, N_("&Using shell patterns"), &source_easy_patterns),
1042 /* 10 - OFFSET */
1043 QUICK_INPUT (3, FMDX, 3, FMDY, easy_patterns ? "*" : "^(.*)$", 58, 0, "input-def",
1044 &source_mask),
1045 /* 11 - OFFSET */
1046 QUICK_LABEL (3, FMDX, 2, FMDY, fmd_buf),
1047 QUICK_END
1050 g_return_val_if_fail (ctx != NULL, NULL);
1052 #ifdef ENABLE_NLS
1053 /* buttons */
1054 for (i = 0; i <= 2 - OFFSET; i++)
1055 fmd_widgets[i].u.button.text = _(fmd_widgets[i].u.button.text);
1057 /* checkboxes */
1058 for (i = 3 - OFFSET; i <= 9 - OFFSET; i++)
1059 if (i != 7 - OFFSET)
1060 fmd_widgets[i].u.checkbox.text = _(fmd_widgets[i].u.checkbox.text);
1061 #endif /* !ENABLE_NLS */
1063 fmd_xlen = max (FMDX, (size_t) COLS * 2 / 3);
1065 len = str_term_width1 (fmd_widgets[6 - OFFSET].u.checkbox.text)
1066 + str_term_width1 (fmd_widgets[4 - OFFSET].u.checkbox.text) + 15;
1067 fmd_xlen = max (fmd_xlen, len);
1069 len = str_term_width1 (fmd_widgets[5 - OFFSET].u.checkbox.text)
1070 + str_term_width1 (fmd_widgets[3 - OFFSET].u.checkbox.text) + 15;
1071 fmd_xlen = max (fmd_xlen, len);
1073 /* buttons */
1074 b2_len = str_term_width1 (fmd_widgets[2 - OFFSET].u.button.text) + 6 + gap; /* OK */
1075 #ifdef ENABLE_BACKGROUND
1076 b1_len = str_term_width1 (fmd_widgets[1].u.button.text) + 4 + gap; /* Background */
1077 #endif
1078 b0_len = str_term_width1 (fmd_widgets[0].u.button.text) + 4; /* Cancel */
1079 len = b0_len + b1_len + b2_len;
1080 fmd_xlen = min (max (fmd_xlen, len + 6), (size_t) COLS);
1082 if (only_one)
1084 int flen;
1086 flen = str_term_width1 (format);
1087 i = fmd_xlen - flen - 4; /* FIXME */
1088 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc ((const char *) text, i));
1090 else
1092 g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1093 fmd_xlen = max (fmd_xlen, (size_t) str_term_width1 (fmd_buf) + 6);
1096 for (i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]); i > 0;)
1097 fmd_widgets[--i].x_divisions = fmd_xlen;
1099 i = (fmd_xlen - len) / 2;
1100 /* OK button */
1101 fmd_widgets[2 - OFFSET].relative_x = i;
1102 i += b2_len;
1103 #ifdef ENABLE_BACKGROUND
1104 /* Background button */
1105 fmd_widgets[1].relative_x = i;
1106 i += b1_len;
1107 #endif
1108 /* Cancel button */
1109 fmd_widgets[0].relative_x = i;
1111 #define chkbox_xpos(i) \
1112 fmd_widgets [i].relative_x = fmd_xlen - str_term_width1 (fmd_widgets [i].u.checkbox.text) - 6
1113 chkbox_xpos (3 - OFFSET);
1114 chkbox_xpos (4 - OFFSET);
1115 chkbox_xpos (9 - OFFSET);
1116 #undef chkbox_xpos
1118 /* inputs */
1119 fmd_widgets[7 - OFFSET].u.input.len = fmd_widgets[10 - OFFSET].u.input.len = fmd_xlen - 6;
1121 /* unselect checkbox if target filesystem don't support attributes */
1122 ctx->op_preserve = filegui__check_attrs_on_fs (def_text);
1124 /* filter out a possible password from def_text */
1126 vfs_path_t *vpath;
1128 vpath = vfs_path_from_str_flags (def_text, (only_one) ? VPF_NO_CANON : VPF_NONE);
1129 tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1130 vfs_path_free (vpath);
1132 if (source_easy_patterns)
1133 def_text_secure = strutils_glob_escape (tmp);
1134 else
1135 def_text_secure = strutils_regex_escape (tmp);
1136 g_free (tmp);
1138 /* destination */
1139 fmd_widgets[7 - OFFSET].u.input.text = def_text_secure;
1141 ctx->stable_symlinks = FALSE;
1142 *do_bg = FALSE;
1145 struct stat buf;
1146 vfs_path_t *vpath;
1148 QuickDialog Quick_input = {
1149 fmd_xlen, FMDY, -1, -1, op_names[operation],
1150 "[Mask Copy/Rename]", fmd_widgets, NULL, TRUE
1153 ask_file_mask:
1154 val = quick_dialog_skip (&Quick_input, 4);
1156 if (val == B_CANCEL)
1158 g_free (def_text_secure);
1159 return NULL;
1162 if (ctx->follow_links)
1163 ctx->stat_func = mc_stat;
1164 else
1165 ctx->stat_func = mc_lstat;
1167 if (ctx->op_preserve)
1169 ctx->preserve = TRUE;
1170 ctx->umask_kill = 0777777;
1171 ctx->preserve_uidgid = (geteuid () == 0);
1173 else
1175 int i2;
1176 ctx->preserve = ctx->preserve_uidgid = FALSE;
1177 i2 = umask (0);
1178 umask (i2);
1179 ctx->umask_kill = i2 ^ 0777777;
1182 if ((dest_dir == NULL) || (*dest_dir == '\0'))
1184 g_free (def_text_secure);
1185 g_free (source_mask);
1186 return dest_dir;
1189 ctx->search_handle = mc_search_new (source_mask, -1);
1191 if (ctx->search_handle == NULL)
1193 message (D_ERROR, MSG_ERROR, _("Invalid source pattern `%s'"), source_mask);
1194 g_free (dest_dir);
1195 g_free (source_mask);
1196 goto ask_file_mask;
1199 g_free (def_text_secure);
1200 g_free (source_mask);
1202 ctx->search_handle->is_case_sensitive = TRUE;
1203 if (source_easy_patterns)
1204 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1205 else
1206 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1208 tmp = dest_dir;
1209 dest_dir = tilde_expand (tmp);
1210 g_free (tmp);
1211 vpath = vfs_path_from_str (dest_dir);
1213 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1214 if (ctx->dest_mask == NULL)
1215 ctx->dest_mask = dest_dir;
1216 else
1217 ctx->dest_mask++;
1218 orig_mask = ctx->dest_mask;
1219 if (*ctx->dest_mask == '\0'
1220 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1221 && (!only_one
1222 || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1223 || (ctx->dive_into_subdirs
1224 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1225 || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1226 ctx->dest_mask = g_strdup ("\\0");
1227 else
1229 ctx->dest_mask = g_strdup (ctx->dest_mask);
1230 *orig_mask = '\0';
1232 if (!*dest_dir)
1234 g_free (dest_dir);
1235 dest_dir = g_strdup ("./");
1237 vfs_path_free (vpath);
1238 if (val == B_USER)
1239 *do_bg = TRUE;
1242 return dest_dir;
1245 /* --------------------------------------------------------------------------------------------- */