Code indentation.
[midnight-commander.git] / src / filemanager / filegui.c
blobeb7374d8132296c7daf9775c285c57b5dfbdee58
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 : device == B_FILE_ERROR ? EIO : 0);
117 return -1;
119 /* If successful, buf->dev will be == device. */
120 return fs_stat_dev (device, buf);
123 #define STRUCT_STATVFS struct fs_info
124 #else
125 #define STRUCT_STATVFS struct statfs
126 #endif
127 #endif
129 #if HAVE_STRUCT_STATVFS_F_BASETYPE
130 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
131 #else
132 #if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
133 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
134 #elif HAVE_OS_H /* BeOS */
135 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
136 #endif
137 #endif
139 #include <unistd.h>
141 #include "lib/global.h"
143 #include "lib/tty/key.h" /* tty_get_event */
144 #include "lib/mcconfig.h"
145 #include "lib/search.h"
146 #include "lib/vfs/vfs.h"
147 #include "lib/strescape.h"
148 #include "lib/strutil.h"
149 #include "lib/timefmt.h" /* file_date() */
150 #include "lib/util.h"
151 #include "lib/widget.h"
153 #include "src/setup.h" /* verbose */
155 #include "midnight.h"
156 #include "fileopctx.h" /* FILE_CONT */
158 #include "filegui.h"
160 /* }}} */
162 /*** global variables ****************************************************************************/
164 int classic_progressbar = 1;
166 /*** file scope macro definitions ****************************************************************/
168 /* Hack: the vfs code should not rely on this */
169 #define WITH_FULL_PATHS 1
171 #define truncFileString(ui, s) str_trunc (s, 52)
172 #define truncFileStringSecure(ui, s) path_trunc (s, 52)
174 /*** file scope type declarations ****************************************************************/
176 /* *INDENT-OFF* */
177 typedef enum {
178 MSDOS_SUPER_MAGIC = 0x4d44,
179 NTFS_SB_MAGIC = 0x5346544e,
180 FUSE_MAGIC = 0x65735546,
181 PROC_SUPER_MAGIC = 0x9fa0,
182 SMB_SUPER_MAGIC = 0x517B,
183 NCP_SUPER_MAGIC = 0x564c,
184 USBDEVICE_SUPER_MAGIC = 0x9fa2
185 } filegui_nonattrs_fs_t;
186 /* *INDENT-ON* */
188 /* Used for button result values */
189 typedef enum
191 REPLACE_YES = B_USER,
192 REPLACE_NO,
193 REPLACE_APPEND,
194 REPLACE_ALWAYS,
195 REPLACE_UPDATE,
196 REPLACE_NEVER,
197 REPLACE_ABORT,
198 REPLACE_SIZE,
199 REPLACE_REGET
200 } replace_action_t;
202 /* This structure describes the UI and internal data required by a file
203 * operation context.
205 typedef struct
207 /* ETA and bps */
208 gboolean showing_eta;
209 gboolean showing_bps;
211 /* Dialog and widgets for the operation progress window */
212 Dlg_head *op_dlg;
213 WLabel *file_string[2];
214 WLabel *file_label[2];
215 WGauge *progress_file_gauge;
216 WLabel *progress_file_label;
218 WGauge *progress_total_gauge;
220 WLabel *total_files_processed_label;
221 WLabel *time_label;
222 WLabel *total_bytes_label;
224 /* Query replace dialog */
225 Dlg_head *replace_dlg;
226 const char *replace_filename;
227 replace_action_t replace_result;
229 struct stat *s_stat, *d_stat;
230 } FileOpContextUI;
232 /*** file scope variables ************************************************************************/
234 /*** file scope functions ************************************************************************/
235 /* --------------------------------------------------------------------------------------------- */
237 static gboolean
238 filegui__check_attrs_on_fs (const char *fs_path)
240 #ifdef USE_STATVFS
241 STRUCT_STATVFS stfs;
243 if (!setup_copymove_persistent_attr)
244 return FALSE;
246 if (statfs (fs_path, &stfs) != 0)
247 return TRUE;
249 #ifdef __linux__
250 switch ((filegui_nonattrs_fs_t) stfs.f_type)
252 case MSDOS_SUPER_MAGIC:
253 case NTFS_SB_MAGIC:
254 case PROC_SUPER_MAGIC:
255 case SMB_SUPER_MAGIC:
256 case NCP_SUPER_MAGIC:
257 case USBDEVICE_SUPER_MAGIC:
258 return FALSE;
259 default:
260 break;
262 #elif defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) \
263 || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
264 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdos") == 0
265 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdosfs") == 0
266 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
267 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "procfs") == 0
268 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
269 || strstr (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fusefs") != NULL)
270 return FALSE;
271 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
272 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "pcfs") == 0
273 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
274 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "proc") == 0
275 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
276 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fuse") == 0)
277 return FALSE;
278 #endif
279 #endif /* USE_STATVFS */
281 return TRUE;
284 /* --------------------------------------------------------------------------------------------- */
286 static void
287 file_frmt_time (char *buffer, double eta_secs)
289 int eta_hours, eta_mins, eta_s;
290 eta_hours = eta_secs / (60 * 60);
291 eta_mins = (eta_secs - (eta_hours * 60 * 60)) / 60;
292 eta_s = eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
293 g_snprintf (buffer, BUF_TINY, _("%d:%02d.%02d"), eta_hours, eta_mins, eta_s);
296 /* --------------------------------------------------------------------------------------------- */
298 static void
299 file_eta_prepare_for_show (char *buffer, double eta_secs, gboolean always_show)
301 char _fmt_buff[BUF_TINY];
302 if (eta_secs <= 0.5 && !always_show)
304 *buffer = '\0';
305 return;
307 if (eta_secs <= 0.5)
308 eta_secs = 1;
309 file_frmt_time (_fmt_buff, eta_secs);
310 g_snprintf (buffer, BUF_TINY, _("ETA %s"), _fmt_buff);
313 /* --------------------------------------------------------------------------------------------- */
315 static void
316 file_bps_prepare_for_show (char *buffer, long bps)
318 if (bps > 1024 * 1024)
320 g_snprintf (buffer, BUF_TINY, _("%.2f MB/s"), bps / (1024 * 1024.0));
322 else if (bps > 1024)
324 g_snprintf (buffer, BUF_TINY, _("%.2f KB/s"), bps / 1024.0);
326 else if (bps > 1)
328 g_snprintf (buffer, BUF_TINY, _("%ld B/s"), bps);
330 else
331 *buffer = '\0';
334 /* --------------------------------------------------------------------------------------------- */
336 * FIXME: probably it is better to replace this with quick dialog machinery,
337 * but actually I'm not familiar with it and have not much time :(
338 * alex
340 static replace_action_t
341 overwrite_query_dialog (FileOpContext * ctx, enum OperationMode mode)
343 #define ADD_RD_BUTTON(i) \
344 add_widget (ui->replace_dlg, \
345 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value, \
346 NORMAL_BUTTON, rd_widgets [i].text, 0))
348 #define ADD_RD_LABEL(i, p1, p2) \
349 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2); \
350 add_widget (ui->replace_dlg, label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer))
352 /* dialog sizes */
353 const int rd_ylen = 17;
354 int rd_xlen = 60;
356 struct
358 const char *text;
359 int ypos, xpos;
360 int value; /* 0 for labels */
361 } rd_widgets[] =
363 /* *INDENT-OFF* */
364 /* 0 */
365 { N_("Target file already exists!"), 3, 4, 0 },
366 /* 1 */
367 { "%s", 4, 4, 0 },
368 /* 2 */ /* cannot use PRIuMAX here; %llu is used instead */
369 { N_("Source date: %s, size %llu"), 6, 4, 0 },
370 /* 3 */ /* cannot use PRIuMAX here; %llu is used instead */
371 { N_("Target date: %s, size %llu"), 7, 4, 0 },
372 /* 4 */
373 { N_("&Abort"), 14, 25, REPLACE_ABORT },
374 /* 5 */
375 { N_("If &size differs"), 12, 28, REPLACE_SIZE },
376 /* 6 */
377 { N_("Non&e"), 11, 47, REPLACE_NEVER },
378 /* 7 */
379 { N_("&Update"), 11, 36, REPLACE_UPDATE },
380 /* 8 */
381 { N_("A&ll"), 11, 28, REPLACE_ALWAYS },
382 /* 9 */
383 { N_("Overwrite all targets?"), 11, 4, 0 },
384 /* 10 */
385 { N_("&Reget"), 10, 28, REPLACE_REGET },
386 /* 11 */
387 { N_("A&ppend"), 9, 45, REPLACE_APPEND },
388 /* 12 */
389 { N_("&No"), 9, 37, REPLACE_NO },
390 /* 13 */
391 { N_("&Yes"), 9, 28, REPLACE_YES },
392 /* 14 */
393 { N_("Overwrite this target?"), 9, 4, 0 }
394 /* *INDENT-ON* */
397 const int num = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
398 int *widgets_len;
400 FileOpContextUI *ui = ctx->ui;
402 char buffer[BUF_SMALL];
403 const char *title;
404 int stripped_name_len;
405 vfs_path_t *stripped_vpath;
406 const char *stripped_name;
407 char *stripped_name_orig;
408 int result;
410 widgets_len = g_new0 (int, num);
412 if (mode == Foreground)
413 title = _("File exists");
414 else
415 title = _("Background process: File exists");
417 stripped_vpath = vfs_path_from_str (ui->replace_filename);
418 stripped_name = stripped_name_orig =
419 vfs_path_to_str_flags (stripped_vpath, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
420 vfs_path_free (stripped_vpath);
421 stripped_name_len = str_term_width1 (stripped_name);
424 int i, l1, l2, l, row;
426 for (i = 0; i < num; i++)
428 #ifdef ENABLE_NLS
429 if (i != 1) /* skip filename */
430 rd_widgets[i].text = _(rd_widgets[i].text);
431 #endif /* ENABLE_NLS */
432 widgets_len[i] = str_term_width1 (rd_widgets[i].text);
436 * longest of "Overwrite..." labels
437 * (assume "Target date..." are short enough)
439 l1 = max (widgets_len[9], widgets_len[14]);
441 /* longest of button rows */
442 i = num;
443 for (row = l = l2 = 0; i--;)
444 if (rd_widgets[i].value != 0)
446 if (row != rd_widgets[i].ypos)
448 row = rd_widgets[i].ypos;
449 l2 = max (l2, l);
450 l = 0;
452 l += widgets_len[i] + 4;
455 l2 = max (l2, l); /* last row */
456 rd_xlen = max (rd_xlen, l1 + l2 + 8);
457 rd_xlen = max (rd_xlen, str_term_width1 (title) + 2);
458 rd_xlen = max (rd_xlen, min (COLS, stripped_name_len + 8));
460 /* Now place widgets */
461 l1 += 5; /* start of first button in the row */
462 i = num;
463 for (l = l1, row = 0; --i > 1;)
464 if (rd_widgets[i].value != 0)
466 if (row != rd_widgets[i].ypos)
468 row = rd_widgets[i].ypos;
469 l = l1;
471 rd_widgets[i].xpos = l;
472 l += widgets_len[i] + 4;
475 /* Abort button is centered */
476 rd_widgets[4].xpos = (rd_xlen - widgets_len[4] - 3) / 2;
479 /* FIXME - missing help node */
480 ui->replace_dlg =
481 create_dlg (TRUE, 0, 0, rd_ylen, rd_xlen, alarm_colors, NULL, NULL, "[Replace]",
482 title, DLG_CENTER | DLG_REVERSE);
484 /* prompt -- centered */
485 add_widget (ui->replace_dlg,
486 label_new (rd_widgets[0].ypos, (rd_xlen - widgets_len[0]) / 2, rd_widgets[0].text));
487 /* file name -- centered */
488 stripped_name = str_trunc (stripped_name, rd_xlen - 8);
489 stripped_name_len = str_term_width1 (stripped_name);
490 add_widget (ui->replace_dlg,
491 label_new (rd_widgets[1].ypos, (rd_xlen - stripped_name_len) / 2, stripped_name));
493 /* source date and size */
494 ADD_RD_LABEL (2, file_date (ui->s_stat->st_mtime), (unsigned long long) ui->s_stat->st_size);
495 /* destination date and size */
496 ADD_RD_LABEL (3, file_date (ui->d_stat->st_mtime), (unsigned long long) ui->d_stat->st_size);
498 ADD_RD_BUTTON (4); /* Abort */
499 ADD_RD_BUTTON (5); /* If size differs */
500 ADD_RD_BUTTON (6); /* None */
501 ADD_RD_BUTTON (7); /* Update */
502 ADD_RD_BUTTON (8); /* All" */
503 ADD_RD_LABEL (9, 0, 0); /* Overwrite all targets? */
505 /* "this target..." widgets */
506 if (!S_ISDIR (ui->d_stat->st_mode))
508 if ((ctx->operation == OP_COPY) && (ui->d_stat->st_size != 0)
509 && (ui->s_stat->st_size > ui->d_stat->st_size))
510 ADD_RD_BUTTON (10); /* Reget */
512 ADD_RD_BUTTON (11); /* Append */
514 ADD_RD_BUTTON (12); /* No */
515 ADD_RD_BUTTON (13); /* Yes */
516 ADD_RD_LABEL (14, 0, 0); /* Overwrite this target? */
518 result = run_dlg (ui->replace_dlg);
519 destroy_dlg (ui->replace_dlg);
521 g_free (widgets_len);
522 g_free (stripped_name_orig);
524 return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
525 #undef ADD_RD_LABEL
526 #undef ADD_RD_BUTTON
529 /* --------------------------------------------------------------------------------------------- */
531 static gboolean
532 is_wildcarded (char *p)
534 for (; *p; p++)
536 if (*p == '*')
537 return TRUE;
538 if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
539 return TRUE;
541 return FALSE;
544 /* --------------------------------------------------------------------------------------------- */
545 /*** public functions ****************************************************************************/
546 /* --------------------------------------------------------------------------------------------- */
548 FileProgressStatus
549 check_progress_buttons (FileOpContext * ctx)
551 int c;
552 Gpm_Event event;
553 FileOpContextUI *ui;
555 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
557 ui = ctx->ui;
559 event.x = -1; /* Don't show the GPM cursor */
560 c = tty_get_event (&event, FALSE, FALSE);
561 if (c == EV_NONE)
562 return FILE_CONT;
564 /* Reinitialize to avoid old values after events other than
565 selecting a button */
566 ui->op_dlg->ret_value = FILE_CONT;
568 dlg_process_event (ui->op_dlg, c, &event);
569 switch (ui->op_dlg->ret_value)
571 case FILE_SKIP:
572 return FILE_SKIP;
573 case B_CANCEL:
574 case FILE_ABORT:
575 return FILE_ABORT;
576 default:
577 return FILE_CONT;
582 /* --------------------------------------------------------------------------------------------- */
583 /* {{{ File progress display routines */
585 void
586 file_op_context_create_ui_without_init (FileOpContext * ctx, gboolean with_eta,
587 filegui_dialog_type_t dialog_type)
589 FileOpContextUI *ui;
590 const char *abort_button_label = N_("&Abort");
591 const char *skip_button_label = N_("&Skip");
592 int abort_button_width, skip_button_width, buttons_width;
593 int dlg_width, dlg_height;
595 g_return_if_fail (ctx != NULL);
596 g_return_if_fail (ctx->ui == NULL);
598 #ifdef ENABLE_NLS
599 abort_button_label = _(abort_button_label);
600 skip_button_label = _(skip_button_label);
601 #endif
603 abort_button_width = str_term_width1 (abort_button_label) + 3;
604 skip_button_width = str_term_width1 (skip_button_label) + 3;
605 buttons_width = abort_button_width + skip_button_width + 2;
607 dlg_width = max (58, buttons_width + 6);
608 dlg_height = 17; /* to make compiler happy :) */
610 ui = g_new0 (FileOpContextUI, 1);
611 ctx->ui = ui;
613 ctx->dialog_type = dialog_type;
615 switch (dialog_type)
617 case FILEGUI_DIALOG_ONE_ITEM:
618 dlg_height = verbose ? 12 : 10;
619 break;
620 case FILEGUI_DIALOG_MULTI_ITEM:
621 dlg_height = !verbose ? 10 : file_op_compute_totals ? 17 : 15;
622 break;
623 case FILEGUI_DIALOG_DELETE_ITEM:
624 dlg_height = 7;
625 break;
628 ctx->recursive_result = RECURSIVE_YES;
630 ui->replace_result = REPLACE_YES;
631 ui->showing_eta = with_eta && file_op_compute_totals;
632 ui->showing_bps = with_eta;
634 ui->op_dlg =
635 create_dlg (TRUE, 0, 0, dlg_height, dlg_width,
636 dialog_colors, NULL, NULL, NULL, op_names[ctx->operation],
637 DLG_CENTER | DLG_REVERSE);
639 add_widget (ui->op_dlg,
640 button_new (dlg_height - 3, dlg_width / 2 + 1, FILE_ABORT,
641 NORMAL_BUTTON, abort_button_label, NULL));
642 add_widget (ui->op_dlg,
643 button_new (dlg_height - 3, dlg_width / 2 - 1 - skip_button_width, FILE_SKIP,
644 NORMAL_BUTTON, skip_button_label, NULL));
646 if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
648 int dy = file_op_compute_totals ? 2 : 0;
650 if (file_op_compute_totals)
651 add_widget (ui->op_dlg, ui->progress_total_gauge =
652 gauge_new (7 + dy, 3 + 3, 0, 100, 0));
654 add_widget (ui->op_dlg, ui->total_files_processed_label = label_new (9 + dy, 3, ""));
656 add_widget (ui->op_dlg, ui->time_label = label_new (10 + dy, 3, ""));
658 add_widget (ui->op_dlg, ui->total_bytes_label = label_new (8, 3 + 15, ""));
659 add_widget (ui->op_dlg, hline_new (8, 1, dlg_width - 2));
662 add_widget (ui->op_dlg, ui->progress_file_label = label_new (7, 3, ""));
664 add_widget (ui->op_dlg, ui->progress_file_gauge = gauge_new (6, 3 + 3, 0, 100, 0));
666 add_widget (ui->op_dlg, ui->file_string[1] = label_new (5, 3, ""));
668 add_widget (ui->op_dlg, ui->file_label[1] = label_new (4, 3, ""));
669 add_widget (ui->op_dlg, ui->file_string[0] = label_new (3, 3, ""));
670 add_widget (ui->op_dlg, ui->file_label[0] = label_new (2, 3, ""));
672 if ((right_panel == current_panel) && !classic_progressbar)
674 ui->progress_file_gauge->from_left_to_right = FALSE;
675 if (verbose && file_op_compute_totals && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
676 ui->progress_total_gauge->from_left_to_right = FALSE;
680 /* --------------------------------------------------------------------------------------------- */
682 void
683 file_op_context_create_ui (FileOpContext * ctx, gboolean with_eta,
684 filegui_dialog_type_t dialog_type)
686 FileOpContextUI *ui;
688 g_return_if_fail (ctx != NULL);
689 g_return_if_fail (ctx->ui == NULL);
691 file_op_context_create_ui_without_init (ctx, with_eta, dialog_type);
692 ui = ctx->ui;
694 /* We will manage the dialog without any help, that's why
695 we have to call init_dlg */
696 init_dlg (ui->op_dlg);
699 /* --------------------------------------------------------------------------------------------- */
701 void
702 file_op_context_destroy_ui (FileOpContext * ctx)
704 g_return_if_fail (ctx != NULL);
706 if (ctx->ui != NULL)
708 FileOpContextUI *ui = (FileOpContextUI *) ctx->ui;
710 dlg_run_done (ui->op_dlg);
711 destroy_dlg (ui->op_dlg);
712 g_free (ui);
713 ctx->ui = NULL;
717 /* --------------------------------------------------------------------------------------------- */
719 show progressbar for file
722 void
723 file_progress_show (FileOpContext * ctx, off_t done, off_t total,
724 const char *stalled_msg, gboolean force_update)
726 FileOpContextUI *ui;
727 char buffer[BUF_TINY];
728 char buffer2[BUF_TINY];
729 char buffer3[BUF_TINY];
731 if (!verbose)
732 return;
734 g_return_if_fail (ctx != NULL);
735 g_return_if_fail (ctx->ui != NULL);
737 ui = ctx->ui;
739 if (total == 0)
741 gauge_show (ui->progress_file_gauge, 0);
742 return;
745 gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
746 gauge_show (ui->progress_file_gauge, 1);
748 if (!force_update)
749 return;
751 if (ui->showing_eta && ctx->eta_secs > 0.5)
753 file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
754 if (ctx->bps == 0)
755 g_snprintf (buffer, BUF_TINY, "%s %s", buffer2, stalled_msg);
756 else
758 file_bps_prepare_for_show (buffer3, ctx->bps);
759 g_snprintf (buffer, BUF_TINY, "%s (%s) %s", buffer2, buffer3, stalled_msg);
762 else
764 g_snprintf (buffer, BUF_TINY, "%s", stalled_msg);
767 label_set_text (ui->progress_file_label, buffer);
770 /* --------------------------------------------------------------------------------------------- */
772 void
773 file_progress_show_count (FileOpContext * ctx, size_t done, size_t total)
775 char buffer[BUF_TINY];
776 FileOpContextUI *ui;
778 g_return_if_fail (ctx != NULL);
779 g_return_if_fail (ctx->ui != NULL);
781 ui = ctx->ui;
782 if (file_op_compute_totals)
783 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu/%zu"), done, total);
784 else
785 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu"), done);
786 label_set_text (ui->total_files_processed_label, buffer);
789 /* --------------------------------------------------------------------------------------------- */
791 void
792 file_progress_show_total (FileOpTotalContext * tctx, FileOpContext * ctx, uintmax_t copied_bytes,
793 gboolean show_summary)
795 char buffer[BUF_TINY];
796 char buffer2[BUF_TINY];
797 char buffer3[BUF_TINY];
798 char buffer4[BUF_TINY];
799 struct timeval tv_current;
800 FileOpContextUI *ui;
802 g_return_if_fail (ctx != NULL);
803 g_return_if_fail (ctx->ui != NULL);
805 ui = ctx->ui;
807 if (file_op_compute_totals)
809 if (ctx->progress_bytes == 0)
810 gauge_show (ui->progress_total_gauge, 0);
811 else
813 gauge_set_value (ui->progress_total_gauge, 1024,
814 (int) (1024 * copied_bytes / ctx->progress_bytes));
815 gauge_show (ui->progress_total_gauge, 1);
819 if (!show_summary && tctx->bps == 0)
820 return;
822 gettimeofday (&tv_current, NULL);
823 file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec);
825 if (file_op_compute_totals)
827 file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
828 if (tctx->bps == 0)
829 g_snprintf (buffer, BUF_TINY, _("Time: %s %s"), buffer2, buffer3);
830 else
832 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
833 g_snprintf (buffer, BUF_TINY, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
836 else
838 if (tctx->bps == 0)
839 g_snprintf (buffer, BUF_TINY, _("Time: %s"), buffer2);
840 else
842 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
843 g_snprintf (buffer, BUF_TINY, _("Time: %s (%s)"), buffer2, buffer4);
847 label_set_text (ui->time_label, buffer);
849 size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
850 if (!file_op_compute_totals)
851 g_snprintf (buffer, BUF_TINY, _(" Total: %s "), buffer2);
852 else
854 size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
855 g_snprintf (buffer, BUF_TINY, _(" Total: %s/%s "), buffer2, buffer3);
858 label_set_text (ui->total_bytes_label, buffer);
861 /* }}} */
863 /* --------------------------------------------------------------------------------------------- */
865 void
866 file_progress_show_source (FileOpContext * ctx, const vfs_path_t * s_vpath)
868 FileOpContextUI *ui;
870 g_return_if_fail (ctx != NULL);
871 g_return_if_fail (ctx->ui != NULL);
873 ui = ctx->ui;
875 if (s_vpath != NULL)
877 char *s;
879 s = vfs_path_tokens_get (s_vpath, -1, 1);
880 label_set_text (ui->file_label[0], _("Source"));
881 label_set_text (ui->file_string[0], truncFileString (ui, s));
882 g_free (s);
884 else
886 label_set_text (ui->file_label[0], "");
887 label_set_text (ui->file_string[0], "");
891 /* --------------------------------------------------------------------------------------------- */
893 void
894 file_progress_show_target (FileOpContext * ctx, const vfs_path_t * s_vpath)
896 FileOpContextUI *ui;
898 g_return_if_fail (ctx != NULL);
899 g_return_if_fail (ctx->ui != NULL);
901 ui = ctx->ui;
903 if (s_vpath != NULL)
905 char *s;
907 s = vfs_path_to_str (s_vpath);
908 label_set_text (ui->file_label[1], _("Target"));
909 label_set_text (ui->file_string[1], truncFileStringSecure (ui, s));
910 g_free (s);
912 else
914 label_set_text (ui->file_label[1], "");
915 label_set_text (ui->file_string[1], "");
919 /* --------------------------------------------------------------------------------------------- */
921 void
922 file_progress_show_deleting (FileOpContext * ctx, const char *s)
924 FileOpContextUI *ui;
926 g_return_if_fail (ctx != NULL);
927 g_return_if_fail (ctx->ui != NULL);
929 ui = ctx->ui;
930 label_set_text (ui->file_label[0], _("Deleting"));
931 label_set_text (ui->file_label[0], truncFileStringSecure (ui, s));
934 /* --------------------------------------------------------------------------------------------- */
936 FileProgressStatus
937 file_progress_real_query_replace (FileOpContext * ctx,
938 enum OperationMode mode, const char *destname,
939 struct stat *_s_stat, struct stat *_d_stat)
941 FileOpContextUI *ui;
943 g_return_val_if_fail (ctx != NULL, FILE_CONT);
944 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
946 ui = ctx->ui;
948 if (ui->replace_result < REPLACE_ALWAYS)
950 ui->replace_filename = destname;
951 ui->s_stat = _s_stat;
952 ui->d_stat = _d_stat;
953 ui->replace_result = overwrite_query_dialog (ctx, mode);
956 switch (ui->replace_result)
958 case REPLACE_UPDATE:
959 do_refresh ();
960 if (_s_stat->st_mtime > _d_stat->st_mtime)
961 return FILE_CONT;
962 else
963 return FILE_SKIP;
965 case REPLACE_SIZE:
966 do_refresh ();
967 if (_s_stat->st_size == _d_stat->st_size)
968 return FILE_SKIP;
969 else
970 return FILE_CONT;
972 case REPLACE_REGET:
973 /* Careful: we fall through and set do_append */
974 ctx->do_reget = _d_stat->st_size;
976 case REPLACE_APPEND:
977 ctx->do_append = TRUE;
979 case REPLACE_YES:
980 case REPLACE_ALWAYS:
981 do_refresh ();
982 return FILE_CONT;
983 case REPLACE_NO:
984 case REPLACE_NEVER:
985 do_refresh ();
986 return FILE_SKIP;
987 case REPLACE_ABORT:
988 default:
989 return FILE_ABORT;
993 /* --------------------------------------------------------------------------------------------- */
995 char *
996 file_mask_dialog (FileOpContext * ctx, FileOperation operation,
997 gboolean only_one,
998 const char *format, const void *text, const char *def_text, gboolean * do_bg)
1000 const size_t FMDY = 13;
1001 const size_t FMDX = 68;
1002 size_t fmd_xlen;
1004 /* buttons */
1005 const size_t gap = 1;
1006 size_t b0_len, b2_len;
1007 size_t b1_len = 0;
1009 int source_easy_patterns = easy_patterns;
1010 size_t i, len;
1011 char fmd_buf[BUF_MEDIUM];
1012 char *source_mask, *orig_mask, *dest_dir, *tmp;
1013 char *def_text_secure;
1014 int val;
1016 QuickWidget fmd_widgets[] = {
1017 /* 0 */ QUICK_BUTTON (42, 64, 10, FMDY, N_("&Cancel"), B_CANCEL, NULL),
1018 #ifdef ENABLE_BACKGROUND
1019 /* 1 */ QUICK_BUTTON (25, 64, 10, FMDY, N_("&Background"), B_USER, NULL),
1020 #define OFFSET 0
1021 #else
1022 #define OFFSET 1
1023 #endif /* ENABLE_BACKGROUND */
1024 /* 2 - OFFSET */
1025 QUICK_BUTTON (14, FMDX, 10, FMDY, N_("&OK"), B_ENTER, NULL),
1026 /* 3 - OFFSET */
1027 QUICK_CHECKBOX (42, FMDX, 8, FMDY, N_("&Stable Symlinks"), &ctx->stable_symlinks),
1028 /* 4 - OFFSET */
1029 QUICK_CHECKBOX (31, FMDX, 7, FMDY, N_("Di&ve into subdir if exists"),
1030 &ctx->dive_into_subdirs),
1031 /* 5 - OFFSET */
1032 QUICK_CHECKBOX (3, FMDX, 8, FMDY, N_("Preserve &attributes"), &ctx->op_preserve),
1033 /* 6 - OFFSET */
1034 QUICK_CHECKBOX (3, FMDX, 7, FMDY, N_("Follow &links"), &ctx->follow_links),
1035 /* 7 - OFFSET */
1036 QUICK_INPUT (3, FMDX, 6, FMDY, "", 58, 0, "input2", &dest_dir),
1037 /* 8 - OFFSET */
1038 QUICK_LABEL (3, FMDX, 5, FMDY, N_("to:")),
1039 /* 9 - OFFSET */
1040 QUICK_CHECKBOX (37, FMDX, 4, FMDY, N_("&Using shell patterns"), &source_easy_patterns),
1041 /* 10 - OFFSET */
1042 QUICK_INPUT (3, FMDX, 3, FMDY, easy_patterns ? "*" : "^(.*)$", 58, 0, "input-def",
1043 &source_mask),
1044 /* 11 - OFFSET */
1045 QUICK_LABEL (3, FMDX, 2, FMDY, fmd_buf),
1046 QUICK_END
1049 g_return_val_if_fail (ctx != NULL, NULL);
1051 #ifdef ENABLE_NLS
1052 /* buttons */
1053 for (i = 0; i <= 2 - OFFSET; i++)
1054 fmd_widgets[i].u.button.text = _(fmd_widgets[i].u.button.text);
1056 /* checkboxes */
1057 for (i = 3 - OFFSET; i <= 9 - OFFSET; i++)
1058 if (i != 7 - OFFSET)
1059 fmd_widgets[i].u.checkbox.text = _(fmd_widgets[i].u.checkbox.text);
1060 #endif /* !ENABLE_NLS */
1062 fmd_xlen = max (FMDX, (size_t) COLS * 2 / 3);
1064 len = str_term_width1 (fmd_widgets[6 - OFFSET].u.checkbox.text)
1065 + str_term_width1 (fmd_widgets[4 - OFFSET].u.checkbox.text) + 15;
1066 fmd_xlen = max (fmd_xlen, len);
1068 len = str_term_width1 (fmd_widgets[5 - OFFSET].u.checkbox.text)
1069 + str_term_width1 (fmd_widgets[3 - OFFSET].u.checkbox.text) + 15;
1070 fmd_xlen = max (fmd_xlen, len);
1072 /* buttons */
1073 b2_len = str_term_width1 (fmd_widgets[2 - OFFSET].u.button.text) + 6 + gap; /* OK */
1074 #ifdef ENABLE_BACKGROUND
1075 b1_len = str_term_width1 (fmd_widgets[1].u.button.text) + 4 + gap; /* Background */
1076 #endif
1077 b0_len = str_term_width1 (fmd_widgets[0].u.button.text) + 4; /* Cancel */
1078 len = b0_len + b1_len + b2_len;
1079 fmd_xlen = min (max (fmd_xlen, len + 6), (size_t) COLS);
1081 if (only_one)
1083 int flen;
1085 flen = str_term_width1 (format);
1086 i = fmd_xlen - flen - 4; /* FIXME */
1087 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc ((const char *) text, i));
1089 else
1091 g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1092 fmd_xlen = max (fmd_xlen, (size_t) str_term_width1 (fmd_buf) + 6);
1095 for (i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]); i > 0;)
1096 fmd_widgets[--i].x_divisions = fmd_xlen;
1098 i = (fmd_xlen - len) / 2;
1099 /* OK button */
1100 fmd_widgets[2 - OFFSET].relative_x = i;
1101 i += b2_len;
1102 #ifdef ENABLE_BACKGROUND
1103 /* Background button */
1104 fmd_widgets[1].relative_x = i;
1105 i += b1_len;
1106 #endif
1107 /* Cancel button */
1108 fmd_widgets[0].relative_x = i;
1110 #define chkbox_xpos(i) \
1111 fmd_widgets [i].relative_x = fmd_xlen - str_term_width1 (fmd_widgets [i].u.checkbox.text) - 6
1112 chkbox_xpos (3 - OFFSET);
1113 chkbox_xpos (4 - OFFSET);
1114 chkbox_xpos (9 - OFFSET);
1115 #undef chkbox_xpos
1117 /* inputs */
1118 fmd_widgets[7 - OFFSET].u.input.len = fmd_widgets[10 - OFFSET].u.input.len = fmd_xlen - 6;
1120 /* unselect checkbox if target filesystem don't support attributes */
1121 ctx->op_preserve = filegui__check_attrs_on_fs (def_text);
1123 /* filter out a possible password from def_text */
1125 vfs_path_t *vpath;
1127 vpath = vfs_path_from_str_flags (def_text, (only_one) ? VPF_NO_CANON : VPF_NONE);
1128 tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1129 vfs_path_free (vpath);
1131 if (source_easy_patterns)
1132 def_text_secure = strutils_glob_escape (tmp);
1133 else
1134 def_text_secure = strutils_regex_escape (tmp);
1135 g_free (tmp);
1137 /* destination */
1138 fmd_widgets[7 - OFFSET].u.input.text = def_text_secure;
1140 ctx->stable_symlinks = FALSE;
1141 *do_bg = FALSE;
1144 struct stat buf;
1145 vfs_path_t *vpath;
1147 QuickDialog Quick_input = {
1148 fmd_xlen, FMDY, -1, -1, op_names[operation],
1149 "[Mask Copy/Rename]", fmd_widgets, NULL, NULL, TRUE
1152 ask_file_mask:
1153 val = quick_dialog_skip (&Quick_input, 4);
1155 if (val == B_CANCEL)
1157 g_free (def_text_secure);
1158 return NULL;
1161 if (ctx->follow_links)
1162 ctx->stat_func = mc_stat;
1163 else
1164 ctx->stat_func = mc_lstat;
1166 if (ctx->op_preserve)
1168 ctx->preserve = TRUE;
1169 ctx->umask_kill = 0777777;
1170 ctx->preserve_uidgid = (geteuid () == 0);
1172 else
1174 int i2;
1175 ctx->preserve = ctx->preserve_uidgid = FALSE;
1176 i2 = umask (0);
1177 umask (i2);
1178 ctx->umask_kill = i2 ^ 0777777;
1181 if ((dest_dir == NULL) || (*dest_dir == '\0'))
1183 g_free (def_text_secure);
1184 g_free (source_mask);
1185 return dest_dir;
1188 ctx->search_handle = mc_search_new (source_mask, -1);
1190 if (ctx->search_handle == NULL)
1192 message (D_ERROR, MSG_ERROR, _("Invalid source pattern `%s'"), source_mask);
1193 g_free (dest_dir);
1194 g_free (source_mask);
1195 goto ask_file_mask;
1198 g_free (def_text_secure);
1199 g_free (source_mask);
1201 ctx->search_handle->is_case_sensitive = TRUE;
1202 if (source_easy_patterns)
1203 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1204 else
1205 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1207 tmp = dest_dir;
1208 dest_dir = tilde_expand (tmp);
1209 g_free (tmp);
1210 vpath = vfs_path_from_str (dest_dir);
1212 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1213 if (ctx->dest_mask == NULL)
1214 ctx->dest_mask = dest_dir;
1215 else
1216 ctx->dest_mask++;
1217 orig_mask = ctx->dest_mask;
1218 if (*ctx->dest_mask == '\0'
1219 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1220 && (!only_one
1221 || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1222 || (ctx->dive_into_subdirs
1223 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1224 || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1225 ctx->dest_mask = g_strdup ("\\0");
1226 else
1228 ctx->dest_mask = g_strdup (ctx->dest_mask);
1229 *orig_mask = '\0';
1231 if (!*dest_dir)
1233 g_free (dest_dir);
1234 dest_dir = g_strdup ("./");
1236 vfs_path_free (vpath);
1237 if (val == B_USER)
1238 *do_bg = TRUE;
1241 return dest_dir;
1244 /* --------------------------------------------------------------------------------------------- */