Changed interface of mc_stat() and mc_lstat() functions
[midnight-commander.git] / src / filemanager / filegui.c
blob18ef3cafeb1b56771811c980812300e4f403fed7
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 const char *stripped_name = strip_home_and_password (ui->replace_filename);
345 int stripped_name_len;
347 int result;
349 widgets_len = g_new0 (int, num);
351 if (mode == Foreground)
352 title = _("File exists");
353 else
354 title = _("Background process: File exists");
356 stripped_name_len = str_term_width1 (stripped_name);
359 int i, l1, l2, l, row;
361 for (i = 0; i < num; i++)
363 #ifdef ENABLE_NLS
364 if (i != 1) /* skip filename */
365 rd_widgets[i].text = _(rd_widgets[i].text);
366 #endif /* ENABLE_NLS */
367 widgets_len[i] = str_term_width1 (rd_widgets[i].text);
371 * longest of "Overwrite..." labels
372 * (assume "Target date..." are short enough)
374 l1 = max (widgets_len[9], widgets_len[14]);
376 /* longest of button rows */
377 i = num;
378 for (row = l = l2 = 0; i--;)
379 if (rd_widgets[i].value != 0)
381 if (row != rd_widgets[i].ypos)
383 row = rd_widgets[i].ypos;
384 l2 = max (l2, l);
385 l = 0;
387 l += widgets_len[i] + 4;
390 l2 = max (l2, l); /* last row */
391 rd_xlen = max (rd_xlen, l1 + l2 + 8);
392 rd_xlen = max (rd_xlen, str_term_width1 (title) + 2);
393 rd_xlen = max (rd_xlen, min (COLS, stripped_name_len + 8));
395 /* Now place widgets */
396 l1 += 5; /* start of first button in the row */
397 i = num;
398 for (l = l1, row = 0; --i > 1;)
399 if (rd_widgets[i].value != 0)
401 if (row != rd_widgets[i].ypos)
403 row = rd_widgets[i].ypos;
404 l = l1;
406 rd_widgets[i].xpos = l;
407 l += widgets_len[i] + 4;
410 /* Abort button is centered */
411 rd_widgets[4].xpos = (rd_xlen - widgets_len[4] - 3) / 2;
414 /* FIXME - missing help node */
415 ui->replace_dlg =
416 create_dlg (TRUE, 0, 0, rd_ylen, rd_xlen, alarm_colors, NULL, "[Replace]",
417 title, DLG_CENTER | DLG_REVERSE);
419 /* prompt -- centered */
420 add_widget (ui->replace_dlg,
421 label_new (rd_widgets[0].ypos, (rd_xlen - widgets_len[0]) / 2, rd_widgets[0].text));
422 /* file name -- centered */
423 stripped_name = str_trunc (stripped_name, rd_xlen - 8);
424 stripped_name_len = str_term_width1 (stripped_name);
425 add_widget (ui->replace_dlg,
426 label_new (rd_widgets[1].ypos, (rd_xlen - stripped_name_len) / 2, stripped_name));
428 /* source date and size */
429 ADD_RD_LABEL (2, file_date (ui->s_stat->st_mtime), (unsigned long long) ui->s_stat->st_size);
430 /* destination date and size */
431 ADD_RD_LABEL (3, file_date (ui->d_stat->st_mtime), (unsigned long long) ui->d_stat->st_size);
433 ADD_RD_BUTTON (4); /* Abort */
434 ADD_RD_BUTTON (5); /* If size differs */
435 ADD_RD_BUTTON (6); /* None */
436 ADD_RD_BUTTON (7); /* Update */
437 ADD_RD_BUTTON (8); /* All" */
438 ADD_RD_LABEL (9, 0, 0); /* Overwrite all targets? */
440 /* "this target..." widgets */
441 if (!S_ISDIR (ui->d_stat->st_mode))
443 if ((ctx->operation == OP_COPY) && (ui->d_stat->st_size != 0)
444 && (ui->s_stat->st_size > ui->d_stat->st_size))
445 ADD_RD_BUTTON (10); /* Reget */
447 ADD_RD_BUTTON (11); /* Append */
449 ADD_RD_BUTTON (12); /* No */
450 ADD_RD_BUTTON (13); /* Yes */
451 ADD_RD_LABEL (14, 0, 0); /* Overwrite this target? */
453 result = run_dlg (ui->replace_dlg);
454 destroy_dlg (ui->replace_dlg);
456 g_free (widgets_len);
458 return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
459 #undef ADD_RD_LABEL
460 #undef ADD_RD_BUTTON
463 /* --------------------------------------------------------------------------------------------- */
465 static gboolean
466 is_wildcarded (char *p)
468 for (; *p; p++)
470 if (*p == '*')
471 return TRUE;
472 if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
473 return TRUE;
475 return FALSE;
478 /* --------------------------------------------------------------------------------------------- */
479 /*** public functions ****************************************************************************/
480 /* --------------------------------------------------------------------------------------------- */
482 FileProgressStatus
483 check_progress_buttons (FileOpContext * ctx)
485 int c;
486 Gpm_Event event;
487 FileOpContextUI *ui;
489 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
491 ui = ctx->ui;
493 event.x = -1; /* Don't show the GPM cursor */
494 c = tty_get_event (&event, FALSE, FALSE);
495 if (c == EV_NONE)
496 return FILE_CONT;
498 /* Reinitialize to avoid old values after events other than
499 selecting a button */
500 ui->op_dlg->ret_value = FILE_CONT;
502 dlg_process_event (ui->op_dlg, c, &event);
503 switch (ui->op_dlg->ret_value)
505 case FILE_SKIP:
506 return FILE_SKIP;
507 case B_CANCEL:
508 case FILE_ABORT:
509 return FILE_ABORT;
510 default:
511 return FILE_CONT;
516 /* --------------------------------------------------------------------------------------------- */
517 /* {{{ File progress display routines */
519 void
520 file_op_context_create_ui_without_init (FileOpContext * ctx, gboolean with_eta,
521 filegui_dialog_type_t dialog_type)
523 FileOpContextUI *ui;
524 const char *abort_button_label = N_("&Abort");
525 const char *skip_button_label = N_("&Skip");
526 int abort_button_width, skip_button_width, buttons_width;
527 int dlg_width, dlg_height;
529 g_return_if_fail (ctx != NULL);
530 g_return_if_fail (ctx->ui == NULL);
532 #ifdef ENABLE_NLS
533 abort_button_label = _(abort_button_label);
534 skip_button_label = _(skip_button_label);
535 #endif
537 abort_button_width = str_term_width1 (abort_button_label) + 3;
538 skip_button_width = str_term_width1 (skip_button_label) + 3;
539 buttons_width = abort_button_width + skip_button_width + 2;
541 dlg_width = max (58, buttons_width + 6);
542 dlg_height = 17; /* to make compiler happy :) */
544 ui = g_new0 (FileOpContextUI, 1);
545 ctx->ui = ui;
547 ctx->dialog_type = dialog_type;
549 switch (dialog_type)
551 case FILEGUI_DIALOG_ONE_ITEM:
552 dlg_height = verbose ? 12 : 10;
553 break;
554 case FILEGUI_DIALOG_MULTI_ITEM:
555 dlg_height = !verbose ? 10 : file_op_compute_totals ? 17 : 15;
556 break;
557 case FILEGUI_DIALOG_DELETE_ITEM:
558 dlg_height = 7;
559 break;
562 ctx->recursive_result = RECURSIVE_YES;
564 ui->replace_result = REPLACE_YES;
565 ui->showing_eta = with_eta && file_op_compute_totals;
566 ui->showing_bps = with_eta;
568 ui->op_dlg =
569 create_dlg (TRUE, 0, 0, dlg_height, dlg_width,
570 dialog_colors, NULL, NULL, op_names[ctx->operation], DLG_CENTER | DLG_REVERSE);
572 add_widget (ui->op_dlg,
573 button_new (dlg_height - 3, dlg_width / 2 + 1, FILE_ABORT,
574 NORMAL_BUTTON, abort_button_label, NULL));
575 add_widget (ui->op_dlg,
576 button_new (dlg_height - 3, dlg_width / 2 - 1 - skip_button_width, FILE_SKIP,
577 NORMAL_BUTTON, skip_button_label, NULL));
579 if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
581 int dy = file_op_compute_totals ? 2 : 0;
583 if (file_op_compute_totals)
584 add_widget (ui->op_dlg, ui->progress_total_gauge =
585 gauge_new (7 + dy, 3 + 3, 0, 100, 0));
587 add_widget (ui->op_dlg, ui->total_files_processed_label =
588 label_new (9 + dy, 3, ""));
590 add_widget (ui->op_dlg, ui->time_label = label_new (10 + dy, 3, ""));
592 add_widget (ui->op_dlg, ui->total_bytes_label = label_new (8, 3 + 15, ""));
593 add_widget (ui->op_dlg, hline_new (8, 1, dlg_width - 2));
596 add_widget (ui->op_dlg, ui->progress_file_label = label_new (7, 3, ""));
598 add_widget (ui->op_dlg, ui->progress_file_gauge = gauge_new (6, 3 + 3, 0, 100, 0));
600 add_widget (ui->op_dlg, ui->file_string[1] = label_new (5, 3, ""));
602 add_widget (ui->op_dlg, ui->file_label[1] = label_new (4, 3, ""));
603 add_widget (ui->op_dlg, ui->file_string[0] = label_new (3, 3, ""));
604 add_widget (ui->op_dlg, ui->file_label[0] = label_new (2, 3, ""));
606 if ((right_panel == current_panel) && !classic_progressbar)
608 ui->progress_file_gauge->from_left_to_right = FALSE;
609 if (verbose && file_op_compute_totals && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
610 ui->progress_total_gauge->from_left_to_right = FALSE;
614 /* --------------------------------------------------------------------------------------------- */
616 void
617 file_op_context_create_ui (FileOpContext * ctx, gboolean with_eta,
618 filegui_dialog_type_t dialog_type)
620 FileOpContextUI *ui;
622 g_return_if_fail (ctx != NULL);
623 g_return_if_fail (ctx->ui == NULL);
625 file_op_context_create_ui_without_init (ctx, with_eta, dialog_type);
626 ui = ctx->ui;
628 /* We will manage the dialog without any help, that's why
629 we have to call init_dlg */
630 init_dlg (ui->op_dlg);
633 /* --------------------------------------------------------------------------------------------- */
635 void
636 file_op_context_destroy_ui (FileOpContext * ctx)
638 g_return_if_fail (ctx != NULL);
640 if (ctx->ui != NULL)
642 FileOpContextUI *ui = (FileOpContextUI *) ctx->ui;
644 dlg_run_done (ui->op_dlg);
645 destroy_dlg (ui->op_dlg);
646 g_free (ui);
647 ctx->ui = NULL;
651 /* --------------------------------------------------------------------------------------------- */
653 show progressbar for file
656 void
657 file_progress_show (FileOpContext * ctx, off_t done, off_t total,
658 const char *stalled_msg, gboolean force_update)
660 FileOpContextUI *ui;
661 char buffer[BUF_TINY];
662 char buffer2[BUF_TINY];
663 char buffer3[BUF_TINY];
665 if (!verbose)
666 return;
668 g_return_if_fail (ctx != NULL);
669 g_return_if_fail (ctx->ui != NULL);
671 ui = ctx->ui;
673 if (total == 0)
675 gauge_show (ui->progress_file_gauge, 0);
676 return;
679 gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
680 gauge_show (ui->progress_file_gauge, 1);
682 if (!force_update)
683 return;
685 if (ui->showing_eta && ctx->eta_secs > 0.5)
687 file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
688 if (ctx->bps == 0)
689 g_snprintf (buffer, BUF_TINY, "%s %s", buffer2, stalled_msg);
690 else
692 file_bps_prepare_for_show (buffer3, ctx->bps);
693 g_snprintf (buffer, BUF_TINY, "%s (%s) %s", buffer2, buffer3, stalled_msg);
696 else
698 g_snprintf (buffer, BUF_TINY, "%s", stalled_msg);
701 label_set_text (ui->progress_file_label, buffer);
704 /* --------------------------------------------------------------------------------------------- */
706 void
707 file_progress_show_count (FileOpContext * ctx, size_t done, size_t total)
709 char buffer[BUF_TINY];
710 FileOpContextUI *ui;
712 g_return_if_fail (ctx != NULL);
713 g_return_if_fail (ctx->ui != NULL);
715 ui = ctx->ui;
716 if (file_op_compute_totals)
717 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu/%zu"), done, total);
718 else
719 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu"), done);
720 label_set_text (ui->total_files_processed_label, buffer);
723 /* --------------------------------------------------------------------------------------------- */
725 void
726 file_progress_show_total (FileOpTotalContext * tctx, FileOpContext * ctx, uintmax_t copied_bytes,
727 gboolean show_summary)
729 char buffer[BUF_TINY];
730 char buffer2[BUF_TINY];
731 char buffer3[BUF_TINY];
732 char buffer4[BUF_TINY];
733 struct timeval tv_current;
734 FileOpContextUI *ui;
736 g_return_if_fail (ctx != NULL);
737 g_return_if_fail (ctx->ui != NULL);
739 ui = ctx->ui;
741 if (file_op_compute_totals)
743 if (ctx->progress_bytes == 0)
744 gauge_show (ui->progress_total_gauge, 0);
745 else
747 gauge_set_value (ui->progress_total_gauge, 1024,
748 (int) (1024 * copied_bytes / ctx->progress_bytes));
749 gauge_show (ui->progress_total_gauge, 1);
753 if (!show_summary && tctx->bps == 0)
754 return;
756 gettimeofday (&tv_current, NULL);
757 file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec);
759 if (file_op_compute_totals)
761 file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
762 if (tctx->bps == 0)
763 g_snprintf (buffer, BUF_TINY, _("Time: %s %s"), buffer2, buffer3);
764 else
766 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
767 g_snprintf (buffer, BUF_TINY, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
770 else
772 if (tctx->bps == 0)
773 g_snprintf (buffer, BUF_TINY, _("Time: %s"), buffer2);
774 else
776 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
777 g_snprintf (buffer, BUF_TINY, _("Time: %s (%s)"), buffer2, buffer4);
781 label_set_text (ui->time_label, buffer);
783 size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
784 if (!file_op_compute_totals)
785 g_snprintf (buffer, BUF_TINY, _(" Total: %s "), buffer2);
786 else
788 size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
789 g_snprintf (buffer, BUF_TINY, _(" Total: %s/%s "), buffer2, buffer3);
792 label_set_text (ui->total_bytes_label, buffer);
795 /* }}} */
797 /* --------------------------------------------------------------------------------------------- */
799 void
800 file_progress_show_source (FileOpContext * ctx, const char *s)
802 FileOpContextUI *ui;
804 g_return_if_fail (ctx != NULL);
805 g_return_if_fail (ctx->ui != NULL);
807 ui = ctx->ui;
809 if (s != NULL)
811 #ifdef WITH_FULL_PATHS
812 size_t i;
814 i = strlen (current_panel->cwd);
816 /* We remove the full path we have added before */
817 if (strncmp (s, current_panel->cwd, i) == 0)
818 if (s[i] == PATH_SEP)
819 s += i + 1;
820 #endif /* WITH_FULL_PATHS */
822 label_set_text (ui->file_label[0], _("Source"));
823 label_set_text (ui->file_string[0], truncFileString (ui, s));
825 else
827 label_set_text (ui->file_label[0], "");
828 label_set_text (ui->file_string[0], "");
832 /* --------------------------------------------------------------------------------------------- */
834 void
835 file_progress_show_target (FileOpContext * ctx, const char *s)
837 FileOpContextUI *ui;
839 g_return_if_fail (ctx != NULL);
840 g_return_if_fail (ctx->ui != NULL);
842 ui = ctx->ui;
844 if (s != NULL)
846 label_set_text (ui->file_label[1], _("Target"));
847 label_set_text (ui->file_string[1], truncFileStringSecure (ui, s));
849 else
851 label_set_text (ui->file_label[1], "");
852 label_set_text (ui->file_string[1], "");
856 /* --------------------------------------------------------------------------------------------- */
858 void
859 file_progress_show_deleting (FileOpContext * ctx, const char *s)
861 FileOpContextUI *ui;
863 g_return_if_fail (ctx != NULL);
864 g_return_if_fail (ctx->ui != NULL);
866 ui = ctx->ui;
867 label_set_text (ui->file_label[0], _("Deleting"));
868 label_set_text (ui->file_label[0], truncFileStringSecure (ui, s));
871 /* --------------------------------------------------------------------------------------------- */
873 FileProgressStatus
874 file_progress_real_query_replace (FileOpContext * ctx,
875 enum OperationMode mode, const char *destname,
876 struct stat *_s_stat, struct stat *_d_stat)
878 FileOpContextUI *ui;
880 g_return_val_if_fail (ctx != NULL, FILE_CONT);
881 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
883 ui = ctx->ui;
885 if (ui->replace_result < REPLACE_ALWAYS)
887 ui->replace_filename = destname;
888 ui->s_stat = _s_stat;
889 ui->d_stat = _d_stat;
890 ui->replace_result = overwrite_query_dialog (ctx, mode);
893 switch (ui->replace_result)
895 case REPLACE_UPDATE:
896 do_refresh ();
897 if (_s_stat->st_mtime > _d_stat->st_mtime)
898 return FILE_CONT;
899 else
900 return FILE_SKIP;
902 case REPLACE_SIZE:
903 do_refresh ();
904 if (_s_stat->st_size == _d_stat->st_size)
905 return FILE_SKIP;
906 else
907 return FILE_CONT;
909 case REPLACE_REGET:
910 /* Careful: we fall through and set do_append */
911 ctx->do_reget = _d_stat->st_size;
913 case REPLACE_APPEND:
914 ctx->do_append = TRUE;
916 case REPLACE_YES:
917 case REPLACE_ALWAYS:
918 do_refresh ();
919 return FILE_CONT;
920 case REPLACE_NO:
921 case REPLACE_NEVER:
922 do_refresh ();
923 return FILE_SKIP;
924 case REPLACE_ABORT:
925 default:
926 return FILE_ABORT;
930 /* --------------------------------------------------------------------------------------------- */
932 char *
933 file_mask_dialog (FileOpContext * ctx, FileOperation operation,
934 gboolean only_one,
935 const char *format, const void *text, const char *def_text, gboolean * do_bg)
937 const size_t FMDY = 13;
938 const size_t FMDX = 68;
939 size_t fmd_xlen;
941 /* buttons */
942 const size_t gap = 1;
943 size_t b0_len, b2_len;
944 size_t b1_len = 0;
946 int source_easy_patterns = easy_patterns;
947 size_t i, len;
948 char fmd_buf[BUF_MEDIUM];
949 char *source_mask, *orig_mask, *dest_dir, *tmp;
950 char *def_text_secure;
951 int val;
953 QuickWidget fmd_widgets[] = {
954 /* 0 */ QUICK_BUTTON (42, 64, 10, FMDY, N_("&Cancel"), B_CANCEL, NULL),
955 #ifdef ENABLE_BACKGROUND
956 /* 1 */ QUICK_BUTTON (25, 64, 10, FMDY, N_("&Background"), B_USER, NULL),
957 #define OFFSET 0
958 #else
959 #define OFFSET 1
960 #endif /* ENABLE_BACKGROUND */
961 /* 2 - OFFSET */
962 QUICK_BUTTON (14, FMDX, 10, FMDY, N_("&OK"), B_ENTER, NULL),
963 /* 3 - OFFSET */
964 QUICK_CHECKBOX (42, FMDX, 8, FMDY, N_("&Stable Symlinks"), &ctx->stable_symlinks),
965 /* 4 - OFFSET */
966 QUICK_CHECKBOX (31, FMDX, 7, FMDY, N_("Di&ve into subdir if exists"),
967 &ctx->dive_into_subdirs),
968 /* 5 - OFFSET */
969 QUICK_CHECKBOX (3, FMDX, 8, FMDY, N_("Preserve &attributes"), &ctx->op_preserve),
970 /* 6 - OFFSET */
971 QUICK_CHECKBOX (3, FMDX, 7, FMDY, N_("Follow &links"), &ctx->follow_links),
972 /* 7 - OFFSET */
973 QUICK_INPUT (3, FMDX, 6, FMDY, "", 58, 0, "input2", &dest_dir),
974 /* 8 - OFFSET */
975 QUICK_LABEL (3, FMDX, 5, FMDY, N_("to:")),
976 /* 9 - OFFSET */
977 QUICK_CHECKBOX (37, FMDX, 4, FMDY, N_("&Using shell patterns"), &source_easy_patterns),
978 /* 10 - OFFSET */
979 QUICK_INPUT (3, FMDX, 3, FMDY, easy_patterns ? "*" : "^(.*)$", 58, 0, "input-def",
980 &source_mask),
981 /* 11 - OFFSET */
982 QUICK_LABEL (3, FMDX, 2, FMDY, fmd_buf),
983 QUICK_END
986 g_return_val_if_fail (ctx != NULL, NULL);
988 #ifdef ENABLE_NLS
989 /* buttons */
990 for (i = 0; i <= 2 - OFFSET; i++)
991 fmd_widgets[i].u.button.text = _(fmd_widgets[i].u.button.text);
993 /* checkboxes */
994 for (i = 3 - OFFSET; i <= 9 - OFFSET; i++)
995 if (i != 7 - OFFSET)
996 fmd_widgets[i].u.checkbox.text = _(fmd_widgets[i].u.checkbox.text);
997 #endif /* !ENABLE_NLS */
999 fmd_xlen = max (FMDX, (size_t) COLS * 2 / 3);
1001 len = str_term_width1 (fmd_widgets[6 - OFFSET].u.checkbox.text)
1002 + str_term_width1 (fmd_widgets[4 - OFFSET].u.checkbox.text) + 15;
1003 fmd_xlen = max (fmd_xlen, len);
1005 len = str_term_width1 (fmd_widgets[5 - OFFSET].u.checkbox.text)
1006 + str_term_width1 (fmd_widgets[3 - OFFSET].u.checkbox.text) + 15;
1007 fmd_xlen = max (fmd_xlen, len);
1009 /* buttons */
1010 b2_len = str_term_width1 (fmd_widgets[2 - OFFSET].u.button.text) + 6 + gap; /* OK */
1011 #ifdef ENABLE_BACKGROUND
1012 b1_len = str_term_width1 (fmd_widgets[1].u.button.text) + 4 + gap; /* Background */
1013 #endif
1014 b0_len = str_term_width1 (fmd_widgets[0].u.button.text) + 4; /* Cancel */
1015 len = b0_len + b1_len + b2_len;
1016 fmd_xlen = min (max (fmd_xlen, len + 6), (size_t) COLS);
1018 if (only_one)
1020 int flen;
1022 flen = str_term_width1 (format);
1023 i = fmd_xlen - flen - 4; /* FIXME */
1024 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc ((const char *) text, i));
1026 else
1028 g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1029 fmd_xlen = max (fmd_xlen, (size_t) str_term_width1 (fmd_buf) + 6);
1032 for (i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]); i > 0;)
1033 fmd_widgets[--i].x_divisions = fmd_xlen;
1035 i = (fmd_xlen - len) / 2;
1036 /* OK button */
1037 fmd_widgets[2 - OFFSET].relative_x = i;
1038 i += b2_len;
1039 #ifdef ENABLE_BACKGROUND
1040 /* Background button */
1041 fmd_widgets[1].relative_x = i;
1042 i += b1_len;
1043 #endif
1044 /* Cancel button */
1045 fmd_widgets[0].relative_x = i;
1047 #define chkbox_xpos(i) \
1048 fmd_widgets [i].relative_x = fmd_xlen - str_term_width1 (fmd_widgets [i].u.checkbox.text) - 6
1049 chkbox_xpos (3 - OFFSET);
1050 chkbox_xpos (4 - OFFSET);
1051 chkbox_xpos (9 - OFFSET);
1052 #undef chkbox_xpos
1054 /* inputs */
1055 fmd_widgets[7 - OFFSET].u.input.len = fmd_widgets[10 - OFFSET].u.input.len = fmd_xlen - 6;
1057 /* unselect checkbox if target filesystem don't support attributes */
1058 ctx->op_preserve = filegui__check_attrs_on_fs (def_text);
1060 /* filter out a possible password from def_text */
1061 tmp = strip_password (g_strdup (def_text), 1);
1062 if (source_easy_patterns)
1063 def_text_secure = strutils_glob_escape (tmp);
1064 else
1065 def_text_secure = strutils_regex_escape (tmp);
1066 g_free (tmp);
1068 /* destination */
1069 fmd_widgets[7 - OFFSET].u.input.text = def_text_secure;
1071 ctx->stable_symlinks = FALSE;
1072 *do_bg = FALSE;
1075 struct stat buf;
1076 vfs_path_t *vpath;
1078 QuickDialog Quick_input = {
1079 fmd_xlen, FMDY, -1, -1, op_names[operation],
1080 "[Mask Copy/Rename]", fmd_widgets, NULL, TRUE
1083 ask_file_mask:
1084 val = quick_dialog_skip (&Quick_input, 4);
1086 if (val == B_CANCEL)
1088 g_free (def_text_secure);
1089 return NULL;
1092 if (ctx->follow_links)
1093 ctx->stat_func = mc_stat;
1094 else
1095 ctx->stat_func = mc_lstat;
1097 if (ctx->op_preserve)
1099 ctx->preserve = TRUE;
1100 ctx->umask_kill = 0777777;
1101 ctx->preserve_uidgid = (geteuid () == 0);
1103 else
1105 int i2;
1106 ctx->preserve = ctx->preserve_uidgid = FALSE;
1107 i2 = umask (0);
1108 umask (i2);
1109 ctx->umask_kill = i2 ^ 0777777;
1112 if ((dest_dir == NULL) || (*dest_dir == '\0'))
1114 g_free (def_text_secure);
1115 g_free (source_mask);
1116 return dest_dir;
1119 ctx->search_handle = mc_search_new (source_mask, -1);
1121 if (ctx->search_handle == NULL)
1123 message (D_ERROR, MSG_ERROR, _("Invalid source pattern `%s'"), source_mask);
1124 g_free (dest_dir);
1125 g_free (source_mask);
1126 goto ask_file_mask;
1129 g_free (def_text_secure);
1130 g_free (source_mask);
1132 ctx->search_handle->is_case_sensitive = TRUE;
1133 if (source_easy_patterns)
1134 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1135 else
1136 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1138 tmp = dest_dir;
1139 dest_dir = tilde_expand (tmp);
1140 g_free (tmp);
1141 vpath = vfs_path_from_str (dest_dir);
1143 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1144 if (ctx->dest_mask == NULL)
1145 ctx->dest_mask = dest_dir;
1146 else
1147 ctx->dest_mask++;
1148 orig_mask = ctx->dest_mask;
1149 if (*ctx->dest_mask == '\0'
1150 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1151 && (!only_one
1152 || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1153 || (ctx->dive_into_subdirs
1154 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1155 || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1156 ctx->dest_mask = g_strdup ("\\0");
1157 else
1159 ctx->dest_mask = g_strdup (ctx->dest_mask);
1160 *orig_mask = '\0';
1162 if (!*dest_dir)
1164 g_free (dest_dir);
1165 dest_dir = g_strdup ("./");
1167 vfs_path_free (vpath);
1168 if (val == B_USER)
1169 *do_bg = TRUE;
1172 return dest_dir;
1175 /* --------------------------------------------------------------------------------------------- */