Changes to handle vfs_path_t object:
[midnight-commander.git] / src / filemanager / filegui.c
blobd76753eee57c0f74103c4df68363bdf94c26c1ea
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
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
26 This file is part of the Midnight Commander.
28 The Midnight Commander is free software: you can redistribute it
29 and/or modify it under the terms of the GNU General Public License as
30 published by the Free Software Foundation, either version 3 of the License,
31 or (at your option) any later version.
33 The Midnight Commander is distributed in the hope that it will be useful,
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 GNU General Public License for more details.
38 You should have received a copy of the GNU General Public License
39 along with this program. If not, see <http://www.gnu.org/licenses/>.
43 * Please note that all dialogs used here must be safe for background
44 * operations.
47 /** \file filegui.c
48 * \brief Source: file management GUI for the text mode edition
51 /* {{{ Include files */
53 #include <config.h>
55 #include <errno.h>
56 #include <ctype.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
62 #if defined(STAT_STATVFS) \
63 && (defined(HAVE_STRUCT_STATVFS_F_BASETYPE) \
64 || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME))
65 #include <sys/statvfs.h>
66 #define STRUCT_STATFS struct statvfs
67 #define STATFS statvfs
68 #elif defined(HAVE_STATFS) && !defined(STAT_STATFS4)
69 #ifdef HAVE_SYS_VFS_H
70 #include <sys/vfs.h>
71 #elif defined(HAVE_SYS_MOUNT_H) && defined(HAVE_SYS_PARAM_H)
72 #include <sys/param.h>
73 #include <sys/mount.h>
74 #elif defined(HAVE_SYS_STATFS_H)
75 #include <sys/statfs.h>
76 #endif
77 #define STRUCT_STATFS struct statfs
78 #define STATFS statfs
79 #endif
81 #include <unistd.h>
83 #include "lib/global.h"
85 #include "lib/tty/key.h" /* tty_get_event */
86 #include "lib/mcconfig.h"
87 #include "lib/search.h"
88 #include "lib/vfs/vfs.h"
89 #include "lib/strescape.h"
90 #include "lib/strutil.h"
91 #include "lib/timefmt.h" /* file_date() */
92 #include "lib/util.h"
93 #include "lib/widget.h"
95 #include "src/setup.h" /* verbose */
97 #include "midnight.h"
98 #include "fileopctx.h" /* FILE_CONT */
100 #include "filegui.h"
102 /* }}} */
104 /*** global variables ****************************************************************************/
106 int classic_progressbar = 1;
108 /*** file scope macro definitions ****************************************************************/
110 /* Hack: the vfs code should not rely on this */
111 #define WITH_FULL_PATHS 1
113 #define truncFileString(ui, s) str_trunc (s, 52)
114 #define truncFileStringSecure(ui, s) path_trunc (s, 52)
116 /*** file scope type declarations ****************************************************************/
118 /* *INDENT-OFF* */
119 typedef enum {
120 MSDOS_SUPER_MAGIC = 0x4d44,
121 NTFS_SB_MAGIC = 0x5346544e,
122 FUSE_MAGIC = 0x65735546,
123 PROC_SUPER_MAGIC = 0x9fa0,
124 SMB_SUPER_MAGIC = 0x517B,
125 NCP_SUPER_MAGIC = 0x564c,
126 USBDEVICE_SUPER_MAGIC = 0x9fa2
127 } filegui_nonattrs_fs_t;
128 /* *INDENT-ON* */
130 /* Used for button result values */
131 typedef enum
133 REPLACE_YES = B_USER,
134 REPLACE_NO,
135 REPLACE_APPEND,
136 REPLACE_ALWAYS,
137 REPLACE_UPDATE,
138 REPLACE_NEVER,
139 REPLACE_ABORT,
140 REPLACE_SIZE,
141 REPLACE_REGET
142 } replace_action_t;
144 /* This structure describes the UI and internal data required by a file
145 * operation context.
147 typedef struct
149 /* ETA and bps */
150 gboolean showing_eta;
151 gboolean showing_bps;
153 /* Dialog and widgets for the operation progress window */
154 Dlg_head *op_dlg;
155 WLabel *file_string[2];
156 WLabel *file_label[2];
157 WGauge *progress_file_gauge;
158 WLabel *progress_file_label;
160 WGauge *progress_total_gauge;
162 WLabel *total_files_processed_label;
163 WLabel *time_label;
164 WLabel *total_bytes_label;
166 /* Query replace dialog */
167 Dlg_head *replace_dlg;
168 const char *replace_filename;
169 replace_action_t replace_result;
171 struct stat *s_stat, *d_stat;
172 } FileOpContextUI;
174 /*** file scope variables ************************************************************************/
176 /*** file scope functions ************************************************************************/
177 /* --------------------------------------------------------------------------------------------- */
179 static gboolean
180 filegui__check_attrs_on_fs (const char *fs_path)
182 #ifdef STATFS
183 STRUCT_STATFS stfs;
185 if (!setup_copymove_persistent_attr)
186 return FALSE;
188 if (STATFS (fs_path, &stfs) != 0)
189 return TRUE;
191 #ifdef __linux__
192 switch ((filegui_nonattrs_fs_t) stfs.f_type)
194 case MSDOS_SUPER_MAGIC:
195 case NTFS_SB_MAGIC:
196 case PROC_SUPER_MAGIC:
197 case SMB_SUPER_MAGIC:
198 case NCP_SUPER_MAGIC:
199 case USBDEVICE_SUPER_MAGIC:
200 return FALSE;
201 default:
202 break;
204 #elif defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) \
205 || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
206 if (!strcmp (stfs.f_fstypename, "msdos")
207 || !strcmp (stfs.f_fstypename, "msdosfs")
208 || !strcmp (stfs.f_fstypename, "ntfs")
209 || !strcmp (stfs.f_fstypename, "procfs")
210 || !strcmp (stfs.f_fstypename, "smbfs") || strstr (stfs.f_fstypename, "fusefs"))
211 return FALSE;
212 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
213 if (!strcmp (stfs.f_basetype, "pcfs")
214 || !strcmp (stfs.f_basetype, "ntfs")
215 || !strcmp (stfs.f_basetype, "proc")
216 || !strcmp (stfs.f_basetype, "smbfs") || !strcmp (stfs.f_basetype, "fuse"))
217 return FALSE;
218 #endif
219 #endif /* STATFS */
221 return TRUE;
224 /* --------------------------------------------------------------------------------------------- */
226 static void
227 file_frmt_time (char *buffer, double eta_secs)
229 int eta_hours, eta_mins, eta_s;
230 eta_hours = eta_secs / (60 * 60);
231 eta_mins = (eta_secs - (eta_hours * 60 * 60)) / 60;
232 eta_s = eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
233 g_snprintf (buffer, BUF_TINY, _("%d:%02d.%02d"), eta_hours, eta_mins, eta_s);
236 /* --------------------------------------------------------------------------------------------- */
238 static void
239 file_eta_prepare_for_show (char *buffer, double eta_secs, gboolean always_show)
241 char _fmt_buff[BUF_TINY];
242 if (eta_secs <= 0.5 && !always_show)
244 *buffer = '\0';
245 return;
247 if (eta_secs <= 0.5)
248 eta_secs = 1;
249 file_frmt_time (_fmt_buff, eta_secs);
250 g_snprintf (buffer, BUF_TINY, _("ETA %s"), _fmt_buff);
253 /* --------------------------------------------------------------------------------------------- */
255 static void
256 file_bps_prepare_for_show (char *buffer, long bps)
258 if (bps > 1024 * 1024)
260 g_snprintf (buffer, BUF_TINY, _("%.2f MB/s"), bps / (1024 * 1024.0));
262 else if (bps > 1024)
264 g_snprintf (buffer, BUF_TINY, _("%.2f KB/s"), bps / 1024.0);
266 else if (bps > 1)
268 g_snprintf (buffer, BUF_TINY, _("%ld B/s"), bps);
270 else
271 *buffer = '\0';
274 /* --------------------------------------------------------------------------------------------- */
276 * FIXME: probably it is better to replace this with quick dialog machinery,
277 * but actually I'm not familiar with it and have not much time :(
278 * alex
280 static replace_action_t
281 overwrite_query_dialog (FileOpContext * ctx, enum OperationMode mode)
283 #define ADD_RD_BUTTON(i) \
284 add_widget (ui->replace_dlg, \
285 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value, \
286 NORMAL_BUTTON, rd_widgets [i].text, 0))
288 #define ADD_RD_LABEL(i, p1, p2) \
289 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2); \
290 add_widget (ui->replace_dlg, label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer))
292 /* dialog sizes */
293 const int rd_ylen = 17;
294 int rd_xlen = 60;
296 struct
298 const char *text;
299 int ypos, xpos;
300 int value; /* 0 for labels */
301 } rd_widgets[] =
303 /* *INDENT-OFF* */
304 /* 0 */
305 { N_("Target file already exists!"), 3, 4, 0 },
306 /* 1 */
307 { "%s", 4, 4, 0 },
308 /* 2 */ /* cannot use PRIuMAX here; %llu is used instead */
309 { N_("Source date: %s, size %llu"), 6, 4, 0 },
310 /* 3 */ /* cannot use PRIuMAX here; %llu is used instead */
311 { N_("Target date: %s, size %llu"), 7, 4, 0 },
312 /* 4 */
313 { N_("&Abort"), 14, 25, REPLACE_ABORT },
314 /* 5 */
315 { N_("If &size differs"), 12, 28, REPLACE_SIZE },
316 /* 6 */
317 { N_("Non&e"), 11, 47, REPLACE_NEVER },
318 /* 7 */
319 { N_("&Update"), 11, 36, REPLACE_UPDATE },
320 /* 8 */
321 { N_("A&ll"), 11, 28, REPLACE_ALWAYS },
322 /* 9 */
323 { N_("Overwrite all targets?"), 11, 4, 0 },
324 /* 10 */
325 { N_("&Reget"), 10, 28, REPLACE_REGET },
326 /* 11 */
327 { N_("A&ppend"), 9, 45, REPLACE_APPEND },
328 /* 12 */
329 { N_("&No"), 9, 37, REPLACE_NO },
330 /* 13 */
331 { N_("&Yes"), 9, 28, REPLACE_YES },
332 /* 14 */
333 { N_("Overwrite this target?"), 9, 4, 0 }
334 /* *INDENT-ON* */
337 const int num = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
338 int *widgets_len;
340 FileOpContextUI *ui = ctx->ui;
342 char buffer[BUF_SMALL];
343 const char *title;
344 int stripped_name_len;
345 vfs_path_t *stripped_vpath;
346 const char *stripped_name;
347 char *stripped_name_orig;
348 int result;
350 widgets_len = g_new0 (int, num);
352 if (mode == Foreground)
353 title = _("File exists");
354 else
355 title = _("Background process: File exists");
357 stripped_vpath = vfs_path_from_str (ui->replace_filename);
358 stripped_name = stripped_name_orig =
359 vfs_path_to_str_flags (stripped_vpath, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
360 vfs_path_free (stripped_vpath);
361 stripped_name_len = str_term_width1 (stripped_name);
364 int i, l1, l2, l, row;
366 for (i = 0; i < num; i++)
368 #ifdef ENABLE_NLS
369 if (i != 1) /* skip filename */
370 rd_widgets[i].text = _(rd_widgets[i].text);
371 #endif /* ENABLE_NLS */
372 widgets_len[i] = str_term_width1 (rd_widgets[i].text);
376 * longest of "Overwrite..." labels
377 * (assume "Target date..." are short enough)
379 l1 = max (widgets_len[9], widgets_len[14]);
381 /* longest of button rows */
382 i = num;
383 for (row = l = l2 = 0; i--;)
384 if (rd_widgets[i].value != 0)
386 if (row != rd_widgets[i].ypos)
388 row = rd_widgets[i].ypos;
389 l2 = max (l2, l);
390 l = 0;
392 l += widgets_len[i] + 4;
395 l2 = max (l2, l); /* last row */
396 rd_xlen = max (rd_xlen, l1 + l2 + 8);
397 rd_xlen = max (rd_xlen, str_term_width1 (title) + 2);
398 rd_xlen = max (rd_xlen, min (COLS, stripped_name_len + 8));
400 /* Now place widgets */
401 l1 += 5; /* start of first button in the row */
402 i = num;
403 for (l = l1, row = 0; --i > 1;)
404 if (rd_widgets[i].value != 0)
406 if (row != rd_widgets[i].ypos)
408 row = rd_widgets[i].ypos;
409 l = l1;
411 rd_widgets[i].xpos = l;
412 l += widgets_len[i] + 4;
415 /* Abort button is centered */
416 rd_widgets[4].xpos = (rd_xlen - widgets_len[4] - 3) / 2;
419 /* FIXME - missing help node */
420 ui->replace_dlg =
421 create_dlg (TRUE, 0, 0, rd_ylen, rd_xlen, alarm_colors, NULL, "[Replace]",
422 title, DLG_CENTER | DLG_REVERSE);
424 /* prompt -- centered */
425 add_widget (ui->replace_dlg,
426 label_new (rd_widgets[0].ypos, (rd_xlen - widgets_len[0]) / 2, rd_widgets[0].text));
427 /* file name -- centered */
428 stripped_name = str_trunc (stripped_name, rd_xlen - 8);
429 stripped_name_len = str_term_width1 (stripped_name);
430 add_widget (ui->replace_dlg,
431 label_new (rd_widgets[1].ypos, (rd_xlen - stripped_name_len) / 2, stripped_name));
433 /* source date and size */
434 ADD_RD_LABEL (2, file_date (ui->s_stat->st_mtime), (unsigned long long) ui->s_stat->st_size);
435 /* destination date and size */
436 ADD_RD_LABEL (3, file_date (ui->d_stat->st_mtime), (unsigned long long) ui->d_stat->st_size);
438 ADD_RD_BUTTON (4); /* Abort */
439 ADD_RD_BUTTON (5); /* If size differs */
440 ADD_RD_BUTTON (6); /* None */
441 ADD_RD_BUTTON (7); /* Update */
442 ADD_RD_BUTTON (8); /* All" */
443 ADD_RD_LABEL (9, 0, 0); /* Overwrite all targets? */
445 /* "this target..." widgets */
446 if (!S_ISDIR (ui->d_stat->st_mode))
448 if ((ctx->operation == OP_COPY) && (ui->d_stat->st_size != 0)
449 && (ui->s_stat->st_size > ui->d_stat->st_size))
450 ADD_RD_BUTTON (10); /* Reget */
452 ADD_RD_BUTTON (11); /* Append */
454 ADD_RD_BUTTON (12); /* No */
455 ADD_RD_BUTTON (13); /* Yes */
456 ADD_RD_LABEL (14, 0, 0); /* Overwrite this target? */
458 result = run_dlg (ui->replace_dlg);
459 destroy_dlg (ui->replace_dlg);
461 g_free (widgets_len);
462 g_free (stripped_name_orig);
464 return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
465 #undef ADD_RD_LABEL
466 #undef ADD_RD_BUTTON
469 /* --------------------------------------------------------------------------------------------- */
471 static gboolean
472 is_wildcarded (char *p)
474 for (; *p; p++)
476 if (*p == '*')
477 return TRUE;
478 if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
479 return TRUE;
481 return FALSE;
484 /* --------------------------------------------------------------------------------------------- */
485 /*** public functions ****************************************************************************/
486 /* --------------------------------------------------------------------------------------------- */
488 FileProgressStatus
489 check_progress_buttons (FileOpContext * ctx)
491 int c;
492 Gpm_Event event;
493 FileOpContextUI *ui;
495 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
497 ui = ctx->ui;
499 event.x = -1; /* Don't show the GPM cursor */
500 c = tty_get_event (&event, FALSE, FALSE);
501 if (c == EV_NONE)
502 return FILE_CONT;
504 /* Reinitialize to avoid old values after events other than
505 selecting a button */
506 ui->op_dlg->ret_value = FILE_CONT;
508 dlg_process_event (ui->op_dlg, c, &event);
509 switch (ui->op_dlg->ret_value)
511 case FILE_SKIP:
512 return FILE_SKIP;
513 case B_CANCEL:
514 case FILE_ABORT:
515 return FILE_ABORT;
516 default:
517 return FILE_CONT;
522 /* --------------------------------------------------------------------------------------------- */
523 /* {{{ File progress display routines */
525 void
526 file_op_context_create_ui_without_init (FileOpContext * ctx, gboolean with_eta,
527 filegui_dialog_type_t dialog_type)
529 FileOpContextUI *ui;
530 const char *abort_button_label = N_("&Abort");
531 const char *skip_button_label = N_("&Skip");
532 int abort_button_width, skip_button_width, buttons_width;
533 int dlg_width, dlg_height;
535 g_return_if_fail (ctx != NULL);
536 g_return_if_fail (ctx->ui == NULL);
538 #ifdef ENABLE_NLS
539 abort_button_label = _(abort_button_label);
540 skip_button_label = _(skip_button_label);
541 #endif
543 abort_button_width = str_term_width1 (abort_button_label) + 3;
544 skip_button_width = str_term_width1 (skip_button_label) + 3;
545 buttons_width = abort_button_width + skip_button_width + 2;
547 dlg_width = max (58, buttons_width + 6);
548 dlg_height = 17; /* to make compiler happy :) */
550 ui = g_new0 (FileOpContextUI, 1);
551 ctx->ui = ui;
553 ctx->dialog_type = dialog_type;
555 switch (dialog_type)
557 case FILEGUI_DIALOG_ONE_ITEM:
558 dlg_height = verbose ? 12 : 10;
559 break;
560 case FILEGUI_DIALOG_MULTI_ITEM:
561 dlg_height = !verbose ? 10 : file_op_compute_totals ? 17 : 15;
562 break;
563 case FILEGUI_DIALOG_DELETE_ITEM:
564 dlg_height = 7;
565 break;
568 ctx->recursive_result = RECURSIVE_YES;
570 ui->replace_result = REPLACE_YES;
571 ui->showing_eta = with_eta && file_op_compute_totals;
572 ui->showing_bps = with_eta;
574 ui->op_dlg =
575 create_dlg (TRUE, 0, 0, dlg_height, dlg_width,
576 dialog_colors, NULL, NULL, op_names[ctx->operation], DLG_CENTER | DLG_REVERSE);
578 add_widget (ui->op_dlg,
579 button_new (dlg_height - 3, dlg_width / 2 + 1, FILE_ABORT,
580 NORMAL_BUTTON, abort_button_label, NULL));
581 add_widget (ui->op_dlg,
582 button_new (dlg_height - 3, dlg_width / 2 - 1 - skip_button_width, FILE_SKIP,
583 NORMAL_BUTTON, skip_button_label, NULL));
585 if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
587 int dy = file_op_compute_totals ? 2 : 0;
589 if (file_op_compute_totals)
590 add_widget (ui->op_dlg, ui->progress_total_gauge =
591 gauge_new (7 + dy, 3 + 3, 0, 100, 0));
593 add_widget (ui->op_dlg, ui->total_files_processed_label =
594 label_new (9 + dy, 3, ""));
596 add_widget (ui->op_dlg, ui->time_label = label_new (10 + dy, 3, ""));
598 add_widget (ui->op_dlg, ui->total_bytes_label = label_new (8, 3 + 15, ""));
599 add_widget (ui->op_dlg, hline_new (8, 1, dlg_width - 2));
602 add_widget (ui->op_dlg, ui->progress_file_label = label_new (7, 3, ""));
604 add_widget (ui->op_dlg, ui->progress_file_gauge = gauge_new (6, 3 + 3, 0, 100, 0));
606 add_widget (ui->op_dlg, ui->file_string[1] = label_new (5, 3, ""));
608 add_widget (ui->op_dlg, ui->file_label[1] = label_new (4, 3, ""));
609 add_widget (ui->op_dlg, ui->file_string[0] = label_new (3, 3, ""));
610 add_widget (ui->op_dlg, ui->file_label[0] = label_new (2, 3, ""));
612 if ((right_panel == current_panel) && !classic_progressbar)
614 ui->progress_file_gauge->from_left_to_right = FALSE;
615 if (verbose && file_op_compute_totals && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
616 ui->progress_total_gauge->from_left_to_right = FALSE;
620 /* --------------------------------------------------------------------------------------------- */
622 void
623 file_op_context_create_ui (FileOpContext * ctx, gboolean with_eta,
624 filegui_dialog_type_t dialog_type)
626 FileOpContextUI *ui;
628 g_return_if_fail (ctx != NULL);
629 g_return_if_fail (ctx->ui == NULL);
631 file_op_context_create_ui_without_init (ctx, with_eta, dialog_type);
632 ui = ctx->ui;
634 /* We will manage the dialog without any help, that's why
635 we have to call init_dlg */
636 init_dlg (ui->op_dlg);
639 /* --------------------------------------------------------------------------------------------- */
641 void
642 file_op_context_destroy_ui (FileOpContext * ctx)
644 g_return_if_fail (ctx != NULL);
646 if (ctx->ui != NULL)
648 FileOpContextUI *ui = (FileOpContextUI *) ctx->ui;
650 dlg_run_done (ui->op_dlg);
651 destroy_dlg (ui->op_dlg);
652 g_free (ui);
653 ctx->ui = NULL;
657 /* --------------------------------------------------------------------------------------------- */
659 show progressbar for file
662 void
663 file_progress_show (FileOpContext * ctx, off_t done, off_t total,
664 const char *stalled_msg, gboolean force_update)
666 FileOpContextUI *ui;
667 char buffer[BUF_TINY];
668 char buffer2[BUF_TINY];
669 char buffer3[BUF_TINY];
671 if (!verbose)
672 return;
674 g_return_if_fail (ctx != NULL);
675 g_return_if_fail (ctx->ui != NULL);
677 ui = ctx->ui;
679 if (total == 0)
681 gauge_show (ui->progress_file_gauge, 0);
682 return;
685 gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
686 gauge_show (ui->progress_file_gauge, 1);
688 if (!force_update)
689 return;
691 if (ui->showing_eta && ctx->eta_secs > 0.5)
693 file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
694 if (ctx->bps == 0)
695 g_snprintf (buffer, BUF_TINY, "%s %s", buffer2, stalled_msg);
696 else
698 file_bps_prepare_for_show (buffer3, ctx->bps);
699 g_snprintf (buffer, BUF_TINY, "%s (%s) %s", buffer2, buffer3, stalled_msg);
702 else
704 g_snprintf (buffer, BUF_TINY, "%s", stalled_msg);
707 label_set_text (ui->progress_file_label, buffer);
710 /* --------------------------------------------------------------------------------------------- */
712 void
713 file_progress_show_count (FileOpContext * ctx, size_t done, size_t total)
715 char buffer[BUF_TINY];
716 FileOpContextUI *ui;
718 g_return_if_fail (ctx != NULL);
719 g_return_if_fail (ctx->ui != NULL);
721 ui = ctx->ui;
722 if (file_op_compute_totals)
723 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu/%zu"), done, total);
724 else
725 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu"), done);
726 label_set_text (ui->total_files_processed_label, buffer);
729 /* --------------------------------------------------------------------------------------------- */
731 void
732 file_progress_show_total (FileOpTotalContext * tctx, FileOpContext * ctx, uintmax_t copied_bytes,
733 gboolean show_summary)
735 char buffer[BUF_TINY];
736 char buffer2[BUF_TINY];
737 char buffer3[BUF_TINY];
738 char buffer4[BUF_TINY];
739 struct timeval tv_current;
740 FileOpContextUI *ui;
742 g_return_if_fail (ctx != NULL);
743 g_return_if_fail (ctx->ui != NULL);
745 ui = ctx->ui;
747 if (file_op_compute_totals)
749 if (ctx->progress_bytes == 0)
750 gauge_show (ui->progress_total_gauge, 0);
751 else
753 gauge_set_value (ui->progress_total_gauge, 1024,
754 (int) (1024 * copied_bytes / ctx->progress_bytes));
755 gauge_show (ui->progress_total_gauge, 1);
759 if (!show_summary && tctx->bps == 0)
760 return;
762 gettimeofday (&tv_current, NULL);
763 file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec);
765 if (file_op_compute_totals)
767 file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
768 if (tctx->bps == 0)
769 g_snprintf (buffer, BUF_TINY, _("Time: %s %s"), buffer2, buffer3);
770 else
772 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
773 g_snprintf (buffer, BUF_TINY, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
776 else
778 if (tctx->bps == 0)
779 g_snprintf (buffer, BUF_TINY, _("Time: %s"), buffer2);
780 else
782 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
783 g_snprintf (buffer, BUF_TINY, _("Time: %s (%s)"), buffer2, buffer4);
787 label_set_text (ui->time_label, buffer);
789 size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
790 if (!file_op_compute_totals)
791 g_snprintf (buffer, BUF_TINY, _(" Total: %s "), buffer2);
792 else
794 size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
795 g_snprintf (buffer, BUF_TINY, _(" Total: %s/%s "), buffer2, buffer3);
798 label_set_text (ui->total_bytes_label, buffer);
801 /* }}} */
803 /* --------------------------------------------------------------------------------------------- */
805 void
806 file_progress_show_source (FileOpContext * ctx, const char *s)
808 FileOpContextUI *ui;
810 g_return_if_fail (ctx != NULL);
811 g_return_if_fail (ctx->ui != NULL);
813 ui = ctx->ui;
815 if (s != NULL)
817 #ifdef WITH_FULL_PATHS
818 size_t i;
819 char *cwd_str;
821 cwd_str = vfs_path_to_str (current_panel->cwd_vpath);
822 i = strlen (cwd_str);
824 /* We remove the full path we have added before */
825 if (strncmp (s, cwd_str, i) == 0)
826 if (s[i] == PATH_SEP)
827 s += i + 1;
828 g_free (cwd_str);
829 #endif /* WITH_FULL_PATHS */
830 label_set_text (ui->file_label[0], _("Source"));
831 label_set_text (ui->file_string[0], truncFileString (ui, s));
833 else
835 label_set_text (ui->file_label[0], "");
836 label_set_text (ui->file_string[0], "");
840 /* --------------------------------------------------------------------------------------------- */
842 void
843 file_progress_show_target (FileOpContext * ctx, const char *s)
845 FileOpContextUI *ui;
847 g_return_if_fail (ctx != NULL);
848 g_return_if_fail (ctx->ui != NULL);
850 ui = ctx->ui;
852 if (s != NULL)
854 label_set_text (ui->file_label[1], _("Target"));
855 label_set_text (ui->file_string[1], truncFileStringSecure (ui, s));
857 else
859 label_set_text (ui->file_label[1], "");
860 label_set_text (ui->file_string[1], "");
864 /* --------------------------------------------------------------------------------------------- */
866 void
867 file_progress_show_deleting (FileOpContext * ctx, const char *s)
869 FileOpContextUI *ui;
871 g_return_if_fail (ctx != NULL);
872 g_return_if_fail (ctx->ui != NULL);
874 ui = ctx->ui;
875 label_set_text (ui->file_label[0], _("Deleting"));
876 label_set_text (ui->file_label[0], truncFileStringSecure (ui, s));
879 /* --------------------------------------------------------------------------------------------- */
881 FileProgressStatus
882 file_progress_real_query_replace (FileOpContext * ctx,
883 enum OperationMode mode, const char *destname,
884 struct stat *_s_stat, struct stat *_d_stat)
886 FileOpContextUI *ui;
888 g_return_val_if_fail (ctx != NULL, FILE_CONT);
889 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
891 ui = ctx->ui;
893 if (ui->replace_result < REPLACE_ALWAYS)
895 ui->replace_filename = destname;
896 ui->s_stat = _s_stat;
897 ui->d_stat = _d_stat;
898 ui->replace_result = overwrite_query_dialog (ctx, mode);
901 switch (ui->replace_result)
903 case REPLACE_UPDATE:
904 do_refresh ();
905 if (_s_stat->st_mtime > _d_stat->st_mtime)
906 return FILE_CONT;
907 else
908 return FILE_SKIP;
910 case REPLACE_SIZE:
911 do_refresh ();
912 if (_s_stat->st_size == _d_stat->st_size)
913 return FILE_SKIP;
914 else
915 return FILE_CONT;
917 case REPLACE_REGET:
918 /* Careful: we fall through and set do_append */
919 ctx->do_reget = _d_stat->st_size;
921 case REPLACE_APPEND:
922 ctx->do_append = TRUE;
924 case REPLACE_YES:
925 case REPLACE_ALWAYS:
926 do_refresh ();
927 return FILE_CONT;
928 case REPLACE_NO:
929 case REPLACE_NEVER:
930 do_refresh ();
931 return FILE_SKIP;
932 case REPLACE_ABORT:
933 default:
934 return FILE_ABORT;
938 /* --------------------------------------------------------------------------------------------- */
940 char *
941 file_mask_dialog (FileOpContext * ctx, FileOperation operation,
942 gboolean only_one,
943 const char *format, const void *text, const char *def_text, gboolean * do_bg)
945 const size_t FMDY = 13;
946 const size_t FMDX = 68;
947 size_t fmd_xlen;
949 /* buttons */
950 const size_t gap = 1;
951 size_t b0_len, b2_len;
952 size_t b1_len = 0;
954 int source_easy_patterns = easy_patterns;
955 size_t i, len;
956 char fmd_buf[BUF_MEDIUM];
957 char *source_mask, *orig_mask, *dest_dir, *tmp;
958 char *def_text_secure;
959 int val;
961 QuickWidget fmd_widgets[] = {
962 /* 0 */ QUICK_BUTTON (42, 64, 10, FMDY, N_("&Cancel"), B_CANCEL, NULL),
963 #ifdef ENABLE_BACKGROUND
964 /* 1 */ QUICK_BUTTON (25, 64, 10, FMDY, N_("&Background"), B_USER, NULL),
965 #define OFFSET 0
966 #else
967 #define OFFSET 1
968 #endif /* ENABLE_BACKGROUND */
969 /* 2 - OFFSET */
970 QUICK_BUTTON (14, FMDX, 10, FMDY, N_("&OK"), B_ENTER, NULL),
971 /* 3 - OFFSET */
972 QUICK_CHECKBOX (42, FMDX, 8, FMDY, N_("&Stable Symlinks"), &ctx->stable_symlinks),
973 /* 4 - OFFSET */
974 QUICK_CHECKBOX (31, FMDX, 7, FMDY, N_("Di&ve into subdir if exists"),
975 &ctx->dive_into_subdirs),
976 /* 5 - OFFSET */
977 QUICK_CHECKBOX (3, FMDX, 8, FMDY, N_("Preserve &attributes"), &ctx->op_preserve),
978 /* 6 - OFFSET */
979 QUICK_CHECKBOX (3, FMDX, 7, FMDY, N_("Follow &links"), &ctx->follow_links),
980 /* 7 - OFFSET */
981 QUICK_INPUT (3, FMDX, 6, FMDY, "", 58, 0, "input2", &dest_dir),
982 /* 8 - OFFSET */
983 QUICK_LABEL (3, FMDX, 5, FMDY, N_("to:")),
984 /* 9 - OFFSET */
985 QUICK_CHECKBOX (37, FMDX, 4, FMDY, N_("&Using shell patterns"), &source_easy_patterns),
986 /* 10 - OFFSET */
987 QUICK_INPUT (3, FMDX, 3, FMDY, easy_patterns ? "*" : "^(.*)$", 58, 0, "input-def",
988 &source_mask),
989 /* 11 - OFFSET */
990 QUICK_LABEL (3, FMDX, 2, FMDY, fmd_buf),
991 QUICK_END
994 g_return_val_if_fail (ctx != NULL, NULL);
996 #ifdef ENABLE_NLS
997 /* buttons */
998 for (i = 0; i <= 2 - OFFSET; i++)
999 fmd_widgets[i].u.button.text = _(fmd_widgets[i].u.button.text);
1001 /* checkboxes */
1002 for (i = 3 - OFFSET; i <= 9 - OFFSET; i++)
1003 if (i != 7 - OFFSET)
1004 fmd_widgets[i].u.checkbox.text = _(fmd_widgets[i].u.checkbox.text);
1005 #endif /* !ENABLE_NLS */
1007 fmd_xlen = max (FMDX, (size_t) COLS * 2 / 3);
1009 len = str_term_width1 (fmd_widgets[6 - OFFSET].u.checkbox.text)
1010 + str_term_width1 (fmd_widgets[4 - OFFSET].u.checkbox.text) + 15;
1011 fmd_xlen = max (fmd_xlen, len);
1013 len = str_term_width1 (fmd_widgets[5 - OFFSET].u.checkbox.text)
1014 + str_term_width1 (fmd_widgets[3 - OFFSET].u.checkbox.text) + 15;
1015 fmd_xlen = max (fmd_xlen, len);
1017 /* buttons */
1018 b2_len = str_term_width1 (fmd_widgets[2 - OFFSET].u.button.text) + 6 + gap; /* OK */
1019 #ifdef ENABLE_BACKGROUND
1020 b1_len = str_term_width1 (fmd_widgets[1].u.button.text) + 4 + gap; /* Background */
1021 #endif
1022 b0_len = str_term_width1 (fmd_widgets[0].u.button.text) + 4; /* Cancel */
1023 len = b0_len + b1_len + b2_len;
1024 fmd_xlen = min (max (fmd_xlen, len + 6), (size_t) COLS);
1026 if (only_one)
1028 int flen;
1030 flen = str_term_width1 (format);
1031 i = fmd_xlen - flen - 4; /* FIXME */
1032 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc ((const char *) text, i));
1034 else
1036 g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1037 fmd_xlen = max (fmd_xlen, (size_t) str_term_width1 (fmd_buf) + 6);
1040 for (i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]); i > 0;)
1041 fmd_widgets[--i].x_divisions = fmd_xlen;
1043 i = (fmd_xlen - len) / 2;
1044 /* OK button */
1045 fmd_widgets[2 - OFFSET].relative_x = i;
1046 i += b2_len;
1047 #ifdef ENABLE_BACKGROUND
1048 /* Background button */
1049 fmd_widgets[1].relative_x = i;
1050 i += b1_len;
1051 #endif
1052 /* Cancel button */
1053 fmd_widgets[0].relative_x = i;
1055 #define chkbox_xpos(i) \
1056 fmd_widgets [i].relative_x = fmd_xlen - str_term_width1 (fmd_widgets [i].u.checkbox.text) - 6
1057 chkbox_xpos (3 - OFFSET);
1058 chkbox_xpos (4 - OFFSET);
1059 chkbox_xpos (9 - OFFSET);
1060 #undef chkbox_xpos
1062 /* inputs */
1063 fmd_widgets[7 - OFFSET].u.input.len = fmd_widgets[10 - OFFSET].u.input.len = fmd_xlen - 6;
1065 /* unselect checkbox if target filesystem don't support attributes */
1066 ctx->op_preserve = filegui__check_attrs_on_fs (def_text);
1068 /* filter out a possible password from def_text */
1070 vfs_path_t *vpath;
1072 vpath = vfs_path_from_str (def_text);
1073 tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1074 vfs_path_free (vpath);
1076 if (source_easy_patterns)
1077 def_text_secure = strutils_glob_escape (tmp);
1078 else
1079 def_text_secure = strutils_regex_escape (tmp);
1080 g_free (tmp);
1082 /* destination */
1083 fmd_widgets[7 - OFFSET].u.input.text = def_text_secure;
1085 ctx->stable_symlinks = FALSE;
1086 *do_bg = FALSE;
1089 struct stat buf;
1090 vfs_path_t *vpath;
1092 QuickDialog Quick_input = {
1093 fmd_xlen, FMDY, -1, -1, op_names[operation],
1094 "[Mask Copy/Rename]", fmd_widgets, NULL, TRUE
1097 ask_file_mask:
1098 val = quick_dialog_skip (&Quick_input, 4);
1100 if (val == B_CANCEL)
1102 g_free (def_text_secure);
1103 return NULL;
1106 if (ctx->follow_links)
1107 ctx->stat_func = mc_stat;
1108 else
1109 ctx->stat_func = mc_lstat;
1111 if (ctx->op_preserve)
1113 ctx->preserve = TRUE;
1114 ctx->umask_kill = 0777777;
1115 ctx->preserve_uidgid = (geteuid () == 0);
1117 else
1119 int i2;
1120 ctx->preserve = ctx->preserve_uidgid = FALSE;
1121 i2 = umask (0);
1122 umask (i2);
1123 ctx->umask_kill = i2 ^ 0777777;
1126 if ((dest_dir == NULL) || (*dest_dir == '\0'))
1128 g_free (def_text_secure);
1129 g_free (source_mask);
1130 return dest_dir;
1133 ctx->search_handle = mc_search_new (source_mask, -1);
1135 if (ctx->search_handle == NULL)
1137 message (D_ERROR, MSG_ERROR, _("Invalid source pattern `%s'"), source_mask);
1138 g_free (dest_dir);
1139 g_free (source_mask);
1140 goto ask_file_mask;
1143 g_free (def_text_secure);
1144 g_free (source_mask);
1146 ctx->search_handle->is_case_sensitive = TRUE;
1147 if (source_easy_patterns)
1148 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1149 else
1150 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1152 tmp = dest_dir;
1153 dest_dir = tilde_expand (tmp);
1154 g_free (tmp);
1155 vpath = vfs_path_from_str (dest_dir);
1157 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1158 if (ctx->dest_mask == NULL)
1159 ctx->dest_mask = dest_dir;
1160 else
1161 ctx->dest_mask++;
1162 orig_mask = ctx->dest_mask;
1163 if (*ctx->dest_mask == '\0'
1164 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1165 && (!only_one
1166 || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1167 || (ctx->dive_into_subdirs
1168 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1169 || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1170 ctx->dest_mask = g_strdup ("\\0");
1171 else
1173 ctx->dest_mask = g_strdup (ctx->dest_mask);
1174 *orig_mask = '\0';
1176 if (!*dest_dir)
1178 g_free (dest_dir);
1179 dest_dir = g_strdup ("./");
1181 vfs_path_free (vpath);
1182 if (val == B_USER)
1183 *do_bg = TRUE;
1186 return dest_dir;
1189 /* --------------------------------------------------------------------------------------------- */