Merge branch '2499_mcedit_select_cur_word'
[midnight-commander/borarpet.git] / src / filemanager / filegui.c
blob3408ab157da04f4f8b2712f62b9e7056b5bbb438
1 /* File management GUI for the text mode edition
3 * Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 * 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
6 * Written by: 1994, 1995 Janne Kukonlehto
7 * 1994, 1995 Fred Leeflang
8 * 1994, 1995, 1996 Miguel de Icaza
9 * 1995, 1996 Jakub Jelinek
10 * 1997 Norbert Warmuth
11 * 1998 Pavel Machek
12 * 2009 Slava Zanko
14 * The copy code was based in GNU's cp, and was written by:
15 * Torbjorn Granlund, David MacKenzie, and Jim Meyering.
17 * The move code was based in GNU's mv, and was written by:
18 * Mike Parker and David MacKenzie.
20 * Janne Kukonlehto added much error recovery to them for being used
21 * in an interactive program.
23 * This program is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation; either version 2 of the License, or
26 * (at your option) any later version.
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
33 * You should have received a copy of the GNU General Public License
34 * along with this program; if not, write to the Free Software
35 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
39 * Please note that all dialogs used here must be safe for background
40 * operations.
43 /** \file filegui.c
44 * \brief Source: file management GUI for the text mode edition
47 /* {{{ Include files */
49 #include <config.h>
51 #include <errno.h>
52 #include <ctype.h>
53 #include <stdio.h>
54 #include <string.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
58 #if defined(STAT_STATVFS) \
59 && (defined(HAVE_STRUCT_STATVFS_F_BASETYPE) \
60 || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME))
61 #include <sys/statvfs.h>
62 #define STRUCT_STATFS struct statvfs
63 #define STATFS statvfs
64 #elif defined(HAVE_STATFS) && !defined(STAT_STATFS4)
65 #ifdef HAVE_SYS_VFS_H
66 #include <sys/vfs.h>
67 #elif defined(HAVE_SYS_MOUNT_H) && defined(HAVE_SYS_PARAM_H)
68 #include <sys/param.h>
69 #include <sys/mount.h>
70 #elif defined(HAVE_SYS_STATFS_H)
71 #include <sys/statfs.h>
72 #endif
73 #define STRUCT_STATFS struct statfs
74 #define STATFS statfs
75 #endif
77 #include <unistd.h>
79 #include "lib/global.h"
81 #include "lib/tty/key.h" /* tty_get_event */
82 #include "lib/mcconfig.h"
83 #include "lib/search.h"
84 #include "lib/vfs/mc-vfs/vfs.h"
85 #include "lib/strescape.h"
86 #include "lib/strutil.h"
87 #include "lib/timefmt.h" /* file_date() */
88 #include "lib/util.h"
89 #include "lib/widget.h"
91 #include "src/setup.h" /* verbose */
93 #include "midnight.h"
94 #include "fileopctx.h" /* FILE_CONT */
96 #include "filegui.h"
98 /* }}} */
100 /*** global variables ****************************************************************************/
102 int classic_progressbar = 1;
104 /*** file scope macro definitions ****************************************************************/
106 /* Hack: the vfs code should not rely on this */
107 #define WITH_FULL_PATHS 1
109 /* File operate window sizes */
110 #define WX 58
111 #define WY 11
112 #define FCOPY_LABEL_X 3
114 #define truncFileString(ui, s) str_trunc (s, 52)
115 #define truncFileStringSecure(ui, s) path_trunc (s, 52)
117 /*** file scope type declarations ****************************************************************/
119 /* *INDENT-OFF* */
120 typedef enum {
121 MSDOS_SUPER_MAGIC = 0x4d44,
122 NTFS_SB_MAGIC = 0x5346544e,
123 NTFS_3G_MAGIC = 0x65735546,
124 PROC_SUPER_MAGIC = 0x9fa0,
125 SMB_SUPER_MAGIC = 0x517B,
126 NCP_SUPER_MAGIC = 0x564c,
127 USBDEVICE_SUPER_MAGIC = 0x9fa2
128 } filegui_nonattrs_fs_t;
129 /* *INDENT-ON* */
131 /* Used for button result values */
132 typedef enum
134 REPLACE_YES = B_USER,
135 REPLACE_NO,
136 REPLACE_APPEND,
137 REPLACE_ALWAYS,
138 REPLACE_UPDATE,
139 REPLACE_NEVER,
140 REPLACE_ABORT,
141 REPLACE_SIZE,
142 REPLACE_REGET
143 } replace_action_t;
145 /* This structure describes the UI and internal data required by a file
146 * operation context.
148 typedef struct
150 /* ETA and bps */
151 gboolean showing_eta;
152 gboolean showing_bps;
154 /* Dialog and widgets for the operation progress window */
155 Dlg_head *op_dlg;
156 WLabel *file_string[2];
157 WLabel *file_label[2];
158 WGauge *progress_file_gauge;
159 WLabel *progress_file_label;
161 WGauge *progress_total_gauge;
163 WLabel *total_files_processed_label;
164 WLabel *time_label;
165 WLabel *total_bytes_label;
167 /* Query replace dialog */
168 Dlg_head *replace_dlg;
169 const char *replace_filename;
170 replace_action_t replace_result;
172 struct stat *s_stat, *d_stat;
173 } FileOpContextUI;
175 /*** file scope variables ************************************************************************/
177 /* Used to save the hint line */
178 static int last_hint_line;
180 /*** file scope functions ************************************************************************/
181 /* --------------------------------------------------------------------------------------------- */
183 static gboolean
184 filegui__check_attrs_on_fs (const char *fs_path)
186 #ifdef STATFS
187 STRUCT_STATFS stfs;
189 if (!setup_copymove_persistent_attr)
190 return FALSE;
192 if (STATFS (fs_path, &stfs) != 0)
193 return TRUE;
195 #ifdef __linux__
196 switch ((filegui_nonattrs_fs_t) stfs.f_type)
198 case MSDOS_SUPER_MAGIC:
199 case NTFS_SB_MAGIC:
200 case NTFS_3G_MAGIC:
201 case PROC_SUPER_MAGIC:
202 case SMB_SUPER_MAGIC:
203 case NCP_SUPER_MAGIC:
204 case USBDEVICE_SUPER_MAGIC:
205 return FALSE;
207 #elif defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) \
208 || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
209 if (!strcmp (stfs.f_fstypename, "msdos")
210 || !strcmp (stfs.f_fstypename, "msdosfs")
211 || !strcmp (stfs.f_fstypename, "ntfs")
212 || !strcmp (stfs.f_fstypename, "procfs")
213 || !strcmp (stfs.f_fstypename, "smbfs") || strstr (stfs.f_fstypename, "fusefs"))
214 return FALSE;
215 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
216 if (!strcmp (stfs.f_basetype, "pcfs")
217 || !strcmp (stfs.f_basetype, "ntfs")
218 || !strcmp (stfs.f_basetype, "proc")
219 || !strcmp (stfs.f_basetype, "smbfs") || !strcmp (stfs.f_basetype, "fuse"))
220 return FALSE;
221 #endif
222 #endif /* STATFS */
224 return TRUE;
227 /* --------------------------------------------------------------------------------------------- */
229 static void
230 file_frmt_time (char *buffer, double eta_secs)
232 int eta_hours, eta_mins, eta_s;
233 eta_hours = eta_secs / (60 * 60);
234 eta_mins = (eta_secs - (eta_hours * 60 * 60)) / 60;
235 eta_s = eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
236 g_snprintf (buffer, BUF_TINY, _("%d:%02d.%02d"), eta_hours, eta_mins, eta_s);
239 /* --------------------------------------------------------------------------------------------- */
241 static void
242 file_eta_prepare_for_show (char *buffer, double eta_secs, gboolean always_show)
244 char _fmt_buff[BUF_TINY];
245 if (eta_secs <= 0.5 && !always_show)
247 *buffer = '\0';
248 return;
250 if (eta_secs <= 0.5)
251 eta_secs = 1;
252 file_frmt_time (_fmt_buff, eta_secs);
253 g_snprintf (buffer, BUF_TINY, _("ETA %s"), _fmt_buff);
256 /* --------------------------------------------------------------------------------------------- */
258 static void
259 file_bps_prepare_for_show (char *buffer, long bps)
261 if (bps > 1024 * 1024)
263 g_snprintf (buffer, BUF_TINY, _("%.2f MB/s"), bps / (1024 * 1024.0));
265 else if (bps > 1024)
267 g_snprintf (buffer, BUF_TINY, _("%.2f KB/s"), bps / 1024.0);
269 else if (bps > 1)
271 g_snprintf (buffer, BUF_TINY, _("%ld B/s"), bps);
273 else
274 *buffer = 0;
277 /* --------------------------------------------------------------------------------------------- */
279 * FIXME: probably it is better to replace this with quick dialog machinery,
280 * but actually I'm not familiar with it and have not much time :(
281 * alex
283 static replace_action_t
284 overwrite_query_dialog (FileOpContext * ctx, enum OperationMode mode)
286 #define ADD_RD_BUTTON(i) \
287 add_widget (ui->replace_dlg, \
288 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value, \
289 NORMAL_BUTTON, rd_widgets [i].text, 0))
291 #define ADD_RD_LABEL(i, p1, p2) \
292 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2); \
293 add_widget (ui->replace_dlg, label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer))
295 /* dialog sizes */
296 const int rd_ylen = 17;
297 int rd_xlen = 60;
299 struct
301 const char *text;
302 int ypos, xpos;
303 int value; /* 0 for labels */
304 } rd_widgets[] =
306 /* *INDENT-OFF* */
307 /* 0 */
308 { N_("Target file already exists!"), 3, 4, 0 },
309 /* 1 */
310 { "%s", 4, 4, 0 },
311 /* 2 */ /* cannot use PRIuMAX here; %llu is used instead */
312 { N_("Source date: %s, size %llu"), 6, 4, 0 },
313 /* 3 */ /* cannot use PRIuMAX here; %llu is used instead */
314 { N_("Target date: %s, size %llu"), 7, 4, 0 },
315 /* 4 */
316 { N_("&Abort"), 14, 25, REPLACE_ABORT },
317 /* 5 */
318 { N_("If &size differs"), 12, 28, REPLACE_SIZE },
319 /* 6 */
320 { N_("Non&e"), 11, 47, REPLACE_NEVER },
321 /* 7 */
322 { N_("&Update"), 11, 36, REPLACE_UPDATE },
323 /* 8 */
324 { N_("A&ll"), 11, 28, REPLACE_ALWAYS },
325 /* 9 */
326 { N_("Overwrite all targets?"), 11, 4, 0 },
327 /* 10 */
328 { N_("&Reget"), 10, 28, REPLACE_REGET },
329 /* 11 */
330 { N_("A&ppend"), 9, 45, REPLACE_APPEND },
331 /* 12 */
332 { N_("&No"), 9, 37, REPLACE_NO },
333 /* 13 */
334 { N_("&Yes"), 9, 28, REPLACE_YES },
335 /* 14 */
336 { N_("Overwrite this target?"), 9, 4, 0 }
337 /* *INDENT-ON* */
340 const int num = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
341 int *widgets_len;
343 FileOpContextUI *ui = ctx->ui;
345 char buffer[BUF_SMALL];
346 const char *title;
347 const char *stripped_name = strip_home_and_password (ui->replace_filename);
348 int stripped_name_len;
350 int result;
352 widgets_len = g_new0 (int, num);
354 if (mode == Foreground)
355 title = _("File exists");
356 else
357 title = _("Background process: File exists");
359 stripped_name_len = str_term_width1 (stripped_name);
362 int i, l1, l2, l, row;
364 for (i = 0; i < num; i++)
366 #ifdef ENABLE_NLS
367 if (i != 1) /* skip filename */
368 rd_widgets[i].text = _(rd_widgets[i].text);
369 #endif /* ENABLE_NLS */
370 widgets_len[i] = str_term_width1 (rd_widgets[i].text);
374 * longest of "Overwrite..." labels
375 * (assume "Target date..." are short enough)
377 l1 = max (widgets_len[9], widgets_len[14]);
379 /* longest of button rows */
380 i = num;
381 for (row = l = l2 = 0; i--;)
382 if (rd_widgets[i].value != 0)
384 if (row != rd_widgets[i].ypos)
386 row = rd_widgets[i].ypos;
387 l2 = max (l2, l);
388 l = 0;
390 l += widgets_len[i] + 4;
393 l2 = max (l2, l); /* last row */
394 rd_xlen = max (rd_xlen, l1 + l2 + 8);
395 rd_xlen = max (rd_xlen, str_term_width1 (title) + 2);
396 rd_xlen = max (rd_xlen, min (COLS, stripped_name_len + 8));
398 /* Now place widgets */
399 l1 += 5; /* start of first button in the row */
400 i = num;
401 for (l = l1, row = 0; --i > 1;)
402 if (rd_widgets[i].value != 0)
404 if (row != rd_widgets[i].ypos)
406 row = rd_widgets[i].ypos;
407 l = l1;
409 rd_widgets[i].xpos = l;
410 l += widgets_len[i] + 4;
413 /* Abort button is centered */
414 rd_widgets[4].xpos = (rd_xlen - widgets_len[4] - 3) / 2;
417 /* FIXME - missing help node */
418 ui->replace_dlg =
419 create_dlg (TRUE, 0, 0, rd_ylen, rd_xlen, alarm_colors, NULL, "[Replace]",
420 title, DLG_CENTER | DLG_REVERSE);
422 /* prompt -- centered */
423 add_widget (ui->replace_dlg,
424 label_new (rd_widgets[0].ypos, (rd_xlen - widgets_len[0]) / 2, rd_widgets[0].text));
425 /* file name -- centered */
426 stripped_name = str_trunc (stripped_name, rd_xlen - 8);
427 stripped_name_len = str_term_width1 (stripped_name);
428 add_widget (ui->replace_dlg,
429 label_new (rd_widgets[1].ypos, (rd_xlen - stripped_name_len) / 2, stripped_name));
431 /* source date and size */
432 ADD_RD_LABEL (2, file_date (ui->s_stat->st_mtime), (unsigned long long) ui->s_stat->st_size);
433 /* destination date and size */
434 ADD_RD_LABEL (3, file_date (ui->d_stat->st_mtime), (unsigned long long) ui->d_stat->st_size);
436 ADD_RD_BUTTON (4); /* Abort */
437 ADD_RD_BUTTON (5); /* If size differs */
438 ADD_RD_BUTTON (6); /* None */
439 ADD_RD_BUTTON (7); /* Update */
440 ADD_RD_BUTTON (8); /* All" */
441 ADD_RD_LABEL (9, 0, 0); /* Overwrite all targets? */
443 /* "this target..." widgets */
444 if (!S_ISDIR (ui->d_stat->st_mode))
446 if ((ctx->operation == OP_COPY) && (ui->d_stat->st_size != 0)
447 && (ui->s_stat->st_size > ui->d_stat->st_size))
448 ADD_RD_BUTTON (10); /* Reget */
450 ADD_RD_BUTTON (11); /* Append */
452 ADD_RD_BUTTON (12); /* No */
453 ADD_RD_BUTTON (13); /* Yes */
454 ADD_RD_LABEL (14, 0, 0); /* Overwrite this target? */
456 result = run_dlg (ui->replace_dlg);
457 destroy_dlg (ui->replace_dlg);
459 g_free (widgets_len);
461 return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
462 #undef ADD_RD_LABEL
463 #undef ADD_RD_BUTTON
466 /* --------------------------------------------------------------------------------------------- */
468 static gboolean
469 is_wildcarded (char *p)
471 for (; *p; p++)
473 if (*p == '*')
474 return TRUE;
475 if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
476 return TRUE;
478 return FALSE;
481 /* --------------------------------------------------------------------------------------------- */
482 /*** public functions ****************************************************************************/
483 /* --------------------------------------------------------------------------------------------- */
485 FileProgressStatus
486 check_progress_buttons (FileOpContext * ctx)
488 int c;
489 Gpm_Event event;
490 FileOpContextUI *ui;
492 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
494 ui = ctx->ui;
496 event.x = -1; /* Don't show the GPM cursor */
497 c = tty_get_event (&event, FALSE, FALSE);
498 if (c == EV_NONE)
499 return FILE_CONT;
501 /* Reinitialize to avoid old values after events other than
502 selecting a button */
503 ui->op_dlg->ret_value = FILE_CONT;
505 dlg_process_event (ui->op_dlg, c, &event);
506 switch (ui->op_dlg->ret_value)
508 case FILE_SKIP:
509 return FILE_SKIP;
510 case B_CANCEL:
511 case FILE_ABORT:
512 return FILE_ABORT;
513 default:
514 return FILE_CONT;
519 /* --------------------------------------------------------------------------------------------- */
520 /* {{{ File progress display routines */
522 void
523 file_op_context_create_ui_without_init (FileOpContext * ctx, gboolean with_eta,
524 filegui_dialog_type_t dialog_type)
526 FileOpContextUI *ui;
527 int minus = 0, total_reserve = 0;
528 const char *abort_button_label = N_("&Abort");
529 const char *skip_button_label = N_("&Skip");
530 int abort_button_width, skip_button_width, buttons_width;
531 int dlg_width;
533 g_return_if_fail (ctx != NULL);
534 g_return_if_fail (ctx->ui == NULL);
536 #ifdef ENABLE_NLS
537 abort_button_label = _(abort_button_label);
538 skip_button_label = _(skip_button_label);
539 #endif
541 abort_button_width = str_term_width1 (abort_button_label) + 3;
542 skip_button_width = str_term_width1 (skip_button_label) + 3;
543 buttons_width = abort_button_width + skip_button_width + 2;
545 dlg_width = max (WX, buttons_width + 6);
547 ui = g_new0 (FileOpContextUI, 1);
548 ctx->ui = ui;
550 ctx->dialog_type = dialog_type;
552 switch (dialog_type)
554 case FILEGUI_DIALOG_ONE_ITEM:
555 total_reserve = 0;
556 minus = verbose ? 0 : 2;
557 break;
558 case FILEGUI_DIALOG_MULTI_ITEM:
559 total_reserve = 5;
560 minus = verbose ? 0 : 7;
561 break;
562 case FILEGUI_DIALOG_DELETE_ITEM:
563 total_reserve = -5;
564 minus = 0;
565 break;
568 ctx->recursive_result = RECURSIVE_YES;
570 ui->replace_result = REPLACE_YES;
571 ui->showing_eta = with_eta;
572 ui->showing_bps = with_eta;
574 ui->op_dlg =
575 create_dlg (TRUE, 0, 0, WY - minus + 1 + total_reserve, dlg_width,
576 dialog_colors, NULL, NULL, op_names[ctx->operation], DLG_CENTER | DLG_REVERSE);
578 last_hint_line = the_hint->widget.y;
579 if ((ui->op_dlg->y + ui->op_dlg->lines) > last_hint_line)
580 the_hint->widget.y = ui->op_dlg->y + ui->op_dlg->lines + 1;
582 add_widget (ui->op_dlg,
583 button_new (WY - minus - 2 + total_reserve,
584 dlg_width / 2 + 1, FILE_ABORT,
585 NORMAL_BUTTON, abort_button_label, NULL));
586 add_widget (ui->op_dlg,
587 button_new (WY - minus - 2 + total_reserve,
588 dlg_width / 2 - 1 - skip_button_width, FILE_SKIP,
589 NORMAL_BUTTON, skip_button_label, NULL));
592 if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
594 add_widget (ui->op_dlg, hline_new (8, 1, dlg_width - 2));
596 add_widget (ui->op_dlg, ui->total_bytes_label = label_new (8, FCOPY_LABEL_X + 15, ""));
598 add_widget (ui->op_dlg, ui->progress_total_gauge =
599 gauge_new (9, FCOPY_LABEL_X + 3, 0, 100, 0));
601 add_widget (ui->op_dlg, ui->total_files_processed_label =
602 label_new (11, FCOPY_LABEL_X, ""));
604 add_widget (ui->op_dlg, ui->time_label = label_new (12, FCOPY_LABEL_X, ""));
607 add_widget (ui->op_dlg, ui->progress_file_label = label_new (7, FCOPY_LABEL_X, ""));
609 add_widget (ui->op_dlg, ui->progress_file_gauge = gauge_new (6, FCOPY_LABEL_X + 3, 0, 100, 0));
611 add_widget (ui->op_dlg, ui->file_string[1] = label_new (5, FCOPY_LABEL_X, ""));
613 add_widget (ui->op_dlg, ui->file_label[1] = label_new (4, FCOPY_LABEL_X, ""));
614 add_widget (ui->op_dlg, ui->file_string[0] = label_new (3, FCOPY_LABEL_X, ""));
615 add_widget (ui->op_dlg, ui->file_label[0] = label_new (2, FCOPY_LABEL_X, ""));
617 if ((right_panel == current_panel) && !classic_progressbar)
619 ui->progress_file_gauge->from_left_to_right = FALSE;
620 if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
621 ui->progress_total_gauge->from_left_to_right = FALSE;
625 /* --------------------------------------------------------------------------------------------- */
627 void
628 file_op_context_create_ui (FileOpContext * ctx, gboolean with_eta,
629 filegui_dialog_type_t dialog_type)
631 FileOpContextUI *ui;
633 g_return_if_fail (ctx != NULL);
634 g_return_if_fail (ctx->ui == NULL);
636 file_op_context_create_ui_without_init (ctx, with_eta, dialog_type);
637 ui = ctx->ui;
639 /* We will manage the dialog without any help, that's why
640 we have to call init_dlg */
641 init_dlg (ui->op_dlg);
644 /* --------------------------------------------------------------------------------------------- */
646 void
647 file_op_context_destroy_ui (FileOpContext * ctx)
649 g_return_if_fail (ctx != NULL);
651 if (ctx->ui != NULL)
653 FileOpContextUI *ui = (FileOpContextUI *) ctx->ui;
655 dlg_run_done (ui->op_dlg);
656 destroy_dlg (ui->op_dlg);
657 g_free (ui);
658 ctx->ui = NULL;
661 the_hint->widget.y = last_hint_line;
664 /* --------------------------------------------------------------------------------------------- */
666 show progressbar for file
669 void
670 file_progress_show (FileOpContext * ctx, off_t done, off_t total,
671 const char *stalled_msg, gboolean force_update)
673 FileOpContextUI *ui;
674 char buffer[BUF_TINY];
675 char buffer2[BUF_TINY];
676 char buffer3[BUF_TINY];
678 if (!verbose)
679 return;
681 g_return_if_fail (ctx != NULL);
682 g_return_if_fail (ctx->ui != NULL);
684 ui = ctx->ui;
686 if (total == 0)
688 gauge_show (ui->progress_file_gauge, 0);
689 return;
692 gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
693 gauge_show (ui->progress_file_gauge, 1);
695 if (!force_update)
696 return;
698 if (ui->showing_eta && ctx->eta_secs > 0.5)
700 file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
701 file_bps_prepare_for_show (buffer3, ctx->bps);
702 g_snprintf (buffer, BUF_TINY, "%s (%s) %s", buffer2, buffer3, stalled_msg);
704 else
706 g_snprintf (buffer, BUF_TINY, "%s", stalled_msg);
709 label_set_text (ui->progress_file_label, buffer);
712 /* --------------------------------------------------------------------------------------------- */
714 void
715 file_progress_show_count (FileOpContext * ctx, size_t done, size_t total)
717 char buffer[BUF_TINY];
718 FileOpContextUI *ui;
720 g_return_if_fail (ctx != NULL);
721 g_return_if_fail (ctx->ui != NULL);
723 ui = ctx->ui;
724 g_snprintf (buffer, BUF_TINY, _("Files processed: %zu of %zu"), done, total);
725 label_set_text (ui->total_files_processed_label, buffer);
728 /* --------------------------------------------------------------------------------------------- */
730 void
731 file_progress_show_total (FileOpTotalContext * tctx, FileOpContext * ctx, uintmax_t copyed_bytes,
732 gboolean show_summary)
734 char buffer[BUF_TINY];
735 char buffer2[BUF_TINY];
736 char buffer3[BUF_TINY];
737 char buffer4[BUF_TINY];
738 struct timeval tv_current;
739 FileOpContextUI *ui;
741 g_return_if_fail (ctx != NULL);
742 g_return_if_fail (ctx->ui != NULL);
744 ui = ctx->ui;
746 if (ctx->progress_bytes != 0)
748 gauge_set_value (ui->progress_total_gauge, 1024,
749 (int) (1024 * copyed_bytes / ctx->progress_bytes));
750 gauge_show (ui->progress_total_gauge, 1);
752 else
753 gauge_show (ui->progress_total_gauge, 0);
755 if (!show_summary && tctx->bps == 0)
756 return;
758 gettimeofday (&tv_current, NULL);
759 file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec);
760 file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
761 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
763 g_snprintf (buffer, BUF_TINY, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
764 label_set_text (ui->time_label, buffer);
766 size_trunc_len (buffer2, 5, tctx->copyed_bytes, 0, panels_options.kilobyte_si);
767 size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
769 g_snprintf (buffer, BUF_TINY, _("Total: %s of %s"), buffer2, buffer3);
771 label_set_text (ui->total_bytes_label, buffer);
774 /* }}} */
776 /* --------------------------------------------------------------------------------------------- */
778 void
779 file_progress_show_source (FileOpContext * ctx, const char *s)
781 FileOpContextUI *ui;
783 g_return_if_fail (ctx != NULL);
784 g_return_if_fail (ctx->ui != NULL);
786 ui = ctx->ui;
788 if (s != NULL)
790 #ifdef WITH_FULL_PATHS
791 size_t i;
793 i = strlen (current_panel->cwd);
795 /* We remove the full path we have added before */
796 if (strncmp (s, current_panel->cwd, i) == 0)
797 if (s[i] == PATH_SEP)
798 s += i + 1;
799 #endif /* WITH_FULL_PATHS */
801 label_set_text (ui->file_label[0], _("Source"));
802 label_set_text (ui->file_string[0], truncFileString (ui, s));
804 else
806 label_set_text (ui->file_label[0], "");
807 label_set_text (ui->file_string[0], "");
811 /* --------------------------------------------------------------------------------------------- */
813 void
814 file_progress_show_target (FileOpContext * ctx, const char *s)
816 FileOpContextUI *ui;
818 g_return_if_fail (ctx != NULL);
819 g_return_if_fail (ctx->ui != NULL);
821 ui = ctx->ui;
823 if (s != NULL)
825 label_set_text (ui->file_label[1], _("Target"));
826 label_set_text (ui->file_string[1], truncFileStringSecure (ui, s));
828 else
830 label_set_text (ui->file_label[1], "");
831 label_set_text (ui->file_string[1], "");
835 /* --------------------------------------------------------------------------------------------- */
837 void
838 file_progress_show_deleting (FileOpContext * ctx, const char *s)
840 FileOpContextUI *ui;
842 g_return_if_fail (ctx != NULL);
843 g_return_if_fail (ctx->ui != NULL);
845 ui = ctx->ui;
846 label_set_text (ui->file_label[0], _("Deleting"));
847 label_set_text (ui->file_label[0], truncFileStringSecure (ui, s));
850 /* --------------------------------------------------------------------------------------------- */
852 FileProgressStatus
853 file_progress_real_query_replace (FileOpContext * ctx,
854 enum OperationMode mode, const char *destname,
855 struct stat * _s_stat, struct stat * _d_stat)
857 FileOpContextUI *ui;
859 g_return_val_if_fail (ctx != NULL, FILE_CONT);
860 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
862 ui = ctx->ui;
864 if (ui->replace_result < REPLACE_ALWAYS)
866 ui->replace_filename = destname;
867 ui->s_stat = _s_stat;
868 ui->d_stat = _d_stat;
869 ui->replace_result = overwrite_query_dialog (ctx, mode);
872 switch (ui->replace_result)
874 case REPLACE_UPDATE:
875 do_refresh ();
876 if (_s_stat->st_mtime > _d_stat->st_mtime)
877 return FILE_CONT;
878 else
879 return FILE_SKIP;
881 case REPLACE_SIZE:
882 do_refresh ();
883 if (_s_stat->st_size == _d_stat->st_size)
884 return FILE_SKIP;
885 else
886 return FILE_CONT;
888 case REPLACE_REGET:
889 /* Careful: we fall through and set do_append */
890 ctx->do_reget = _d_stat->st_size;
892 case REPLACE_APPEND:
893 ctx->do_append = TRUE;
895 case REPLACE_YES:
896 case REPLACE_ALWAYS:
897 do_refresh ();
898 return FILE_CONT;
899 case REPLACE_NO:
900 case REPLACE_NEVER:
901 do_refresh ();
902 return FILE_SKIP;
903 case REPLACE_ABORT:
904 default:
905 return FILE_ABORT;
909 /* --------------------------------------------------------------------------------------------- */
911 char *
912 file_mask_dialog (FileOpContext * ctx, FileOperation operation,
913 gboolean only_one,
914 const char *format, const void *text,
915 const char *def_text, gboolean * do_bg)
917 const size_t FMDY = 13;
918 const size_t FMDX = 68;
919 size_t fmd_xlen;
921 /* buttons */
922 const size_t gap = 1;
923 size_t b0_len, b2_len;
924 size_t b1_len = 0;
926 int source_easy_patterns = easy_patterns;
927 size_t i, len;
928 char fmd_buf[BUF_MEDIUM];
929 char *source_mask, *orig_mask, *dest_dir, *tmp;
930 char *def_text_secure;
931 int val;
933 QuickWidget fmd_widgets[] = {
934 /* 0 */ QUICK_BUTTON (42, 64, 10, FMDY, N_("&Cancel"), B_CANCEL, NULL),
935 #ifdef WITH_BACKGROUND
936 /* 1 */ QUICK_BUTTON (25, 64, 10, FMDY, N_("&Background"), B_USER, NULL),
937 #define OFFSET 0
938 #else
939 #define OFFSET 1
940 #endif /* WITH_BACKGROUND */
941 /* 2 - OFFSET */
942 QUICK_BUTTON (14, FMDX, 10, FMDY, N_("&OK"), B_ENTER, NULL),
943 /* 3 - OFFSET */
944 QUICK_CHECKBOX (42, FMDX, 8, FMDY, N_("&Stable Symlinks"), &ctx->stable_symlinks),
945 /* 4 - OFFSET */
946 QUICK_CHECKBOX (31, FMDX, 7, FMDY, N_("Di&ve into subdir if exists"),
947 &ctx->dive_into_subdirs),
948 /* 5 - OFFSET */
949 QUICK_CHECKBOX (3, FMDX, 8, FMDY, N_("Preserve &attributes"), &ctx->op_preserve),
950 /* 6 - OFFSET */
951 QUICK_CHECKBOX (3, FMDX, 7, FMDY, N_("Follow &links"), &ctx->follow_links),
952 /* 7 - OFFSET */
953 QUICK_INPUT (3, FMDX, 6, FMDY, "", 58, 0, "input2", &dest_dir),
954 /* 8 - OFFSET */
955 QUICK_LABEL (3, FMDX, 5, FMDY, N_("to:")),
956 /* 9 - OFFSET */
957 QUICK_CHECKBOX (37, FMDX, 4, FMDY, N_("&Using shell patterns"), &source_easy_patterns),
958 /* 10 - OFFSET */
959 QUICK_INPUT (3, FMDX, 3, FMDY, easy_patterns ? "*" : "^(.*)$", 58, 0, "input-def",
960 &source_mask),
961 /* 11 - OFFSET */
962 QUICK_LABEL (3, FMDX, 2, FMDY, fmd_buf),
963 QUICK_END
966 g_return_val_if_fail (ctx != NULL, NULL);
968 #ifdef ENABLE_NLS
969 /* buttons */
970 for (i = 0; i <= 2 - OFFSET; i++)
971 fmd_widgets[i].u.button.text = _(fmd_widgets[i].u.button.text);
973 /* checkboxes */
974 for (i = 3 - OFFSET; i <= 9 - OFFSET; i++)
975 if (i != 7 - OFFSET)
976 fmd_widgets[i].u.checkbox.text = _(fmd_widgets[i].u.checkbox.text);
977 #endif /* !ENABLE_NLS */
979 fmd_xlen = max (FMDX, (size_t) COLS * 2 / 3);
981 len = str_term_width1 (fmd_widgets[6 - OFFSET].u.checkbox.text)
982 + str_term_width1 (fmd_widgets[4 - OFFSET].u.checkbox.text) + 15;
983 fmd_xlen = max (fmd_xlen, len);
985 len = str_term_width1 (fmd_widgets[5 - OFFSET].u.checkbox.text)
986 + str_term_width1 (fmd_widgets[3 - OFFSET].u.checkbox.text) + 15;
987 fmd_xlen = max (fmd_xlen, len);
989 /* buttons */
990 b2_len = str_term_width1 (fmd_widgets[2 - OFFSET].u.button.text) + 6 + gap; /* OK */
991 #ifdef WITH_BACKGROUND
992 b1_len = str_term_width1 (fmd_widgets[1].u.button.text) + 4 + gap; /* Background */
993 #endif
994 b0_len = str_term_width1 (fmd_widgets[0].u.button.text) + 4; /* Cancel */
995 len = b0_len + b1_len + b2_len;
996 fmd_xlen = min (max (fmd_xlen, len + 6), (size_t) COLS);
998 if (only_one)
1000 int flen;
1002 flen = str_term_width1 (format);
1003 i = fmd_xlen - flen - 4; /* FIXME */
1004 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc ((const char *) text, i));
1006 else
1008 g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1009 fmd_xlen = max (fmd_xlen, (size_t) str_term_width1 (fmd_buf) + 6);
1012 for (i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]); i > 0;)
1013 fmd_widgets[--i].x_divisions = fmd_xlen;
1015 i = (fmd_xlen - len) / 2;
1016 /* OK button */
1017 fmd_widgets[2 - OFFSET].relative_x = i;
1018 i += b2_len;
1019 #ifdef WITH_BACKGROUND
1020 /* Background button */
1021 fmd_widgets[1].relative_x = i;
1022 i += b1_len;
1023 #endif
1024 /* Cancel button */
1025 fmd_widgets[0].relative_x = i;
1027 #define chkbox_xpos(i) \
1028 fmd_widgets [i].relative_x = fmd_xlen - str_term_width1 (fmd_widgets [i].u.checkbox.text) - 6
1029 chkbox_xpos (3 - OFFSET);
1030 chkbox_xpos (4 - OFFSET);
1031 chkbox_xpos (9 - OFFSET);
1032 #undef chkbox_xpos
1034 /* inputs */
1035 fmd_widgets[7 - OFFSET].u.input.len = fmd_widgets[10 - OFFSET].u.input.len = fmd_xlen - 6;
1037 /* unselect checkbox if target filesystem don't support attributes */
1038 ctx->op_preserve = filegui__check_attrs_on_fs (def_text);
1040 /* filter out a possible password from def_text */
1041 tmp = strip_password (g_strdup (def_text), 1);
1042 if (source_easy_patterns)
1043 def_text_secure = strutils_glob_escape (tmp);
1044 else
1045 def_text_secure = strutils_regex_escape (tmp);
1046 g_free (tmp);
1048 /* destination */
1049 fmd_widgets[7 - OFFSET].u.input.text = def_text_secure;
1051 ctx->stable_symlinks = FALSE;
1052 *do_bg = FALSE;
1055 struct stat buf;
1057 QuickDialog Quick_input = {
1058 fmd_xlen, FMDY, -1, -1, op_names[operation],
1059 "[Mask Copy/Rename]", fmd_widgets, NULL, TRUE
1062 ask_file_mask:
1063 val = quick_dialog_skip (&Quick_input, 4);
1065 if (val == B_CANCEL)
1067 g_free (def_text_secure);
1068 return NULL;
1071 if (ctx->follow_links)
1072 ctx->stat_func = mc_stat;
1073 else
1074 ctx->stat_func = mc_lstat;
1076 if (ctx->op_preserve)
1078 ctx->preserve = TRUE;
1079 ctx->umask_kill = 0777777;
1080 ctx->preserve_uidgid = (geteuid () == 0);
1082 else
1084 int i2;
1085 ctx->preserve = ctx->preserve_uidgid = FALSE;
1086 i2 = umask (0);
1087 umask (i2);
1088 ctx->umask_kill = i2 ^ 0777777;
1091 if ((dest_dir == NULL) || (*dest_dir == '\0'))
1093 g_free (def_text_secure);
1094 g_free (source_mask);
1095 return dest_dir;
1098 ctx->search_handle = mc_search_new (source_mask, -1);
1100 if (ctx->search_handle == NULL)
1102 message (D_ERROR, MSG_ERROR, _("Invalid source pattern `%s'"), source_mask);
1103 g_free (dest_dir);
1104 g_free (source_mask);
1105 goto ask_file_mask;
1108 g_free (def_text_secure);
1109 g_free (source_mask);
1111 ctx->search_handle->is_case_sensitive = TRUE;
1112 if (source_easy_patterns)
1113 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1114 else
1115 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1117 tmp = dest_dir;
1118 dest_dir = tilde_expand (tmp);
1119 g_free (tmp);
1121 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1122 if (ctx->dest_mask == NULL)
1123 ctx->dest_mask = dest_dir;
1124 else
1125 ctx->dest_mask++;
1126 orig_mask = ctx->dest_mask;
1127 if (!*ctx->dest_mask
1128 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1129 && (!only_one
1130 || (!mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode))))
1131 || (ctx->dive_into_subdirs
1132 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1133 || (only_one && !mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode)))))
1134 ctx->dest_mask = g_strdup ("\\0");
1135 else
1137 ctx->dest_mask = g_strdup (ctx->dest_mask);
1138 *orig_mask = '\0';
1140 if (!*dest_dir)
1142 g_free (dest_dir);
1143 dest_dir = g_strdup ("./");
1145 if (val == B_USER)
1146 *do_bg = TRUE;
1149 return dest_dir;
1152 /* --------------------------------------------------------------------------------------------- */