Removed unused defines.
[midnight-commander.git] / src / filegui.c
blobf0c673981bd996c26f61468f1c9705171ae5b20f
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/search.h"
83 #include "lib/vfs/mc-vfs/vfs.h"
84 #include "lib/strescape.h"
85 #include "lib/strutil.h"
87 #include "setup.h" /* verbose */
88 #include "dialog.h" /* do_refresh() */
89 #include "widget.h" /* WLabel */
90 #include "main-widgets.h"
91 #include "main.h" /* the_hint */
92 #include "wtools.h" /* QuickDialog */
93 #include "panel.h" /* current_panel */
94 #include "fileopctx.h" /* FILE_CONT */
95 #include "filegui.h"
97 /* }}} */
98 /* *INDENT-OFF* */
99 typedef enum {
100 MSDOS_SUPER_MAGIC = 0x4d44,
101 NTFS_SB_MAGIC = 0x5346544e,
102 NTFS_3G_MAGIC = 0x65735546,
103 PROC_SUPER_MAGIC = 0x9fa0,
104 SMB_SUPER_MAGIC = 0x517B,
105 NCP_SUPER_MAGIC = 0x564c,
106 USBDEVICE_SUPER_MAGIC = 0x9fa2
107 } filegui_nonattrs_fs_t;
108 /* *INDENT-ON* */
110 /* Hack: the vfs code should not rely on this */
111 #define WITH_FULL_PATHS 1
113 /* This structure describes the UI and internal data required by a file
114 * operation context.
116 typedef struct
118 /* ETA and bps */
120 int showing_eta;
121 int showing_bps;
122 int eta_extra;
124 /* Dialog and widgets for the operation progress window */
126 Dlg_head *op_dlg;
128 WLabel *file_label[2];
129 WLabel *file_string[2];
130 WLabel *progress_label[3];
131 WGauge *progress_gauge[3];
132 WLabel *eta_label;
133 WLabel *bps_label;
134 WLabel *stalled_label;
136 /* Query replace dialog */
138 Dlg_head *replace_dlg;
139 const char *replace_filename;
140 int replace_result;
141 struct stat *s_stat, *d_stat;
142 } FileOpContextUI;
145 /* Used to save the hint line */
146 static int last_hint_line;
148 /* File operate window sizes */
149 #define WX 62
150 #define WY 10
151 #define BY 10
152 #define WX_ETA_EXTRA 12
154 #define FCOPY_GAUGE_X 14
155 #define FCOPY_LABEL_X 5
157 /* Used for button result values */
158 enum
160 REPLACE_YES = B_USER,
161 REPLACE_NO,
162 REPLACE_APPEND,
163 REPLACE_ALWAYS,
164 REPLACE_UPDATE,
165 REPLACE_NEVER,
166 REPLACE_ABORT,
167 REPLACE_SIZE,
168 REPLACE_REGET
171 static int
172 filegui__check_attrs_on_fs (const char *fs_path)
174 #ifdef STATFS
175 STRUCT_STATFS stfs;
177 if (!setup_copymove_persistent_attr)
178 return 0;
180 if (STATFS (fs_path, &stfs) != 0)
181 return 1;
183 # ifdef __linux__
184 switch ((filegui_nonattrs_fs_t) stfs.f_type)
186 case MSDOS_SUPER_MAGIC:
187 case NTFS_SB_MAGIC:
188 case NTFS_3G_MAGIC:
189 case PROC_SUPER_MAGIC:
190 case SMB_SUPER_MAGIC:
191 case NCP_SUPER_MAGIC:
192 case USBDEVICE_SUPER_MAGIC:
193 return 0;
194 break;
196 # elif defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) \
197 || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
198 if (!strcmp (stfs.f_fstypename, "msdos")
199 || !strcmp (stfs.f_fstypename, "msdosfs")
200 || !strcmp (stfs.f_fstypename, "ntfs")
201 || !strcmp (stfs.f_fstypename, "procfs")
202 || !strcmp (stfs.f_fstypename, "smbfs") || strstr (stfs.f_fstypename, "fusefs"))
203 return 0;
204 # elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
205 if (!strcmp (stfs.f_basetype, "pcfs")
206 || !strcmp (stfs.f_basetype, "ntfs")
207 || !strcmp (stfs.f_basetype, "proc")
208 || !strcmp (stfs.f_basetype, "smbfs") || !strcmp (stfs.f_basetype, "fuse"))
209 return 0;
210 # endif
211 #endif /* STATFS */
213 return 1;
216 static FileProgressStatus
217 check_progress_buttons (FileOpContext * ctx)
219 int c;
220 Gpm_Event event;
221 FileOpContextUI *ui;
223 if (ctx->ui == NULL)
224 return FILE_CONT;
226 ui = ctx->ui;
228 event.x = -1; /* Don't show the GPM cursor */
229 c = tty_get_event (&event, FALSE, FALSE);
230 if (c == EV_NONE)
231 return FILE_CONT;
233 /* Reinitialize to avoid old values after events other than
234 selecting a button */
235 ui->op_dlg->ret_value = FILE_CONT;
237 dlg_process_event (ui->op_dlg, c, &event);
238 switch (ui->op_dlg->ret_value)
240 case FILE_SKIP:
241 return FILE_SKIP;
242 break;
243 case B_CANCEL:
244 case FILE_ABORT:
245 return FILE_ABORT;
246 break;
247 default:
248 return FILE_CONT;
252 /* {{{ File progress display routines */
254 void
255 file_op_context_create_ui_without_init (FileOpContext * ctx, int with_eta)
257 FileOpContextUI *ui;
258 int x_size;
259 int minus;
260 int eta_offset;
261 const char *sixty;
262 const char *fifteen;
264 g_return_if_fail (ctx != NULL);
265 g_return_if_fail (ctx->ui == NULL);
267 ui = g_new0 (FileOpContextUI, 1);
268 ctx->ui = ui;
270 minus = verbose ? 0 : 3;
271 eta_offset = with_eta ? (WX_ETA_EXTRA) / 2 : 0;
273 sixty = "";
274 fifteen = "";
276 ctx->recursive_result = 0;
278 ui->replace_result = 0;
279 ui->showing_eta = with_eta;
280 ui->showing_bps = with_eta;
281 ui->eta_extra = with_eta ? WX_ETA_EXTRA : 0;
282 x_size = (WX + 4) + ui->eta_extra;
284 ui->op_dlg =
285 create_dlg (0, 0, WY - minus + 4, x_size, dialog_colors, NULL,
286 NULL, op_names[ctx->operation], DLG_CENTER | DLG_REVERSE);
288 last_hint_line = the_hint->widget.y;
289 if ((ui->op_dlg->y + ui->op_dlg->lines) > last_hint_line)
290 the_hint->widget.y = ui->op_dlg->y + ui->op_dlg->lines + 1;
292 add_widget (ui->op_dlg,
293 button_new (BY - minus, WX - 19 + eta_offset, FILE_ABORT,
294 NORMAL_BUTTON, _("&Abort"), 0));
295 add_widget (ui->op_dlg,
296 button_new (BY - minus, 14 + eta_offset, FILE_SKIP, NORMAL_BUTTON, _("&Skip"), 0));
298 add_widget (ui->op_dlg, ui->progress_gauge[2] = gauge_new (7, FCOPY_GAUGE_X, 0, 100, 0));
299 add_widget (ui->op_dlg, ui->progress_label[2] = label_new (7, FCOPY_LABEL_X, fifteen));
300 add_widget (ui->op_dlg, ui->bps_label = label_new (7, WX, ""));
302 add_widget (ui->op_dlg, ui->progress_gauge[1] = gauge_new (8, FCOPY_GAUGE_X, 0, 100, 0));
303 add_widget (ui->op_dlg, ui->progress_label[1] = label_new (8, FCOPY_LABEL_X, fifteen));
304 add_widget (ui->op_dlg, ui->stalled_label = label_new (8, WX, ""));
306 add_widget (ui->op_dlg, ui->progress_gauge[0] = gauge_new (6, FCOPY_GAUGE_X, 0, 100, 0));
307 add_widget (ui->op_dlg, ui->progress_label[0] = label_new (6, FCOPY_LABEL_X, fifteen));
308 add_widget (ui->op_dlg, ui->eta_label = label_new (6, WX, ""));
310 add_widget (ui->op_dlg, ui->file_string[1] = label_new (4, FCOPY_GAUGE_X, sixty));
311 add_widget (ui->op_dlg, ui->file_label[1] = label_new (4, FCOPY_LABEL_X, fifteen));
312 add_widget (ui->op_dlg, ui->file_string[0] = label_new (3, FCOPY_GAUGE_X, sixty));
313 add_widget (ui->op_dlg, ui->file_label[0] = label_new (3, FCOPY_LABEL_X, fifteen));
316 void
317 file_op_context_create_ui (FileOpContext * ctx, int with_eta)
319 FileOpContextUI *ui;
321 g_return_if_fail (ctx != NULL);
322 g_return_if_fail (ctx->ui == NULL);
324 file_op_context_create_ui_without_init (ctx, with_eta);
325 ui = ctx->ui;
327 /* We will manage the dialog without any help, that's why
328 we have to call init_dlg */
329 init_dlg (ui->op_dlg);
330 ui->op_dlg->running = 1;
333 void
334 file_op_context_destroy_ui (FileOpContext * ctx)
336 FileOpContextUI *ui;
338 g_return_if_fail (ctx != NULL);
340 if (ctx->ui)
342 ui = ctx->ui;
344 dlg_run_done (ui->op_dlg);
345 destroy_dlg (ui->op_dlg);
346 g_free (ui);
349 the_hint->widget.y = last_hint_line;
351 ctx->ui = NULL;
354 static FileProgressStatus
355 show_no_bar (FileOpContext * ctx, int n)
357 FileOpContextUI *ui;
359 if (ctx->ui == NULL)
360 return FILE_CONT;
362 ui = ctx->ui;
364 if (n >= 0)
366 label_set_text (ui->progress_label[n], "");
367 gauge_show (ui->progress_gauge[n], 0);
369 return check_progress_buttons (ctx);
372 static FileProgressStatus
373 show_bar (FileOpContext * ctx, int n, double done, double total)
375 FileOpContextUI *ui;
377 if (ctx->ui == NULL)
378 return FILE_CONT;
380 ui = ctx->ui;
383 * Gauge needs integers, so give it with integers between 0 and 1023.
384 * This precision should be quite reasonable.
386 gauge_set_value (ui->progress_gauge[n], 1024, (int) (1024 * done / total));
387 gauge_show (ui->progress_gauge[n], 1);
388 return check_progress_buttons (ctx);
391 static void
392 file_eta_show (FileOpContext * ctx)
394 int eta_hours, eta_mins, eta_s;
395 char eta_buffer[BUF_TINY];
396 FileOpContextUI *ui;
398 if (ctx->ui == NULL)
399 return;
401 ui = ctx->ui;
403 if (!ui->showing_eta)
404 return;
406 if (ctx->eta_secs > 0.5)
408 eta_hours = ctx->eta_secs / (60 * 60);
409 eta_mins = (ctx->eta_secs - (eta_hours * 60 * 60)) / 60;
410 eta_s = ctx->eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
411 g_snprintf (eta_buffer, sizeof (eta_buffer), _("ETA %d:%02d.%02d"),
412 eta_hours, eta_mins, eta_s);
414 else
415 *eta_buffer = 0;
417 label_set_text (ui->eta_label, eta_buffer);
420 static void
421 file_bps_show (FileOpContext * ctx)
423 char bps_buffer[BUF_TINY];
424 FileOpContextUI *ui;
426 if (ctx->ui == NULL)
427 return;
429 ui = ctx->ui;
431 if (!ui->showing_bps)
432 return;
434 if (ctx->bps > 1024 * 1024)
436 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f MB/s"), ctx->bps / (1024 * 1024.0));
438 else if (ctx->bps > 1024)
440 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f KB/s"), ctx->bps / 1024.0);
442 else if (ctx->bps > 1)
444 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%ld B/s"), ctx->bps);
446 else
447 *bps_buffer = 0;
449 label_set_text (ui->bps_label, bps_buffer);
452 FileProgressStatus
453 file_progress_show (FileOpContext * ctx, off_t done, off_t total)
455 FileOpContextUI *ui;
457 g_return_val_if_fail (ctx != NULL, FILE_CONT);
459 if (ctx->ui == NULL)
460 return FILE_CONT;
462 ui = ctx->ui;
464 if (!verbose)
465 return check_progress_buttons (ctx);
466 if (total > 0)
468 label_set_text (ui->progress_label[0], _("File"));
469 file_eta_show (ctx);
470 file_bps_show (ctx);
471 return show_bar (ctx, 0, done, total);
473 else
474 return show_no_bar (ctx, 0);
477 FileProgressStatus
478 file_progress_show_count (FileOpContext * ctx, off_t done, off_t total)
480 FileOpContextUI *ui;
482 g_return_val_if_fail (ctx != NULL, FILE_CONT);
484 if (ctx->ui == NULL)
485 return FILE_CONT;
487 ui = ctx->ui;
489 if (!verbose)
490 return check_progress_buttons (ctx);
491 if (total > 0)
493 label_set_text (ui->progress_label[1], _("Count"));
494 return show_bar (ctx, 1, done, total);
496 else
497 return show_no_bar (ctx, 1);
500 FileProgressStatus
501 file_progress_show_bytes (FileOpContext * ctx, double done, double total)
503 FileOpContextUI *ui;
505 g_return_val_if_fail (ctx != NULL, FILE_CONT);
507 if (ctx->ui == NULL)
508 return FILE_CONT;
510 ui = ctx->ui;
512 if (!verbose)
513 return check_progress_buttons (ctx);
514 if (total > 0)
516 label_set_text (ui->progress_label[2], _("Bytes"));
517 return show_bar (ctx, 2, done, total);
519 else
520 return show_no_bar (ctx, 2);
523 /* }}} */
525 #define truncFileString(ui, s) str_trunc (s, ui->eta_extra + 47)
526 #define truncFileStringSecure(ui, s) path_trunc (s, ui->eta_extra + 47)
528 FileProgressStatus
529 file_progress_show_source (FileOpContext * ctx, const char *s)
531 FileOpContextUI *ui;
533 g_return_val_if_fail (ctx != NULL, FILE_CONT);
535 if (ctx->ui == NULL)
536 return FILE_CONT;
538 ui = ctx->ui;
540 if (s != NULL)
542 #ifdef WITH_FULL_PATHS
543 int i = strlen (current_panel->cwd);
545 /* We remove the full path we have added before */
546 if (!strncmp (s, current_panel->cwd, i))
548 if (s[i] == PATH_SEP)
549 s += i + 1;
551 #endif /* WITH_FULL_PATHS */
553 label_set_text (ui->file_label[0], _("Source"));
554 label_set_text (ui->file_string[0], truncFileString (ui, s));
555 return check_progress_buttons (ctx);
557 else
559 label_set_text (ui->file_label[0], "");
560 label_set_text (ui->file_string[0], "");
561 return check_progress_buttons (ctx);
565 FileProgressStatus
566 file_progress_show_target (FileOpContext * ctx, const char *s)
568 FileOpContextUI *ui;
570 g_return_val_if_fail (ctx != NULL, FILE_CONT);
572 if (ctx->ui == NULL)
573 return FILE_CONT;
575 ui = ctx->ui;
577 if (s != NULL)
579 label_set_text (ui->file_label[1], _("Target"));
580 label_set_text (ui->file_string[1], truncFileStringSecure (ui, s));
581 return check_progress_buttons (ctx);
583 else
585 label_set_text (ui->file_label[1], "");
586 label_set_text (ui->file_string[1], "");
587 return check_progress_buttons (ctx);
591 FileProgressStatus
592 file_progress_show_deleting (FileOpContext * ctx, const char *s)
594 FileOpContextUI *ui;
596 g_return_val_if_fail (ctx != NULL, FILE_CONT);
598 if (ctx->ui == NULL)
599 return FILE_CONT;
601 ui = ctx->ui;
603 label_set_text (ui->file_label[0], _("Deleting"));
604 label_set_text (ui->file_label[0], truncFileStringSecure (ui, s));
605 return check_progress_buttons (ctx);
609 * FIXME: probably it is better to replace this with quick dialog machinery,
610 * but actually I'm not familiar with it and have not much time :(
611 * alex
615 static int
616 overwrite_query_dialog (FileOpContext * ctx, enum OperationMode mode)
618 #define ADD_RD_BUTTON(i)\
619 add_widget (ui->replace_dlg,\
620 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value,\
621 NORMAL_BUTTON, rd_widgets [i].text, 0))
623 #define ADD_RD_LABEL(i, p1, p2)\
624 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2);\
625 add_widget (ui->replace_dlg,\
626 label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer))
628 /* dialog sizes */
629 const int rd_ylen = 17;
630 int rd_xlen = 60;
632 struct
634 const char *text;
635 int ypos, xpos;
636 int value; /* 0 for labels */
637 } rd_widgets[] =
639 /* 0 */
641 N_("Target file already exists!"), 3, 4, 0},
642 /* 1 */
644 "%s", 4, 4, 0},
645 #if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || (defined _LARGE_FILES && _LARGE_FILES)
646 /* 2 */
648 N_("Source date: %s, size %llu"), 6, 4, 0},
649 /* 3 */
651 N_("Target date: %s, size %llu"), 7, 4, 0},
652 #else
653 /* 2 */
655 N_("Source date: %s, size %u"), 6, 4, 0},
656 /* 3 */
658 N_("Target date: %s, size %u"), 7, 4, 0},
659 #endif
660 /* 4 */
662 N_("&Abort"), 14, 25, REPLACE_ABORT},
663 /* 5 */
665 N_("If &size differs"), 12, 28, REPLACE_SIZE},
666 /* 6 */
668 N_("Non&e"), 11, 47, REPLACE_NEVER},
669 /* 7 */
671 N_("&Update"), 11, 36, REPLACE_UPDATE},
672 /* 8 */
674 N_("A&ll"), 11, 28, REPLACE_ALWAYS},
675 /* 9 */
677 N_("Overwrite all targets?"), 11, 4, 0},
678 /* 10 */
680 N_("&Reget"), 10, 28, REPLACE_REGET},
681 /* 11 */
683 N_("A&ppend"), 9, 45, REPLACE_APPEND},
684 /* 12 */
686 N_("&No"), 9, 37, REPLACE_NO},
687 /* 13 */
689 N_("&Yes"), 9, 28, REPLACE_YES},
690 /* 14 */
692 N_("Overwrite this target?"), 9, 4, 0}
695 const int num = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
696 int *widgets_len;
698 FileOpContextUI *ui = ctx->ui;
700 char buffer[BUF_SMALL];
701 const char *title;
702 const char *stripped_name = strip_home_and_password (ui->replace_filename);
703 int stripped_name_len;
705 int result;
707 widgets_len = g_new0 (int, num);
709 if (mode == Foreground)
710 title = _(" File exists ");
711 else
712 title = _(" Background process: File exists ");
714 stripped_name_len = str_term_width1 (stripped_name);
717 int i, l1, l2, l, row;
719 for (i = 0; i < num; i++)
721 #ifdef ENABLE_NLS
722 if (i != 1) /* skip filename */
723 rd_widgets[i].text = _(rd_widgets[i].text);
724 #endif /* ENABLE_NLS */
725 widgets_len[i] = str_term_width1 (rd_widgets[i].text);
729 * longest of "Overwrite..." labels
730 * (assume "Target date..." are short enough)
732 l1 = max (widgets_len[9], widgets_len[14]);
734 /* longest of button rows */
735 i = num;
736 for (row = l = l2 = 0; i--;)
737 if (rd_widgets[i].value != 0)
739 if (row != rd_widgets[i].ypos)
741 row = rd_widgets[i].ypos;
742 l2 = max (l2, l);
743 l = 0;
745 l += widgets_len[i] + 4;
748 l2 = max (l2, l); /* last row */
749 rd_xlen = max (rd_xlen, l1 + l2 + 8);
750 rd_xlen = max (rd_xlen, str_term_width1 (title) + 2);
751 rd_xlen = max (rd_xlen, min (COLS, stripped_name_len + 8));
753 /* Now place widgets */
754 l1 += 5; /* start of first button in the row */
755 i = num;
756 for (l = l1, row = 0; --i > 1;)
757 if (rd_widgets[i].value != 0)
759 if (row != rd_widgets[i].ypos)
761 row = rd_widgets[i].ypos;
762 l = l1;
764 rd_widgets[i].xpos = l;
765 l += widgets_len[i] + 4;
768 /* Abort button is centered */
769 rd_widgets[4].xpos = (rd_xlen - widgets_len[4] - 3) / 2;
772 /* FIXME - missing help node */
773 ui->replace_dlg =
774 create_dlg (0, 0, rd_ylen, rd_xlen, alarm_colors, NULL, "[Replace]",
775 title, DLG_CENTER | DLG_REVERSE);
777 /* prompt -- centered */
778 add_widget (ui->replace_dlg,
779 label_new (rd_widgets[0].ypos, (rd_xlen - widgets_len[0]) / 2, rd_widgets[0].text));
780 /* file name -- centered */
781 stripped_name = str_trunc (stripped_name, rd_xlen - 8);
782 stripped_name_len = str_term_width1 (stripped_name);
783 add_widget (ui->replace_dlg,
784 label_new (rd_widgets[1].ypos, (rd_xlen - stripped_name_len) / 2, stripped_name));
786 /* source date */
787 ADD_RD_LABEL (2, file_date (ui->s_stat->st_mtime), (off_t) ui->s_stat->st_size);
788 /* destination date */
789 ADD_RD_LABEL (3, file_date (ui->d_stat->st_mtime), (off_t) ui->d_stat->st_size);
791 ADD_RD_BUTTON (4); /* Abort */
792 ADD_RD_BUTTON (5); /* If size differs */
793 ADD_RD_BUTTON (6); /* None */
794 ADD_RD_BUTTON (7); /* Update */
795 ADD_RD_BUTTON (8); /* All" */
796 ADD_RD_LABEL (9, 0, 0); /* Overwrite all targets? */
798 /* "this target..." widgets */
799 if (!S_ISDIR (ui->d_stat->st_mode))
801 if ((ctx->operation == OP_COPY) && (ui->d_stat->st_size != 0)
802 && (ui->s_stat->st_size > ui->d_stat->st_size))
803 ADD_RD_BUTTON (10); /* Reget */
805 ADD_RD_BUTTON (11); /* Append */
807 ADD_RD_BUTTON (12); /* No */
808 ADD_RD_BUTTON (13); /* Yes */
809 ADD_RD_LABEL (14, 0, 0); /* Overwrite this target? */
811 result = run_dlg (ui->replace_dlg);
812 destroy_dlg (ui->replace_dlg);
814 g_free (widgets_len);
816 return result;
817 #undef ADD_RD_LABEL
818 #undef ADD_RD_BUTTON
821 void
822 file_progress_set_stalled_label (FileOpContext * ctx, const char *stalled_msg)
824 FileOpContextUI *ui;
826 g_return_if_fail (ctx != NULL);
827 g_return_if_fail (ctx->ui != NULL);
829 ui = ctx->ui;
830 label_set_text (ui->stalled_label, stalled_msg);
833 FileProgressStatus
834 file_progress_real_query_replace (FileOpContext * ctx,
835 enum OperationMode mode, const char *destname,
836 struct stat *_s_stat, struct stat *_d_stat)
838 FileOpContextUI *ui;
840 g_return_val_if_fail (ctx != NULL, FILE_CONT);
841 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
843 ui = ctx->ui;
845 if (ui->replace_result < REPLACE_ALWAYS)
847 ui->replace_filename = destname;
848 ui->s_stat = _s_stat;
849 ui->d_stat = _d_stat;
850 ui->replace_result = overwrite_query_dialog (ctx, mode);
851 if (ui->replace_result == B_CANCEL)
852 ui->replace_result = REPLACE_ABORT;
855 switch (ui->replace_result)
857 case REPLACE_UPDATE:
858 do_refresh ();
859 if (_s_stat->st_mtime > _d_stat->st_mtime)
860 return FILE_CONT;
861 else
862 return FILE_SKIP;
864 case REPLACE_SIZE:
865 do_refresh ();
866 if (_s_stat->st_size == _d_stat->st_size)
867 return FILE_SKIP;
868 else
869 return FILE_CONT;
871 case REPLACE_REGET:
872 /* Careful: we fall through and set do_append */
873 ctx->do_reget = _d_stat->st_size;
875 case REPLACE_APPEND:
876 ctx->do_append = 1;
878 case REPLACE_YES:
879 case REPLACE_ALWAYS:
880 do_refresh ();
881 return FILE_CONT;
882 case REPLACE_NO:
883 case REPLACE_NEVER:
884 do_refresh ();
885 return FILE_SKIP;
886 case REPLACE_ABORT:
887 default:
888 return FILE_ABORT;
892 static gboolean
893 is_wildcarded (char *p)
895 for (; *p; p++)
897 if (*p == '*')
898 return TRUE;
899 if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
900 return TRUE;
902 return FALSE;
905 char *
906 file_mask_dialog (FileOpContext * ctx, FileOperation operation,
907 gboolean only_one,
908 const char *format, const void *text,
909 const char *def_text, gboolean * do_background)
911 const size_t FMDY = 13;
912 const size_t FMDX = 68;
913 size_t fmd_xlen;
915 /* buttons */
916 const size_t gap = 1;
917 size_t b0_len, b2_len;
918 size_t b1_len = 0;
920 int source_easy_patterns = easy_patterns;
921 size_t i, len;
922 char fmd_buf[BUF_MEDIUM];
923 char *source_mask, *orig_mask, *dest_dir, *tmp;
924 char *def_text_secure;
925 int val;
927 QuickWidget fmd_widgets[] = {
928 /* 0 */ QUICK_BUTTON (42, 64, 10, FMDY, N_("&Cancel"), B_CANCEL, NULL),
929 #ifdef WITH_BACKGROUND
930 /* 1 */ QUICK_BUTTON (25, 64, 10, FMDY, N_("&Background"), B_USER, NULL),
931 #define OFFSET 0
932 #else
933 #define OFFSET 1
934 #endif /* WITH_BACKGROUND */
935 /* 2 - OFFSET */
936 QUICK_BUTTON (14, FMDX, 10, FMDY, N_("&OK"), B_ENTER, NULL),
937 /* 3 - OFFSET */
938 QUICK_CHECKBOX (42, FMDX, 8, FMDY, N_("&Stable Symlinks"), &ctx->stable_symlinks),
939 /* 4 - OFFSET */
940 QUICK_CHECKBOX (31, FMDX, 7, FMDY, N_("di&Ve into subdir if exists"),
941 &ctx->dive_into_subdirs),
942 /* 5 - OFFSET */
943 QUICK_CHECKBOX (3, FMDX, 8, FMDY, N_("preserve &Attributes"), &ctx->op_preserve),
944 /* 6 - OFFSET */
945 QUICK_CHECKBOX (3, FMDX, 7, FMDY, N_("follow &Links"), &ctx->follow_links),
946 /* 7 - OFFSET */
947 QUICK_INPUT (3, FMDX, 6, FMDY, "", 58, 0, "input2", &dest_dir),
948 /* 8 - OFFSET */
949 QUICK_LABEL (3, FMDX, 5, FMDY, N_("to:")),
950 /* 9 - OFFSET */
951 QUICK_CHECKBOX (37, FMDX, 4, FMDY, N_("&Using shell patterns"), &source_easy_patterns),
952 /* 10 - OFFSET */
953 QUICK_INPUT (3, FMDX, 3, FMDY, easy_patterns ? "*" : "^(.*)$", 58, 0, "input-def",
954 &source_mask),
955 /* 11 - OFFSET */
956 QUICK_LABEL (3, FMDX, 2, FMDY, fmd_buf),
957 QUICK_END
960 g_return_val_if_fail (ctx != NULL, NULL);
962 #ifdef ENABLE_NLS
963 /* buttons */
964 for (i = 0; i <= 2 - OFFSET; i++)
965 fmd_widgets[i].u.button.text = _(fmd_widgets[i].u.button.text);
967 /* checkboxes */
968 for (i = 3 - OFFSET; i <= 9 - OFFSET; i++)
969 if (i != 7 - OFFSET)
970 fmd_widgets[i].u.checkbox.text = _(fmd_widgets[i].u.checkbox.text);
971 #endif /* !ENABLE_NLS */
973 fmd_xlen = max (FMDX, (size_t) COLS * 2 / 3);
975 len = str_term_width1 (fmd_widgets[6 - OFFSET].u.checkbox.text)
976 + str_term_width1 (fmd_widgets[4 - OFFSET].u.checkbox.text) + 15;
977 fmd_xlen = max (fmd_xlen, len);
979 len = str_term_width1 (fmd_widgets[5 - OFFSET].u.checkbox.text)
980 + str_term_width1 (fmd_widgets[3 - OFFSET].u.checkbox.text) + 15;
981 fmd_xlen = max (fmd_xlen, len);
983 /* buttons */
984 b2_len = str_term_width1 (fmd_widgets[2 - OFFSET].u.button.text) + 6 + gap; /* OK */
985 #ifdef WITH_BACKGROUND
986 b1_len = str_term_width1 (fmd_widgets[1].u.button.text) + 4 + gap; /* Background */
987 #endif
988 b0_len = str_term_width1 (fmd_widgets[0].u.button.text) + 4; /* Cancel */
989 len = b0_len + b1_len + b2_len;
990 fmd_xlen = min (max (fmd_xlen, len + 6), (size_t) COLS);
992 if (only_one)
994 int flen;
996 flen = str_term_width1 (format);
997 i = fmd_xlen - flen - 4; /* FIXME */
998 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc ((const char *) text, i));
1000 else
1002 g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1003 fmd_xlen = max (fmd_xlen, (size_t) str_term_width1 (fmd_buf) + 6);
1006 for (i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]); i > 0;)
1007 fmd_widgets[--i].x_divisions = fmd_xlen;
1009 i = (fmd_xlen - len) / 2;
1010 /* OK button */
1011 fmd_widgets[2 - OFFSET].relative_x = i;
1012 i += b2_len;
1013 #ifdef WITH_BACKGROUND
1014 /* Background button */
1015 fmd_widgets[1].relative_x = i;
1016 i += b1_len;
1017 #endif
1018 /* Cancel button */
1019 fmd_widgets[0].relative_x = i;
1021 #define chkbox_xpos(i) \
1022 fmd_widgets [i].relative_x = fmd_xlen - str_term_width1 (fmd_widgets [i].u.checkbox.text) - 6
1023 chkbox_xpos (3 - OFFSET);
1024 chkbox_xpos (4 - OFFSET);
1025 chkbox_xpos (9 - OFFSET);
1026 #undef chkbox_xpos
1028 /* inputs */
1029 fmd_widgets[7 - OFFSET].u.input.len = fmd_widgets[10 - OFFSET].u.input.len = fmd_xlen - 6;
1031 /* unselect checkbox if target filesystem don't support attributes */
1032 ctx->op_preserve = filegui__check_attrs_on_fs (def_text);
1034 /* filter out a possible password from def_text */
1035 tmp = strip_password (g_strdup (def_text), 1);
1036 if (source_easy_patterns)
1037 def_text_secure = strutils_glob_escape (tmp);
1038 else
1039 def_text_secure = strutils_regex_escape (tmp);
1040 g_free (tmp);
1042 /* destination */
1043 fmd_widgets[7 - OFFSET].u.input.text = def_text_secure;
1045 ctx->stable_symlinks = 0;
1046 *do_background = FALSE;
1049 struct stat buf;
1051 QuickDialog Quick_input = {
1052 fmd_xlen, FMDY, -1, -1, op_names[operation],
1053 "[Mask Copy/Rename]", fmd_widgets, TRUE
1056 ask_file_mask:
1057 val = quick_dialog_skip (&Quick_input, 4);
1059 if (val == B_CANCEL)
1061 g_free (def_text_secure);
1062 return NULL;
1065 if (ctx->follow_links)
1066 ctx->stat_func = mc_stat;
1067 else
1068 ctx->stat_func = mc_lstat;
1070 if (ctx->op_preserve)
1072 ctx->preserve = 1;
1073 ctx->umask_kill = 0777777;
1074 ctx->preserve_uidgid = (geteuid () == 0) ? 1 : 0;
1076 else
1078 int i2;
1079 ctx->preserve = ctx->preserve_uidgid = 0;
1080 i2 = umask (0);
1081 umask (i2);
1082 ctx->umask_kill = i2 ^ 0777777;
1085 if (!dest_dir || !*dest_dir)
1087 g_free (def_text_secure);
1088 g_free (source_mask);
1089 return dest_dir;
1092 ctx->search_handle = mc_search_new (source_mask, -1);
1094 if (ctx->search_handle == NULL)
1096 message (D_ERROR, MSG_ERROR, _("Invalid source pattern `%s'"), source_mask);
1097 g_free (dest_dir);
1098 g_free (source_mask);
1099 goto ask_file_mask;
1102 g_free (def_text_secure);
1103 g_free (source_mask);
1105 ctx->search_handle->is_case_sentitive = TRUE;
1106 if (source_easy_patterns)
1107 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1108 else
1109 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1111 tmp = dest_dir;
1112 dest_dir = tilde_expand (tmp);
1113 g_free (tmp);
1115 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1116 if (ctx->dest_mask == NULL)
1117 ctx->dest_mask = dest_dir;
1118 else
1119 ctx->dest_mask++;
1120 orig_mask = ctx->dest_mask;
1121 if (!*ctx->dest_mask
1122 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1123 && (!only_one
1124 || (!mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode))))
1125 || (ctx->dive_into_subdirs
1126 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1127 || (only_one && !mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode)))))
1128 ctx->dest_mask = g_strdup ("\\0");
1129 else
1131 ctx->dest_mask = g_strdup (ctx->dest_mask);
1132 *orig_mask = '\0';
1134 if (!*dest_dir)
1136 g_free (dest_dir);
1137 dest_dir = g_strdup ("./");
1139 if (val == B_USER)
1140 *do_background = TRUE;
1143 return dest_dir;