Code indentation.
[midnight-commander.git] / src / filemanager / filegui.c
blob009b2e701ceb62daac732b9ae707f5a0d081a0df
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, 2013
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 <aborodin@vmail.ru>, 2009, 2010, 2011, 2012, 2013
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(dlg, s) str_trunc (s, WIDGET (dlg)->cols - 10)
201 #define truncFileStringSecure(dlg, s) path_trunc (s, WIDGET (dlg)->cols - 10)
203 /*** file scope type declarations ****************************************************************/
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 WHLine *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 struct
265 Widget *w;
266 FileProgressStatus action;
267 const char *text;
268 button_flags_t flags;
269 int len;
270 } progress_buttons[] =
272 /* *INDENT-OFF* */
273 { NULL, FILE_SKIP, N_("&Skip"), NORMAL_BUTTON, -1 },
274 { NULL, FILE_SUSPEND, N_("S&uspend"), NORMAL_BUTTON, -1 },
275 { NULL, FILE_SUSPEND, N_("Con&tinue"), NORMAL_BUTTON, -1 },
276 { NULL, FILE_ABORT, N_("&Abort"), NORMAL_BUTTON, -1 }
277 /* *INDENT-ON* */
280 /* --------------------------------------------------------------------------------------------- */
281 /*** file scope functions ************************************************************************/
282 /* --------------------------------------------------------------------------------------------- */
284 static gboolean
285 filegui__check_attrs_on_fs (const char *fs_path)
287 STRUCT_STATVFS stfs;
289 if (!setup_copymove_persistent_attr)
290 return FALSE;
292 #if USE_STATVFS && defined(STAT_STATVFS)
293 if (statvfs_works () && statvfs (fs_path, &stfs) != 0)
294 return TRUE;
295 #else
296 if (STATFS (fs_path, &stfs) != 0)
297 return TRUE;
298 #endif
300 #if (USE_STATVFS && defined(HAVE_STRUCT_STATVFS_F_TYPE)) || \
301 (!USE_STATVFS && defined(HAVE_STRUCT_STATFS_F_TYPE))
302 switch ((filegui_nonattrs_fs_t) stfs.f_type)
304 case MSDOS_SUPER_MAGIC:
305 case NTFS_SB_MAGIC:
306 case PROC_SUPER_MAGIC:
307 case SMB_SUPER_MAGIC:
308 case NCP_SUPER_MAGIC:
309 case USBDEVICE_SUPER_MAGIC:
310 return FALSE;
311 default:
312 break;
314 #elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
315 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdos") == 0
316 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdosfs") == 0
317 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
318 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "procfs") == 0
319 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
320 || strstr (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fusefs") != NULL)
321 return FALSE;
322 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
323 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "pcfs") == 0
324 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
325 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "proc") == 0
326 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
327 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fuse") == 0)
328 return FALSE;
329 #endif
331 return TRUE;
334 /* --------------------------------------------------------------------------------------------- */
336 static void
337 file_frmt_time (char *buffer, double eta_secs)
339 int eta_hours, eta_mins, eta_s;
340 eta_hours = eta_secs / (60 * 60);
341 eta_mins = (eta_secs - (eta_hours * 60 * 60)) / 60;
342 eta_s = eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
343 g_snprintf (buffer, BUF_TINY, _("%d:%02d.%02d"), eta_hours, eta_mins, eta_s);
346 /* --------------------------------------------------------------------------------------------- */
348 static void
349 file_eta_prepare_for_show (char *buffer, double eta_secs, gboolean always_show)
351 char _fmt_buff[BUF_TINY];
352 if (eta_secs <= 0.5 && !always_show)
354 *buffer = '\0';
355 return;
357 if (eta_secs <= 0.5)
358 eta_secs = 1;
359 file_frmt_time (_fmt_buff, eta_secs);
360 g_snprintf (buffer, BUF_TINY, _("ETA %s"), _fmt_buff);
363 /* --------------------------------------------------------------------------------------------- */
365 static void
366 file_bps_prepare_for_show (char *buffer, long bps)
368 if (bps > 1024 * 1024)
370 g_snprintf (buffer, BUF_TINY, _("%.2f MB/s"), bps / (1024 * 1024.0));
372 else if (bps > 1024)
374 g_snprintf (buffer, BUF_TINY, _("%.2f KB/s"), bps / 1024.0);
376 else if (bps > 1)
378 g_snprintf (buffer, BUF_TINY, _("%ld B/s"), bps);
380 else
381 *buffer = '\0';
384 /* --------------------------------------------------------------------------------------------- */
386 * FIXME: probably it is better to replace this with quick dialog machinery,
387 * but actually I'm not familiar with it and have not much time :(
388 * alex
390 static replace_action_t
391 overwrite_query_dialog (FileOpContext * ctx, enum OperationMode mode)
393 #define ADD_RD_BUTTON(i, ypos) \
394 add_widget_autopos (ui->replace_dlg, \
395 button_new (ypos, rd_widgets [i].xpos, rd_widgets [i].value, \
396 NORMAL_BUTTON, rd_widgets [i].text, NULL), \
397 rd_widgets [i].pos_flags, ui->replace_dlg->current->data)
399 #define ADD_RD_LABEL(i, p1, p2, ypos) \
400 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2); \
401 label2 = WIDGET (label_new (ypos, rd_widgets [i].xpos, buffer)); \
402 add_widget_autopos (ui->replace_dlg, label2, rd_widgets [i].pos_flags, \
403 ui->replace_dlg->current != NULL ? ui->replace_dlg->current->data : NULL)
405 /* dialog sizes */
406 const int rd_ylen = 1;
407 int rd_xlen = 60;
408 int y = 2;
409 unsigned long yes_id;
411 struct
413 const char *text;
414 int ypos, xpos;
415 widget_pos_flags_t pos_flags;
416 int value; /* 0 for labels */
417 } rd_widgets[] =
419 /* *INDENT-OFF* */
420 /* 0 */
421 { N_("Target file already exists!"), 3, 4, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
422 /* 1 */
423 { "%s", 4, 4, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
424 /* 2 */
425 { N_("New : %s, size %s"), 6, 4, WPOS_KEEP_DEFAULT, 0 },
426 /* 3 */
427 { N_("Existing: %s, size %s"), 7, 4, WPOS_KEEP_DEFAULT, 0 },
428 /* 4 */
429 { N_("Overwrite this target?"), 9, 4, WPOS_KEEP_DEFAULT, 0 },
430 /* 5 */
431 { N_("&Yes"), 9, 28, WPOS_KEEP_DEFAULT, REPLACE_YES },
432 /* 6 */
433 { N_("&No"), 9, 37, WPOS_KEEP_DEFAULT, REPLACE_NO },
434 /* 7 */
435 { N_("A&ppend"), 9, 45, WPOS_KEEP_DEFAULT, REPLACE_APPEND },
436 /* 8 */
437 { N_("&Reget"), 10, 28, WPOS_KEEP_DEFAULT, REPLACE_REGET },
438 /* 9 */
439 { N_("Overwrite all targets?"), 11, 4, WPOS_KEEP_DEFAULT, 0 },
440 /* 10 */
441 { N_("A&ll"), 11, 28, WPOS_KEEP_DEFAULT, REPLACE_ALWAYS },
442 /* 11 */
443 { N_("&Update"), 11, 36, WPOS_KEEP_DEFAULT, REPLACE_UPDATE },
444 /* 12 */
445 { N_("Non&e"), 11, 47, WPOS_KEEP_DEFAULT, REPLACE_NEVER },
446 /* 13 */
447 { N_("If &size differs"), 12, 28, WPOS_KEEP_DEFAULT, REPLACE_SIZE },
448 /* 14 */
449 { N_("&Abort"), 14, 25, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, REPLACE_ABORT }
450 /* *INDENT-ON* */
453 const size_t num = G_N_ELEMENTS (rd_widgets);
454 int *widgets_len;
456 FileOpContextUI *ui = ctx->ui;
458 char buffer[BUF_SMALL];
459 char fsize_buffer[BUF_SMALL];
460 Widget *label1, *label2;
461 const char *title;
462 vfs_path_t *stripped_vpath;
463 const char *stripped_name;
464 char *stripped_name_orig;
465 int result;
467 widgets_len = g_new0 (int, num);
469 if (mode == Foreground)
470 title = _("File exists");
471 else
472 title = _("Background process: File exists");
474 stripped_vpath = vfs_path_from_str (ui->replace_filename);
475 stripped_name = stripped_name_orig =
476 vfs_path_to_str_flags (stripped_vpath, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
477 vfs_path_free (stripped_vpath);
480 size_t i;
481 int l1, l2, l, row;
482 int stripped_name_len;
484 for (i = 0; i < num; i++)
486 #ifdef ENABLE_NLS
487 if (i != 1) /* skip filename */
488 rd_widgets[i].text = _(rd_widgets[i].text);
489 #endif /* ENABLE_NLS */
490 widgets_len[i] = str_term_width1 (rd_widgets[i].text);
494 * longest of "Overwrite..." labels
495 * (assume "Target date..." are short enough)
497 l1 = max (widgets_len[9], widgets_len[4]);
499 /* longest of button rows */
500 l = l2 = 0;
501 row = 0;
502 for (i = 1; i < num - 1; i++)
503 if (rd_widgets[i].value != 0)
505 if (row != rd_widgets[i].ypos)
507 row = rd_widgets[i].ypos;
508 l2 = max (l2, l);
509 l = 0;
511 l += widgets_len[i] + 4;
514 l2 = max (l2, l); /* last row */
515 rd_xlen = max (rd_xlen, l1 + l2 + 8);
516 /* rd_xlen = max (rd_xlen, str_term_width1 (title) + 2); */
517 stripped_name_len = str_term_width1 (stripped_name);
518 rd_xlen = max (rd_xlen, min (COLS, stripped_name_len + 8));
520 /* Now place widgets */
521 l1 += 5; /* start of first button in the row */
522 l = l1;
523 row = 0;
524 for (i = 2; i < num - 1; i++)
525 if (rd_widgets[i].value != 0)
527 if (row != rd_widgets[i].ypos)
529 row = rd_widgets[i].ypos;
530 l = l1;
532 rd_widgets[i].xpos = l;
533 l += widgets_len[i] + 4;
537 /* FIXME - missing help node */
538 ui->replace_dlg =
539 create_dlg (TRUE, 0, 0, rd_ylen, rd_xlen, alarm_colors, NULL, NULL, "[Replace]", title,
540 DLG_CENTER);
542 /* prompt */
543 ADD_RD_LABEL (0, "", "", y++);
544 /* file name */
545 ADD_RD_LABEL (1, "", "", y++);
546 label1 = label2;
548 add_widget (ui->replace_dlg, hline_new (y++, -1, -1));
550 /* source date and size */
551 size_trunc_len (fsize_buffer, sizeof (fsize_buffer), ui->s_stat->st_size, -1,
552 panels_options.kilobyte_si);
553 ADD_RD_LABEL (2, file_date (ui->s_stat->st_mtime), fsize_buffer, y++);
554 rd_xlen = max (rd_xlen, label2->cols + 8);
555 /* destination date and size */
556 size_trunc_len (fsize_buffer, sizeof (fsize_buffer), ui->d_stat->st_size, -1,
557 panels_options.kilobyte_si);
558 ADD_RD_LABEL (3, file_date (ui->d_stat->st_mtime), fsize_buffer, y++);
559 rd_xlen = max (rd_xlen, label2->cols + 8);
561 add_widget (ui->replace_dlg, hline_new (y++, -1, -1));
563 ADD_RD_LABEL (4, 0, 0, y); /* Overwrite this target? */
564 yes_id = ADD_RD_BUTTON (5, y); /* Yes */
565 ADD_RD_BUTTON (6, y); /* No */
567 /* "this target..." widgets */
568 if (!S_ISDIR (ui->d_stat->st_mode))
570 ADD_RD_BUTTON (7, y++); /* Append */
572 if ((ctx->operation == OP_COPY) && (ui->d_stat->st_size != 0)
573 && (ui->s_stat->st_size > ui->d_stat->st_size))
574 ADD_RD_BUTTON (8, y++); /* Reget */
577 add_widget (ui->replace_dlg, hline_new (y++, -1, -1));
579 ADD_RD_LABEL (9, 0, 0, y); /* Overwrite all targets? */
580 ADD_RD_BUTTON (10, y); /* All" */
581 ADD_RD_BUTTON (11, y); /* Update */
582 ADD_RD_BUTTON (12, y++); /* None */
583 ADD_RD_BUTTON (13, y++); /* If size differs */
585 add_widget (ui->replace_dlg, hline_new (y++, -1, -1));
587 ADD_RD_BUTTON (14, y); /* Abort */
589 label_set_text (LABEL (label1), str_trunc (stripped_name, rd_xlen - 8));
590 dlg_set_size (ui->replace_dlg, y + 3, rd_xlen);
591 dlg_select_by_id (ui->replace_dlg, yes_id);
592 result = run_dlg (ui->replace_dlg);
593 destroy_dlg (ui->replace_dlg);
595 g_free (widgets_len);
596 g_free (stripped_name_orig);
598 return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
599 #undef ADD_RD_LABEL
600 #undef ADD_RD_BUTTON
603 /* --------------------------------------------------------------------------------------------- */
605 static gboolean
606 is_wildcarded (char *p)
608 for (; *p; p++)
610 if (*p == '*')
611 return TRUE;
612 if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
613 return TRUE;
615 return FALSE;
618 /* --------------------------------------------------------------------------------------------- */
620 static void
621 place_progress_buttons (WDialog * h, gboolean suspended)
623 const size_t i = suspended ? 2 : 1;
624 Widget *w = WIDGET (h);
625 int buttons_width;
627 buttons_width = 2 + progress_buttons[0].len + progress_buttons[3].len;
628 buttons_width += progress_buttons[i].len;
629 button_set_text (BUTTON (progress_buttons[i].w), progress_buttons[i].text);
631 progress_buttons[0].w->x = w->x + (w->cols - buttons_width) / 2;
632 progress_buttons[i].w->x = progress_buttons[0].w->x + progress_buttons[0].len + 1;
633 progress_buttons[3].w->x = progress_buttons[i].w->x + progress_buttons[i].len + 1;
636 /* --------------------------------------------------------------------------------------------- */
638 static int
639 progress_button_callback (WButton * button, int action)
641 (void) button;
642 (void) action;
644 /* don't close dialog in any case */
645 return 0;
648 /* --------------------------------------------------------------------------------------------- */
649 /*** public functions ****************************************************************************/
650 /* --------------------------------------------------------------------------------------------- */
652 FileProgressStatus
653 check_progress_buttons (FileOpContext * ctx)
655 int c;
656 Gpm_Event event;
657 FileOpContextUI *ui;
659 if (ctx == NULL || ctx->ui == NULL)
660 return FILE_CONT;
662 ui = ctx->ui;
664 get_event:
665 event.x = -1; /* Don't show the GPM cursor */
666 c = tty_get_event (&event, FALSE, ctx->suspended);
667 if (c == EV_NONE)
668 return FILE_CONT;
670 /* Reinitialize to avoid old values after events other than selecting a button */
671 ui->op_dlg->ret_value = FILE_CONT;
673 dlg_process_event (ui->op_dlg, c, &event);
674 switch (ui->op_dlg->ret_value)
676 case FILE_SKIP:
677 if (ctx->suspended)
679 /* redraw dialog in case of Skip after Suspend */
680 place_progress_buttons (ui->op_dlg, FALSE);
681 dlg_redraw (ui->op_dlg);
683 ctx->suspended = FALSE;
684 return FILE_SKIP;
685 case B_CANCEL:
686 case FILE_ABORT:
687 ctx->suspended = FALSE;
688 return FILE_ABORT;
689 case FILE_SUSPEND:
690 ctx->suspended = !ctx->suspended;
691 place_progress_buttons (ui->op_dlg, ctx->suspended);
692 dlg_redraw (ui->op_dlg);
693 /* fallthrough */
694 default:
695 if (ctx->suspended)
696 goto get_event;
697 return FILE_CONT;
701 /* --------------------------------------------------------------------------------------------- */
702 /* {{{ File progress display routines */
704 void
705 file_op_context_create_ui (FileOpContext * ctx, gboolean with_eta,
706 filegui_dialog_type_t dialog_type)
708 FileOpContextUI *ui;
709 int buttons_width;
710 int dlg_width = 58, dlg_height = 17;
711 int y = 2, x = 3;
713 if (ctx == NULL || ctx->ui != NULL)
714 return;
716 #ifdef ENABLE_NLS
717 if (progress_buttons[0].len == -1)
719 size_t i;
721 for (i = 0; i < G_N_ELEMENTS (progress_buttons); i++)
722 progress_buttons[i].text = _(progress_buttons[i].text);
724 #endif
726 ctx->dialog_type = dialog_type;
727 ctx->recursive_result = RECURSIVE_YES;
728 ctx->ui = g_new0 (FileOpContextUI, 1);
730 ui = ctx->ui;
731 ui->replace_result = REPLACE_YES;
732 ui->showing_eta = with_eta && ctx->progress_totals_computed;
733 ui->showing_bps = with_eta;
735 ui->op_dlg =
736 create_dlg (TRUE, 0, 0, dlg_height, dlg_width, dialog_colors, NULL, NULL, NULL,
737 op_names[ctx->operation], DLG_CENTER);
739 ui->file_label[0] = label_new (y++, x, "");
740 add_widget (ui->op_dlg, ui->file_label[0]);
742 ui->file_string[0] = label_new (y++, x, "");
743 add_widget (ui->op_dlg, ui->file_string[0]);
745 ui->file_label[1] = label_new (y++, x, "");
746 add_widget (ui->op_dlg, ui->file_label[1]);
748 ui->file_string[1] = label_new (y++, x, "");
749 add_widget (ui->op_dlg, ui->file_string[1]);
751 ui->progress_file_gauge = gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
752 if (!classic_progressbar && (current_panel == right_panel))
753 ui->progress_file_gauge->from_left_to_right = FALSE;
754 add_widget_autopos (ui->op_dlg, ui->progress_file_gauge, WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
756 ui->progress_file_label = label_new (y++, x, "");
757 add_widget (ui->op_dlg, ui->progress_file_label);
759 if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
761 ui->total_bytes_label = hline_new (y++, -1, -1);
762 add_widget (ui->op_dlg, ui->total_bytes_label);
764 if (ctx->progress_totals_computed)
766 ui->progress_total_gauge =
767 gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
768 if (!classic_progressbar && (current_panel == right_panel))
769 ui->progress_total_gauge->from_left_to_right = FALSE;
770 add_widget_autopos (ui->op_dlg, ui->progress_total_gauge,
771 WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
774 ui->total_files_processed_label = label_new (y++, x, "");
775 add_widget (ui->op_dlg, ui->total_files_processed_label);
777 ui->time_label = label_new (y++, x, "");
778 add_widget (ui->op_dlg, ui->time_label);
781 add_widget (ui->op_dlg, hline_new (y++, -1, -1));
783 progress_buttons[0].w = WIDGET (button_new (y, 0, progress_buttons[0].action,
784 progress_buttons[0].flags, progress_buttons[0].text,
785 progress_button_callback));
786 if (progress_buttons[0].len == -1)
787 progress_buttons[0].len = button_get_len (BUTTON (progress_buttons[0].w));
789 progress_buttons[1].w = WIDGET (button_new (y, 0, progress_buttons[1].action,
790 progress_buttons[1].flags, progress_buttons[1].text,
791 progress_button_callback));
792 if (progress_buttons[1].len == -1)
793 progress_buttons[1].len = button_get_len (BUTTON (progress_buttons[1].w));
795 if (progress_buttons[2].len == -1)
797 /* create and destroy button to get it length */
798 progress_buttons[2].w = WIDGET (button_new (y, 0, progress_buttons[2].action,
799 progress_buttons[2].flags,
800 progress_buttons[2].text,
801 progress_button_callback));
802 progress_buttons[2].len = button_get_len (BUTTON (progress_buttons[2].w));
803 send_message (progress_buttons[2].w, NULL, MSG_DESTROY, 0, NULL);
804 g_free (progress_buttons[2].w);
806 progress_buttons[2].w = progress_buttons[1].w;
808 progress_buttons[3].w = WIDGET (button_new (y, 0, progress_buttons[3].action,
809 progress_buttons[3].flags, progress_buttons[3].text,
810 NULL));
811 if (progress_buttons[3].len == -1)
812 progress_buttons[3].len = button_get_len (BUTTON (progress_buttons[3].w));
814 add_widget (ui->op_dlg, progress_buttons[0].w);
815 add_widget (ui->op_dlg, progress_buttons[1].w);
816 add_widget (ui->op_dlg, progress_buttons[3].w);
818 buttons_width = 2 +
819 progress_buttons[0].len + max (progress_buttons[1].len, progress_buttons[2].len) +
820 progress_buttons[3].len;
822 /* adjust dialog sizes */
823 dlg_set_size (ui->op_dlg, y + 3, max (COLS * 2 / 3, buttons_width + 6));
825 place_progress_buttons (ui->op_dlg, FALSE);
827 dlg_select_widget (progress_buttons[0].w);
829 /* We will manage the dialog without any help, that's why
830 we have to call init_dlg */
831 init_dlg (ui->op_dlg);
834 /* --------------------------------------------------------------------------------------------- */
836 void
837 file_op_context_destroy_ui (FileOpContext * ctx)
839 if (ctx != NULL && ctx->ui != NULL)
841 FileOpContextUI *ui = (FileOpContextUI *) ctx->ui;
843 dlg_run_done (ui->op_dlg);
844 destroy_dlg (ui->op_dlg);
845 g_free (ui);
846 ctx->ui = NULL;
850 /* --------------------------------------------------------------------------------------------- */
852 show progressbar for file
855 void
856 file_progress_show (FileOpContext * ctx, off_t done, off_t total,
857 const char *stalled_msg, gboolean force_update)
859 FileOpContextUI *ui;
860 char buffer[BUF_TINY];
861 char buffer2[BUF_TINY];
862 char buffer3[BUF_TINY];
864 if (!verbose || ctx == NULL || ctx->ui == NULL)
865 return;
867 ui = ctx->ui;
869 if (total == 0)
871 gauge_show (ui->progress_file_gauge, 0);
872 return;
875 gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
876 gauge_show (ui->progress_file_gauge, 1);
878 if (!force_update)
879 return;
881 if (ui->showing_eta && ctx->eta_secs > 0.5)
883 file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
884 if (ctx->bps == 0)
885 g_snprintf (buffer, BUF_TINY, "%s %s", buffer2, stalled_msg);
886 else
888 file_bps_prepare_for_show (buffer3, ctx->bps);
889 g_snprintf (buffer, BUF_TINY, "%s (%s) %s", buffer2, buffer3, stalled_msg);
892 else
894 g_snprintf (buffer, BUF_TINY, "%s", stalled_msg);
897 label_set_text (ui->progress_file_label, buffer);
900 /* --------------------------------------------------------------------------------------------- */
902 void
903 file_progress_show_count (FileOpContext * ctx, size_t done, size_t total)
905 char buffer[BUF_TINY];
906 FileOpContextUI *ui;
908 if (ctx == NULL || ctx->ui == NULL)
909 return;
911 ui = ctx->ui;
912 if (ctx->progress_totals_computed)
913 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu/%zu"), done, total);
914 else
915 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu"), done);
916 label_set_text (ui->total_files_processed_label, buffer);
919 /* --------------------------------------------------------------------------------------------- */
921 void
922 file_progress_show_total (FileOpTotalContext * tctx, FileOpContext * ctx, uintmax_t copied_bytes,
923 gboolean show_summary)
925 char buffer[BUF_TINY];
926 char buffer2[BUF_TINY];
927 char buffer3[BUF_TINY];
928 char buffer4[BUF_TINY];
929 struct timeval tv_current;
930 FileOpContextUI *ui;
932 if (ctx == NULL || ctx->ui == NULL)
933 return;
935 ui = ctx->ui;
937 if (ctx->progress_totals_computed)
939 if (ctx->progress_bytes == 0)
940 gauge_show (ui->progress_total_gauge, 0);
941 else
943 gauge_set_value (ui->progress_total_gauge, 1024,
944 (int) (1024 * copied_bytes / ctx->progress_bytes));
945 gauge_show (ui->progress_total_gauge, 1);
949 if (!show_summary && tctx->bps == 0)
950 return;
952 gettimeofday (&tv_current, NULL);
953 file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec);
955 if (ctx->progress_totals_computed)
957 file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
958 if (tctx->bps == 0)
959 g_snprintf (buffer, BUF_TINY, _("Time: %s %s"), buffer2, buffer3);
960 else
962 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
963 g_snprintf (buffer, BUF_TINY, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
966 else
968 if (tctx->bps == 0)
969 g_snprintf (buffer, BUF_TINY, _("Time: %s"), buffer2);
970 else
972 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
973 g_snprintf (buffer, BUF_TINY, _("Time: %s (%s)"), buffer2, buffer4);
977 label_set_text (ui->time_label, buffer);
979 size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
980 if (!ctx->progress_totals_computed)
981 g_snprintf (buffer, BUF_TINY, _(" Total: %s "), buffer2);
982 else
984 size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
985 g_snprintf (buffer, BUF_TINY, _(" Total: %s/%s "), buffer2, buffer3);
988 hline_set_text (ui->total_bytes_label, buffer);
991 /* }}} */
993 /* --------------------------------------------------------------------------------------------- */
995 void
996 file_progress_show_source (FileOpContext * ctx, const vfs_path_t * s_vpath)
998 FileOpContextUI *ui;
1000 if (ctx == NULL || ctx->ui == NULL)
1001 return;
1003 ui = ctx->ui;
1005 if (s_vpath != NULL)
1007 char *s;
1009 s = vfs_path_tokens_get (s_vpath, -1, 1);
1010 label_set_text (ui->file_label[0], _("Source"));
1011 label_set_text (ui->file_string[0], truncFileString (ui->op_dlg, s));
1012 g_free (s);
1014 else
1016 label_set_text (ui->file_label[0], "");
1017 label_set_text (ui->file_string[0], "");
1021 /* --------------------------------------------------------------------------------------------- */
1023 void
1024 file_progress_show_target (FileOpContext * ctx, const vfs_path_t * s_vpath)
1026 FileOpContextUI *ui;
1028 if (ctx == NULL || ctx->ui == NULL)
1029 return;
1031 ui = ctx->ui;
1033 if (s_vpath != NULL)
1035 char *s;
1037 s = vfs_path_to_str (s_vpath);
1038 label_set_text (ui->file_label[1], _("Target"));
1039 label_set_text (ui->file_string[1], truncFileStringSecure (ui->op_dlg, s));
1040 g_free (s);
1042 else
1044 label_set_text (ui->file_label[1], "");
1045 label_set_text (ui->file_string[1], "");
1049 /* --------------------------------------------------------------------------------------------- */
1051 void
1052 file_progress_show_deleting (FileOpContext * ctx, const char *s)
1054 FileOpContextUI *ui;
1056 if (ctx == NULL || ctx->ui == NULL)
1057 return;
1059 ui = ctx->ui;
1060 label_set_text (ui->file_label[0], _("Deleting"));
1061 label_set_text (ui->file_label[0], truncFileStringSecure (ui->op_dlg, s));
1064 /* --------------------------------------------------------------------------------------------- */
1066 FileProgressStatus
1067 file_progress_real_query_replace (FileOpContext * ctx,
1068 enum OperationMode mode, const char *destname,
1069 struct stat * _s_stat, struct stat * _d_stat)
1071 FileOpContextUI *ui;
1073 if (ctx == NULL || ctx->ui == NULL)
1074 return FILE_CONT;
1076 ui = ctx->ui;
1078 if (ui->replace_result < REPLACE_ALWAYS)
1080 ui->replace_filename = destname;
1081 ui->s_stat = _s_stat;
1082 ui->d_stat = _d_stat;
1083 ui->replace_result = overwrite_query_dialog (ctx, mode);
1086 switch (ui->replace_result)
1088 case REPLACE_UPDATE:
1089 do_refresh ();
1090 if (_s_stat->st_mtime > _d_stat->st_mtime)
1091 return FILE_CONT;
1092 else
1093 return FILE_SKIP;
1095 case REPLACE_SIZE:
1096 do_refresh ();
1097 if (_s_stat->st_size == _d_stat->st_size)
1098 return FILE_SKIP;
1099 else
1100 return FILE_CONT;
1102 case REPLACE_REGET:
1103 /* Careful: we fall through and set do_append */
1104 ctx->do_reget = _d_stat->st_size;
1106 case REPLACE_APPEND:
1107 ctx->do_append = TRUE;
1109 case REPLACE_YES:
1110 case REPLACE_ALWAYS:
1111 do_refresh ();
1112 return FILE_CONT;
1113 case REPLACE_NO:
1114 case REPLACE_NEVER:
1115 do_refresh ();
1116 return FILE_SKIP;
1117 case REPLACE_ABORT:
1118 default:
1119 return FILE_ABORT;
1123 /* --------------------------------------------------------------------------------------------- */
1125 char *
1126 file_mask_dialog (FileOpContext * ctx, FileOperation operation,
1127 gboolean only_one,
1128 const char *format, const void *text, const char *def_text, gboolean * do_bg)
1130 size_t fmd_xlen;
1131 vfs_path_t *vpath;
1132 int source_easy_patterns = easy_patterns;
1133 char fmd_buf[BUF_MEDIUM];
1134 char *dest_dir, *tmp;
1135 char *def_text_secure;
1137 if (ctx == NULL)
1138 return NULL;
1140 /* unselect checkbox if target filesystem don't support attributes */
1141 ctx->op_preserve = filegui__check_attrs_on_fs (def_text);
1142 ctx->stable_symlinks = FALSE;
1143 *do_bg = FALSE;
1145 /* filter out a possible password from def_text */
1146 vpath = vfs_path_from_str_flags (def_text, only_one ? VPF_NO_CANON : VPF_NONE);
1147 tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1148 vfs_path_free (vpath);
1150 if (source_easy_patterns)
1151 def_text_secure = strutils_glob_escape (tmp);
1152 else
1153 def_text_secure = strutils_regex_escape (tmp);
1154 g_free (tmp);
1156 if (only_one)
1158 int format_len, text_len;
1159 int max_len;
1161 format_len = str_term_width1 (format);
1162 text_len = str_term_width1 (text);
1163 max_len = COLS - 2 - 6;
1165 if (format_len + text_len <= max_len)
1167 fmd_xlen = format_len + text_len + 6;
1168 fmd_xlen = max (fmd_xlen, 68);
1170 else
1172 text = str_trunc ((const char *) text, max_len - format_len);
1173 fmd_xlen = max_len + 6;
1176 g_snprintf (fmd_buf, sizeof (fmd_buf), format, (const char *) text);
1178 else
1180 fmd_xlen = COLS * 2 / 3;
1181 fmd_xlen = max (fmd_xlen, 68);
1182 g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1186 char *source_mask, *orig_mask;
1187 int val;
1188 struct stat buf;
1190 quick_widget_t quick_widgets[] = {
1191 /* *INDENT-OFF* */
1192 QUICK_LABELED_INPUT (fmd_buf, input_label_above,
1193 easy_patterns ? "*" : "^(.*)$", "input-def", &source_mask,
1194 NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1195 QUICK_START_COLUMNS,
1196 QUICK_SEPARATOR (FALSE),
1197 QUICK_NEXT_COLUMN,
1198 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns, NULL),
1199 QUICK_STOP_COLUMNS,
1200 QUICK_LABELED_INPUT (N_("to:"), input_label_above,
1201 def_text_secure, "input2", &dest_dir, NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1202 QUICK_SEPARATOR (TRUE),
1203 QUICK_START_COLUMNS,
1204 QUICK_CHECKBOX (N_("Follow &links"), &ctx->follow_links, NULL),
1205 QUICK_CHECKBOX (N_("Preserve &attributes"), &ctx->op_preserve, NULL),
1206 QUICK_NEXT_COLUMN,
1207 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx->dive_into_subdirs, NULL),
1208 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx->stable_symlinks, NULL),
1209 QUICK_STOP_COLUMNS,
1210 QUICK_START_BUTTONS (TRUE, TRUE),
1211 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
1212 #ifdef ENABLE_BACKGROUND
1213 QUICK_BUTTON (N_("&Background"), B_USER, NULL, NULL),
1214 #endif /* ENABLE_BACKGROUND */
1215 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1216 QUICK_END
1217 /* *INDENT-ON* */
1220 quick_dialog_t qdlg = {
1221 -1, -1, fmd_xlen,
1222 op_names[operation], "[Mask Copy/Rename]",
1223 quick_widgets, NULL, NULL
1226 ask_file_mask:
1227 val = quick_dialog_skip (&qdlg, 4);
1229 if (val == B_CANCEL)
1231 g_free (def_text_secure);
1232 return NULL;
1235 if (ctx->follow_links)
1236 ctx->stat_func = mc_stat;
1237 else
1238 ctx->stat_func = mc_lstat;
1240 if (ctx->op_preserve)
1242 ctx->preserve = TRUE;
1243 ctx->umask_kill = 0777777;
1244 ctx->preserve_uidgid = (geteuid () == 0);
1246 else
1248 int i2;
1250 ctx->preserve = ctx->preserve_uidgid = FALSE;
1251 i2 = umask (0);
1252 umask (i2);
1253 ctx->umask_kill = i2 ^ 0777777;
1256 if ((dest_dir == NULL) || (*dest_dir == '\0'))
1258 g_free (def_text_secure);
1259 g_free (source_mask);
1260 return dest_dir;
1263 ctx->search_handle = mc_search_new (source_mask, -1);
1265 if (ctx->search_handle == NULL)
1267 message (D_ERROR, MSG_ERROR, _("Invalid source pattern `%s'"), source_mask);
1268 g_free (dest_dir);
1269 g_free (source_mask);
1270 goto ask_file_mask;
1273 g_free (def_text_secure);
1274 g_free (source_mask);
1276 ctx->search_handle->is_case_sensitive = TRUE;
1277 if (source_easy_patterns)
1278 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1279 else
1280 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1282 tmp = dest_dir;
1283 dest_dir = tilde_expand (tmp);
1284 g_free (tmp);
1285 vpath = vfs_path_from_str (dest_dir);
1287 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1288 if (ctx->dest_mask == NULL)
1289 ctx->dest_mask = dest_dir;
1290 else
1291 ctx->dest_mask++;
1292 orig_mask = ctx->dest_mask;
1293 if (*ctx->dest_mask == '\0'
1294 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1295 && (!only_one
1296 || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1297 || (ctx->dive_into_subdirs
1298 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1299 || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1300 ctx->dest_mask = g_strdup ("\\0");
1301 else
1303 ctx->dest_mask = g_strdup (ctx->dest_mask);
1304 *orig_mask = '\0';
1306 if (*dest_dir == '\0')
1308 g_free (dest_dir);
1309 dest_dir = g_strdup ("./");
1311 vfs_path_free (vpath);
1312 if (val == B_USER)
1313 *do_bg = TRUE;
1316 return dest_dir;
1319 /* --------------------------------------------------------------------------------------------- */