done_screen() is moved from src/layout.c to src/main.c.
[midnight-commander.git] / src / filegui.c
blobb0b37cdc25abba17a664d4108659cd78a2b7880e
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.
5 *
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 (__FreeBSD__)
59 # include <sys/param.h>
60 #endif
61 #if defined(__APPLE__) || defined (__FreeBSD__)
62 # include <sys/mount.h>
63 #elif defined (__NetBSD__)
64 # include <sys/param.h>
65 #else
66 # ifdef HAVE_VFS
67 # include <sys/vfs.h>
68 # else
69 # include <sys/statfs.h>
70 # endif
71 #endif
73 #include <unistd.h>
75 #include "global.h"
77 #include "../src/tty/key.h" /* get_event */
79 #include "../src/search/search.h"
81 #include "setup.h" /* verbose */
82 #include "dialog.h" /* do_refresh() */
83 #include "widget.h" /* WLabel */
84 #include "main-widgets.h"
85 #include "main.h" /* the_hint */
86 #include "wtools.h" /* QuickDialog */
87 #include "panel.h" /* current_panel */
88 #include "fileopctx.h" /* FILE_CONT */
89 #include "filegui.h"
90 #include "util.h" /* strip_password() */
91 #include "strutil.h"
92 #include "../src/strescape.h"
94 /* }}} */
95 typedef enum {
96 MSDOS_SUPER_MAGIC = 0x4d44,
97 NTFS_SB_MAGIC = 0x5346544e,
98 NTFS_3G_MAGIC = 0x65735546,
99 PROC_SUPER_MAGIC = 0x9fa0,
100 SMB_SUPER_MAGIC = 0x517B,
101 NCP_SUPER_MAGIC = 0x564c,
102 USBDEVICE_SUPER_MAGIC = 0x9fa2
103 } filegui_nonattrs_fs_t;
105 /* Hack: the vfs code should not rely on this */
106 #define WITH_FULL_PATHS 1
108 /* This structure describes the UI and internal data required by a file
109 * operation context.
111 typedef struct {
112 /* ETA and bps */
114 int showing_eta;
115 int showing_bps;
116 int eta_extra;
118 /* Dialog and widgets for the operation progress window */
120 Dlg_head *op_dlg;
122 WLabel *file_label[2];
123 WLabel *file_string[2];
124 WLabel *progress_label[3];
125 WGauge *progress_gauge[3];
126 WLabel *eta_label;
127 WLabel *bps_label;
128 WLabel *stalled_label;
130 /* Query replace dialog */
132 Dlg_head *replace_dlg;
133 const char *replace_filename;
134 int replace_result;
135 struct stat *s_stat, *d_stat;
136 } FileOpContextUI;
139 /* Used to save the hint line */
140 static int last_hint_line;
142 /* File operate window sizes */
143 #define WX 62
144 #define WY 10
145 #define BY 10
146 #define WX_ETA_EXTRA 12
148 #define FCOPY_GAUGE_X 14
149 #define FCOPY_LABEL_X 5
151 /* Used for button result values */
152 enum {
153 REPLACE_YES = B_USER,
154 REPLACE_NO,
155 REPLACE_APPEND,
156 REPLACE_ALWAYS,
157 REPLACE_UPDATE,
158 REPLACE_NEVER,
159 REPLACE_ABORT,
160 REPLACE_SIZE,
161 REPLACE_REGET
164 static int
165 filegui__check_attrs_on_fs(const char *fs_path)
167 struct statfs stfs;
169 if (statfs(fs_path, &stfs)!=0)
170 return 1;
172 switch ((filegui_nonattrs_fs_t) stfs.f_type)
174 case MSDOS_SUPER_MAGIC:
175 case NTFS_SB_MAGIC:
176 case NTFS_3G_MAGIC:
177 case PROC_SUPER_MAGIC:
178 case SMB_SUPER_MAGIC:
179 case NCP_SUPER_MAGIC:
180 case USBDEVICE_SUPER_MAGIC:
181 return 0;
182 break;
184 return 1;
187 static FileProgressStatus
188 check_progress_buttons (FileOpContext *ctx)
190 int c;
191 Gpm_Event event;
192 FileOpContextUI *ui;
194 if (ctx->ui == NULL)
195 return FILE_CONT;
197 ui = ctx->ui;
199 event.x = -1; /* Don't show the GPM cursor */
200 c = get_event (&event, 0, 0);
201 if (c == EV_NONE)
202 return FILE_CONT;
204 /* Reinitialize to avoid old values after events other than
205 selecting a button */
206 ui->op_dlg->ret_value = FILE_CONT;
208 dlg_process_event (ui->op_dlg, c, &event);
209 switch (ui->op_dlg->ret_value) {
210 case FILE_SKIP:
211 return FILE_SKIP;
212 break;
213 case B_CANCEL:
214 case FILE_ABORT:
215 return FILE_ABORT;
216 break;
217 default:
218 return FILE_CONT;
222 /* {{{ File progress display routines */
224 void
225 file_op_context_create_ui (FileOpContext *ctx, int with_eta)
227 FileOpContextUI *ui;
228 int x_size;
229 int minus;
230 int eta_offset;
231 const char *sixty;
232 const char *fifteen;
234 g_return_if_fail (ctx != NULL);
235 g_return_if_fail (ctx->ui == NULL);
237 ui = g_new0 (FileOpContextUI, 1);
238 ctx->ui = ui;
240 minus = verbose ? 0 : 3;
241 eta_offset = with_eta ? (WX_ETA_EXTRA) / 2 : 0;
243 sixty = "";
244 fifteen = "";
246 ctx->recursive_result = 0;
248 ui->replace_result = 0;
249 ui->showing_eta = with_eta;
250 ui->showing_bps = with_eta;
251 ui->eta_extra = with_eta ? WX_ETA_EXTRA : 0;
252 x_size = (WX + 4) + ui->eta_extra;
254 ui->op_dlg =
255 create_dlg (0, 0, WY - minus + 4, x_size, dialog_colors, NULL,
256 NULL, op_names[ctx->operation],
257 DLG_CENTER | DLG_REVERSE);
259 last_hint_line = the_hint->widget.y;
260 if ((ui->op_dlg->y + ui->op_dlg->lines) > last_hint_line)
261 the_hint->widget.y = ui->op_dlg->y + ui->op_dlg->lines + 1;
263 add_widget (ui->op_dlg,
264 button_new (BY - minus, WX - 19 + eta_offset, FILE_ABORT,
265 NORMAL_BUTTON, _("&Abort"), 0));
266 add_widget (ui->op_dlg,
267 button_new (BY - minus, 14 + eta_offset, FILE_SKIP,
268 NORMAL_BUTTON, _("&Skip"), 0));
270 add_widget (ui->op_dlg, ui->progress_gauge[2] =
271 gauge_new (7, FCOPY_GAUGE_X, 0, 100, 0));
272 add_widget (ui->op_dlg, ui->progress_label[2] =
273 label_new (7, FCOPY_LABEL_X, fifteen));
274 add_widget (ui->op_dlg, ui->bps_label = label_new (7, WX, ""));
276 add_widget (ui->op_dlg, ui->progress_gauge[1] =
277 gauge_new (8, FCOPY_GAUGE_X, 0, 100, 0));
278 add_widget (ui->op_dlg, ui->progress_label[1] =
279 label_new (8, FCOPY_LABEL_X, fifteen));
280 add_widget (ui->op_dlg, ui->stalled_label = label_new (8, WX, ""));
282 add_widget (ui->op_dlg, ui->progress_gauge[0] =
283 gauge_new (6, FCOPY_GAUGE_X, 0, 100, 0));
284 add_widget (ui->op_dlg, ui->progress_label[0] =
285 label_new (6, FCOPY_LABEL_X, fifteen));
286 add_widget (ui->op_dlg, ui->eta_label = label_new (6, WX, ""));
288 add_widget (ui->op_dlg, ui->file_string[1] =
289 label_new (4, FCOPY_GAUGE_X, sixty));
290 add_widget (ui->op_dlg, ui->file_label[1] =
291 label_new (4, FCOPY_LABEL_X, fifteen));
292 add_widget (ui->op_dlg, ui->file_string[0] =
293 label_new (3, FCOPY_GAUGE_X, sixty));
294 add_widget (ui->op_dlg, ui->file_label[0] =
295 label_new (3, FCOPY_LABEL_X, fifteen));
297 /* We will manage the dialog without any help, that's why
298 we have to call init_dlg */
299 init_dlg (ui->op_dlg);
300 ui->op_dlg->running = 1;
303 void
304 file_op_context_destroy_ui (FileOpContext *ctx)
306 FileOpContextUI *ui;
308 g_return_if_fail (ctx != NULL);
310 if (ctx->ui) {
311 ui = ctx->ui;
313 dlg_run_done (ui->op_dlg);
314 destroy_dlg (ui->op_dlg);
315 g_free (ui);
318 the_hint->widget.y = last_hint_line;
320 ctx->ui = NULL;
323 static FileProgressStatus
324 show_no_bar (FileOpContext *ctx, int n)
326 FileOpContextUI *ui;
328 if (ctx->ui == NULL)
329 return FILE_CONT;
331 ui = ctx->ui;
333 if (n >= 0) {
334 label_set_text (ui->progress_label[n], "");
335 gauge_show (ui->progress_gauge[n], 0);
337 return check_progress_buttons (ctx);
340 static FileProgressStatus
341 show_bar (FileOpContext *ctx, int n, double done, double total)
343 FileOpContextUI *ui;
345 if (ctx->ui == NULL)
346 return FILE_CONT;
348 ui = ctx->ui;
351 * Gauge needs integers, so give it with integers between 0 and 1023.
352 * This precision should be quite reasonable.
354 gauge_set_value (ui->progress_gauge[n], 1024,
355 (int) (1024 * done / total));
356 gauge_show (ui->progress_gauge[n], 1);
357 return check_progress_buttons (ctx);
360 static void
361 file_eta_show (FileOpContext *ctx)
363 int eta_hours, eta_mins, eta_s;
364 char eta_buffer[BUF_TINY];
365 FileOpContextUI *ui;
367 if (ctx->ui == NULL)
368 return;
370 ui = ctx->ui;
372 if (!ui->showing_eta)
373 return;
375 if (ctx->eta_secs > 0.5) {
376 eta_hours = ctx->eta_secs / (60 * 60);
377 eta_mins = (ctx->eta_secs - (eta_hours * 60 * 60)) / 60;
378 eta_s = ctx->eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
379 g_snprintf (eta_buffer, sizeof (eta_buffer), _("ETA %d:%02d.%02d"),
380 eta_hours, eta_mins, eta_s);
381 } else
382 *eta_buffer = 0;
384 label_set_text (ui->eta_label, eta_buffer);
387 static void
388 file_bps_show (FileOpContext *ctx)
390 char bps_buffer[BUF_TINY];
391 FileOpContextUI *ui;
393 if (ctx->ui == NULL)
394 return;
396 ui = ctx->ui;
398 if (!ui->showing_bps)
399 return;
401 if (ctx->bps > 1024 * 1024) {
402 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f MB/s"),
403 ctx->bps / (1024 * 1024.0));
404 } else if (ctx->bps > 1024) {
405 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f KB/s"),
406 ctx->bps / 1024.0);
407 } else if (ctx->bps > 1) {
408 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%ld B/s"),
409 ctx->bps);
410 } else
411 *bps_buffer = 0;
413 label_set_text (ui->bps_label, bps_buffer);
416 FileProgressStatus
417 file_progress_show (FileOpContext *ctx, off_t done, off_t total)
419 FileOpContextUI *ui;
421 g_return_val_if_fail (ctx != NULL, FILE_CONT);
423 if (ctx->ui == NULL)
424 return FILE_CONT;
426 ui = ctx->ui;
428 if (!verbose)
429 return check_progress_buttons (ctx);
430 if (total > 0) {
431 label_set_text (ui->progress_label[0], _("File"));
432 file_eta_show (ctx);
433 file_bps_show (ctx);
434 return show_bar (ctx, 0, done, total);
435 } else
436 return show_no_bar (ctx, 0);
439 FileProgressStatus
440 file_progress_show_count (FileOpContext *ctx, off_t done, off_t total)
442 FileOpContextUI *ui;
444 g_return_val_if_fail (ctx != NULL, FILE_CONT);
446 if (ctx->ui == NULL)
447 return FILE_CONT;
449 ui = ctx->ui;
451 if (!verbose)
452 return check_progress_buttons (ctx);
453 if (total > 0) {
454 label_set_text (ui->progress_label[1], _("Count"));
455 return show_bar (ctx, 1, done, total);
456 } else
457 return show_no_bar (ctx, 1);
460 FileProgressStatus
461 file_progress_show_bytes (FileOpContext *ctx, double done, double total)
463 FileOpContextUI *ui;
465 g_return_val_if_fail (ctx != NULL, FILE_CONT);
467 if (ctx->ui == NULL)
468 return FILE_CONT;
470 ui = ctx->ui;
472 if (!verbose)
473 return check_progress_buttons (ctx);
474 if (total > 0) {
475 label_set_text (ui->progress_label[2], _("Bytes"));
476 return show_bar (ctx, 2, done, total);
477 } else
478 return show_no_bar (ctx, 2);
481 /* }}} */
483 #define truncFileString(ui, s) str_trunc (s, ui->eta_extra + 47)
484 #define truncFileStringSecure(ui, s) path_trunc (s, ui->eta_extra + 47)
486 FileProgressStatus
487 file_progress_show_source (FileOpContext *ctx, const char *s)
489 FileOpContextUI *ui;
491 g_return_val_if_fail (ctx != NULL, FILE_CONT);
493 if (ctx->ui == NULL)
494 return FILE_CONT;
496 ui = ctx->ui;
498 if (s != NULL) {
499 #ifdef WITH_FULL_PATHS
500 int i = strlen (current_panel->cwd);
502 /* We remove the full path we have added before */
503 if (!strncmp (s, current_panel->cwd, i)) {
504 if (s[i] == PATH_SEP)
505 s += i + 1;
507 #endif /* WITH_FULL_PATHS */
509 label_set_text (ui->file_label[0], _("Source"));
510 label_set_text (ui->file_string[0], truncFileString (ui, s));
511 return check_progress_buttons (ctx);
512 } else {
513 label_set_text (ui->file_label[0], "");
514 label_set_text (ui->file_string[0], "");
515 return check_progress_buttons (ctx);
519 FileProgressStatus
520 file_progress_show_target (FileOpContext *ctx, const char *s)
522 FileOpContextUI *ui;
524 g_return_val_if_fail (ctx != NULL, FILE_CONT);
526 if (ctx->ui == NULL)
527 return FILE_CONT;
529 ui = ctx->ui;
531 if (s != NULL) {
532 label_set_text (ui->file_label[1], _("Target"));
533 label_set_text (ui->file_string[1], truncFileStringSecure (ui, s));
534 return check_progress_buttons (ctx);
535 } else {
536 label_set_text (ui->file_label[1], "");
537 label_set_text (ui->file_string[1], "");
538 return check_progress_buttons (ctx);
542 FileProgressStatus
543 file_progress_show_deleting (FileOpContext *ctx, const char *s)
545 FileOpContextUI *ui;
547 g_return_val_if_fail (ctx != NULL, FILE_CONT);
549 if (ctx->ui == NULL)
550 return FILE_CONT;
552 ui = ctx->ui;
554 label_set_text (ui->file_label[0], _("Deleting"));
555 label_set_text (ui->file_label[0], truncFileStringSecure (ui, s));
556 return check_progress_buttons (ctx);
559 #define X_TRUNC 52
562 * FIXME: probably it is better to replace this with quick dialog machinery,
563 * but actually I'm not familiar with it and have not much time :(
564 * alex
566 static struct {
567 const char *text;
568 int ypos, xpos;
569 int value; /* 0 for labels */
570 } rd_widgets[] = {
572 N_("Target file \"%s\" already exists!"), 3, 4, 0}, {
573 N_("&Abort"), BY + 3, 25, REPLACE_ABORT}, {
574 N_("If &size differs"), BY + 1, 28, REPLACE_SIZE}, {
575 N_("Non&e"), BY, 47, REPLACE_NEVER}, {
576 N_("&Update"), BY, 36, REPLACE_UPDATE}, {
577 N_("A&ll"), BY, 28, REPLACE_ALWAYS}, {
578 N_("Overwrite all targets?"), BY, 4, 0}, {
579 N_("&Reget"), BY - 1, 28, REPLACE_REGET}, {
580 N_("A&ppend"), BY - 2, 45, REPLACE_APPEND}, {
581 N_("&No"), BY - 2, 37, REPLACE_NO}, {
582 N_("&Yes"), BY - 2, 28, REPLACE_YES}, {
583 N_("Overwrite this target?"), BY - 2, 4, 0}, {
584 #if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || (defined _LARGE_FILES && _LARGE_FILES)
585 N_("Target date: %s, size %llu"), 6, 4, 0}, {
586 N_("Source date: %s, size %llu"), 5, 4, 0}
587 #else
588 N_("Target date: %s, size %u"), 6, 4, 0}, {
589 N_("Source date: %s, size %u"), 5, 4, 0}
590 #endif
593 #define ADD_RD_BUTTON(i)\
594 add_widget (ui->replace_dlg,\
595 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value,\
596 NORMAL_BUTTON, rd_widgets [i].text, 0))
598 #define ADD_RD_LABEL(ui,i,p1,p2)\
599 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2);\
600 add_widget (ui->replace_dlg,\
601 label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer))
603 static void
604 init_replace (FileOpContext *ctx, enum OperationMode mode)
606 FileOpContextUI *ui;
607 char buffer[BUF_SMALL];
608 const char *title;
609 static int rd_xlen = 60, rd_trunc = X_TRUNC;
611 #ifdef ENABLE_NLS
612 static int i18n_flag;
613 if (!i18n_flag) {
614 int l1, l2, l, row;
615 register int i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
616 while (i--)
617 rd_widgets[i].text = _(rd_widgets[i].text);
620 * longest of "Overwrite..." labels
621 * (assume "Target date..." are short enough)
623 l1 = max (str_term_width1 (rd_widgets[6].text),
624 str_term_width1 (rd_widgets[11].text));
626 /* longest of button rows */
627 i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
628 for (row = l = l2 = 0; i--;) {
629 if (rd_widgets[i].value != 0) {
630 if (row != rd_widgets[i].ypos) {
631 row = rd_widgets[i].ypos;
632 l2 = max (l2, l);
633 l = 0;
635 l+= str_term_width1 (rd_widgets[i].text) + 4;
638 l2 = max (l2, l); /* last row */
639 rd_xlen = max (rd_xlen, l1 + l2 + 8);
640 rd_trunc = rd_xlen - 6;
642 /* Now place buttons */
643 l1 += 5; /* start of first button in the row */
644 i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
646 for (l = l1, row = 0; --i > 1;) {
647 if (rd_widgets[i].value != 0) {
648 if (row != rd_widgets[i].ypos) {
649 row = rd_widgets[i].ypos;
650 l = l1;
652 rd_widgets[i].xpos = l;
653 l+= str_term_width1 (rd_widgets[i].text) + 4;
656 /* Abort button is centered */
657 rd_widgets[1].xpos =
658 (rd_xlen - str_term_width1 (rd_widgets[1].text) - 3) / 2;
660 #endif /* ENABLE_NLS */
662 ui = ctx->ui;
664 if (mode == Foreground)
665 title = _(" File exists ");
666 else
667 title = _(" Background process: File exists ");
669 /* FIXME - missing help node */
670 ui->replace_dlg =
671 create_dlg (0, 0, 16, rd_xlen, alarm_colors, NULL, "[Replace]",
672 title, DLG_CENTER | DLG_REVERSE);
675 ADD_RD_LABEL (ui, 0,
676 str_trunc (ui->replace_filename,
677 rd_trunc - str_term_width1 (rd_widgets[0].text)), 0);
678 ADD_RD_BUTTON (1);
680 ADD_RD_BUTTON (2);
681 ADD_RD_BUTTON (3);
682 ADD_RD_BUTTON (4);
683 ADD_RD_BUTTON (5);
684 ADD_RD_LABEL (ui, 6, 0, 0);
686 /* "this target..." widgets */
687 if (!S_ISDIR (ui->d_stat->st_mode)) {
688 if ((ctx->operation == OP_COPY) && (ui->d_stat->st_size != 0)
689 && (ui->s_stat->st_size > ui->d_stat->st_size))
690 ADD_RD_BUTTON (7); /* reget */
692 ADD_RD_BUTTON (8); /* Overwrite all targets? */
694 ADD_RD_BUTTON (9);
695 ADD_RD_BUTTON (10);
696 ADD_RD_LABEL (ui, 11, 0, 0);
698 ADD_RD_LABEL (ui, 12, file_date (ui->d_stat->st_mtime),
699 (off_t) ui->d_stat->st_size);
700 ADD_RD_LABEL (ui, 13, file_date (ui->s_stat->st_mtime),
701 (off_t) ui->s_stat->st_size);
704 void
705 file_progress_set_stalled_label (FileOpContext *ctx, const char *stalled_msg)
707 FileOpContextUI *ui;
709 g_return_if_fail (ctx != NULL);
711 if (ctx->ui == NULL)
712 return;
714 ui = ctx->ui;
716 label_set_text (ui->stalled_label, stalled_msg);
719 FileProgressStatus
720 file_progress_real_query_replace (FileOpContext *ctx,
721 enum OperationMode mode, const char *destname,
722 struct stat *_s_stat,
723 struct stat *_d_stat)
725 FileOpContextUI *ui;
727 g_return_val_if_fail (ctx != NULL, FILE_CONT);
728 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
730 ui = ctx->ui;
732 if (ui->replace_result < REPLACE_ALWAYS) {
733 ui->replace_filename = destname;
734 ui->s_stat = _s_stat;
735 ui->d_stat = _d_stat;
736 init_replace (ctx, mode);
737 run_dlg (ui->replace_dlg);
738 ui->replace_result = ui->replace_dlg->ret_value;
739 if (ui->replace_result == B_CANCEL)
740 ui->replace_result = REPLACE_ABORT;
741 destroy_dlg (ui->replace_dlg);
744 switch (ui->replace_result) {
745 case REPLACE_UPDATE:
746 do_refresh ();
747 if (_s_stat->st_mtime > _d_stat->st_mtime)
748 return FILE_CONT;
749 else
750 return FILE_SKIP;
752 case REPLACE_SIZE:
753 do_refresh ();
754 if (_s_stat->st_size == _d_stat->st_size)
755 return FILE_SKIP;
756 else
757 return FILE_CONT;
759 case REPLACE_REGET:
760 /* Careful: we fall through and set do_append */
761 ctx->do_reget = _d_stat->st_size;
763 case REPLACE_APPEND:
764 ctx->do_append = 1;
766 case REPLACE_YES:
767 case REPLACE_ALWAYS:
768 do_refresh ();
769 return FILE_CONT;
770 case REPLACE_NO:
771 case REPLACE_NEVER:
772 do_refresh ();
773 return FILE_SKIP;
774 case REPLACE_ABORT:
775 default:
776 return FILE_ABORT;
780 #define FMDY 13
781 #define FMD_XLEN 64
782 extern int fmd_xlen;
783 static QuickWidget fmd_widgets[] = {
785 #define FMCB0 FMDC
786 #define FMCB12 0
787 #define FMCB11 1
788 /* follow symlinks and preserve Attributes must be the first */
789 {quick_checkbox, 3, 64, 8, FMDY, N_("preserve &Attributes"), 9, 0,
790 0 /* &op_preserve */ , 0, NULL, NULL, NULL},
791 {quick_checkbox, 3, 64, 7, FMDY, N_("follow &Links"), 7, 0,
792 0 /* &file_mask_op_follow_links */ , 0, NULL, NULL, NULL},
793 {quick_label, 3, 64, 5, FMDY, N_("to:"), 0, 0, 0, 0, NULL, NULL, NULL},
794 {quick_checkbox, 37, 64, 4, FMDY, N_("&Using shell patterns"), 0, 0,
795 0 /* &source_easy_patterns */ , 0, NULL, NULL, NULL},
796 {quick_input, 3, 64, 3, FMDY, "", 58,
797 0, 0, 0, "input-def", NULL, NULL},
798 #define FMDI1 4
799 #define FMDI2 5
800 #define FMDC 3
801 {quick_input, 3, 64, 6, FMDY, "", 58, 0,
802 0, 0, "input2", NULL, NULL},
803 #define FMDI0 6
804 {quick_label, 3, 64, 2, FMDY, "", 0, 0, 0, 0, NULL, NULL, NULL},
805 #define FMBRGT 7
806 {quick_button, 42, 64, 9, FMDY, N_("&Cancel"), 0, B_CANCEL, 0, 0,
807 NULL, NULL, NULL},
808 #undef SKIP
809 #ifdef WITH_BACKGROUND
810 # define SKIP 5
811 # define FMCB21 11
812 # define FMCB22 10
813 # define FMBLFT 9
814 # define FMBMID 8
815 {quick_button, 25, 64, 9, FMDY, N_("&Background"), 0, B_USER, 0, 0,
816 NULL, NULL, NULL},
817 #else /* WITH_BACKGROUND */
818 # define SKIP 4
819 # define FMCB21 10
820 # define FMCB22 9
821 # define FMBLFT 8
822 # undef FMBMID
823 #endif
824 {quick_button, 14, 64, 9, FMDY, N_("&OK"), 0, B_ENTER, 0, 0, NULL, NULL, NULL},
825 {quick_checkbox, 42, 64, 8, FMDY, N_("&Stable Symlinks"), 0, 0,
826 0 /* &file_mask_stable_symlinks */ , 0, NULL, NULL, NULL},
827 {quick_checkbox, 31, 64, 7, FMDY, N_("&Dive into subdir if exists"), 0,
828 0, 0 /* &dive_into_subdirs */ , 0, NULL, NULL, NULL},
829 NULL_QuickWidget
832 static int
833 is_wildcarded (char *p)
835 for (; *p; p++) {
836 if (*p == '*')
837 return 1;
838 else if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
839 return 1;
841 return 0;
844 void
845 fmd_init_i18n (int force)
847 #ifdef ENABLE_NLS
848 static int initialized = FALSE;
849 register int i;
850 int len;
852 if (initialized && !force)
853 return;
855 for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
856 op_names[i] = _(op_names[i]);
858 i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
859 while (i--)
860 if (fmd_widgets[i].text[0] != '\0')
861 fmd_widgets[i].text = _(fmd_widgets[i].text);
863 len = str_term_width1 (fmd_widgets[FMCB11].text)
864 + str_term_width1 (fmd_widgets[FMCB21].text) + 15;
865 fmd_xlen = max (fmd_xlen, len);
867 len = str_term_width1 (fmd_widgets[FMCB12].text)
868 + str_term_width1 (fmd_widgets[FMCB22].text) + 15;
869 fmd_xlen = max (fmd_xlen, len);
871 len = str_term_width1 (fmd_widgets[FMBRGT].text)
872 + str_term_width1 (fmd_widgets[FMBLFT].text) + 11;
874 #ifdef FMBMID
875 len+= str_term_width1 (fmd_widgets[FMBMID].text) + 6;
876 #endif
878 fmd_xlen = max (fmd_xlen, len + 4);
880 len = (fmd_xlen - (len + 6)) / 2;
881 i = fmd_widgets[FMBLFT].relative_x = len + 3;
882 i+= str_term_width1 (fmd_widgets[FMBLFT].text) + 8;
884 #ifdef FMBMID
885 fmd_widgets[FMBMID].relative_x = i;
886 i+= str_term_width1 (fmd_widgets[FMBMID].text) + 6;
887 #endif
889 fmd_widgets[FMBRGT].relative_x = i;
891 #define chkbox_xpos(i) \
892 fmd_widgets [i].relative_x = fmd_xlen - str_term_width1 (fmd_widgets [i].text) - 6
894 chkbox_xpos (FMCB0);
895 chkbox_xpos (FMCB21);
896 chkbox_xpos (FMCB22);
898 if (fmd_xlen != FMD_XLEN) {
899 i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
900 while (i--)
901 fmd_widgets[i].x_divisions = fmd_xlen;
903 fmd_widgets[FMDI1].hotkey_pos =
904 fmd_widgets[FMDI2].hotkey_pos = fmd_xlen - 6;
906 #undef chkbox_xpos
908 initialized = TRUE;
909 #endif /* !ENABLE_NLS */
912 char *
913 file_mask_dialog (FileOpContext *ctx, FileOperation operation, const char *text,
914 const char *def_text, int only_one, int *do_background)
916 int source_easy_patterns = easy_patterns;
917 char *source_mask, *orig_mask, *dest_dir, *tmp;
918 char *def_text_secure;
919 struct stat buf;
920 int val;
921 QuickDialog Quick_input;
923 g_return_val_if_fail (ctx != NULL, NULL);
925 fmd_init_i18n (FALSE);
927 /* unselect checkbox if target filesystem don't support attributes */
928 ctx->op_preserve = filegui__check_attrs_on_fs(def_text);
930 /* Set up the result pointers */
932 fmd_widgets[FMCB12].result = &ctx->op_preserve;
933 fmd_widgets[FMCB11].result = &ctx->follow_links;
934 fmd_widgets[FMCB22].result = &ctx->stable_symlinks;
935 fmd_widgets[FMCB21].result = &ctx->dive_into_subdirs;
937 /* filter out a possible password from def_text */
938 tmp = strip_password (g_strdup (def_text), 1);
939 if (source_easy_patterns)
940 def_text_secure = strutils_glob_escape (tmp);
941 else
942 def_text_secure = strutils_regex_escape (tmp);
943 g_free (tmp);
945 /* Create the dialog */
947 ctx->stable_symlinks = 0;
948 fmd_widgets[FMDC].result = &source_easy_patterns;
949 fmd_widgets[FMDI1].text = easy_patterns ? "*" : "^\\(.*\\)$";
950 Quick_input.xlen = fmd_xlen;
951 Quick_input.xpos = -1;
952 Quick_input.title = op_names[operation];
953 Quick_input.help = "[Mask Copy/Rename]";
954 Quick_input.ylen = FMDY;
955 Quick_input.i18n = 1;
956 Quick_input.widgets = fmd_widgets;
957 fmd_widgets[FMDI0].text = text;
958 fmd_widgets[FMDI2].text = def_text_secure;
959 fmd_widgets[FMDI2].str_result = &dest_dir;
960 fmd_widgets[FMDI1].str_result = &source_mask;
962 *do_background = 0;
964 ask_file_mask:
965 val = quick_dialog_skip (&Quick_input, SKIP);
967 if (val == B_CANCEL) {
968 g_free (def_text_secure);
969 return NULL;
972 if (ctx->follow_links)
973 ctx->stat_func = mc_stat;
974 else
975 ctx->stat_func = mc_lstat;
977 if (ctx->op_preserve) {
978 ctx->preserve = 1;
979 ctx->umask_kill = 0777777;
980 ctx->preserve_uidgid = (geteuid () == 0) ? 1 : 0;
981 } else {
982 int i;
983 ctx->preserve = ctx->preserve_uidgid = 0;
984 i = umask (0);
985 umask (i);
986 ctx->umask_kill = i ^ 0777777;
989 if (!dest_dir || !*dest_dir) {
990 g_free (def_text_secure);
991 g_free (source_mask);
992 return dest_dir;
995 ctx->search_handle = mc_search_new(source_mask,-1);
997 if (ctx->search_handle == NULL) {
998 message (D_ERROR, MSG_ERROR, _("Invalid source pattern `%s'"),
999 source_mask);
1000 g_free (dest_dir);
1001 g_free (source_mask);
1002 goto ask_file_mask;
1005 g_free (def_text_secure);
1006 g_free (source_mask);
1008 ctx->search_handle->is_case_sentitive = TRUE;
1009 if (source_easy_patterns)
1010 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1011 else
1012 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1014 tmp = dest_dir;
1015 dest_dir = tilde_expand (tmp);
1016 g_free (tmp);
1018 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1019 if (ctx->dest_mask == NULL)
1020 ctx->dest_mask = dest_dir;
1021 else
1022 ctx->dest_mask++;
1023 orig_mask = ctx->dest_mask;
1024 if (!*ctx->dest_mask
1025 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1026 && (!only_one
1027 || (!mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode))))
1028 || (ctx->dive_into_subdirs
1029 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1030 || (only_one && !mc_stat (dest_dir, &buf)
1031 && S_ISDIR (buf.st_mode)))))
1032 ctx->dest_mask = g_strdup ("\\0");
1033 else {
1034 ctx->dest_mask = g_strdup (ctx->dest_mask);
1035 *orig_mask = '\0';
1037 if (!*dest_dir) {
1038 g_free (dest_dir);
1039 dest_dir = g_strdup ("./");
1041 if (val == B_USER)
1042 *do_background = 1;
1044 return dest_dir;