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
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
44 * \brief Source: file management GUI for the text mode edition
47 /* {{{ Include files */
55 #include <sys/types.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
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>
73 # define STRUCT_STATFS struct statfs
74 # define STATFS statfs
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 */
99 MSDOS_SUPER_MAGIC
= 0x4d44,
100 NTFS_SB_MAGIC
= 0x5346544e,
101 NTFS_3G_MAGIC
= 0x65735546,
102 PROC_SUPER_MAGIC
= 0x9fa0,
103 SMB_SUPER_MAGIC
= 0x517B,
104 NCP_SUPER_MAGIC
= 0x564c,
105 USBDEVICE_SUPER_MAGIC
= 0x9fa2
106 } filegui_nonattrs_fs_t
;
108 /* Hack: the vfs code should not rely on this */
109 #define WITH_FULL_PATHS 1
111 /* Used for button result values */
113 REPLACE_YES
= B_USER
,
124 /* This structure describes the UI and internal data required by a file
129 gboolean showing_eta
;
130 gboolean showing_bps
;
133 /* Dialog and widgets for the operation progress window */
135 WLabel
*file_label
[2];
136 WLabel
*file_string
[2];
137 WLabel
*progress_label
[3];
138 WGauge
*progress_gauge
[3];
141 WLabel
*stalled_label
;
143 /* Query replace dialog */
144 Dlg_head
*replace_dlg
;
145 const char *replace_filename
;
146 replace_action_t replace_result
;
148 struct stat
*s_stat
, *d_stat
;
152 /* Used to save the hint line */
153 static int last_hint_line
;
155 /* File operate window sizes */
159 #define WX_ETA_EXTRA 12
161 #define FCOPY_GAUGE_X 14
162 #define FCOPY_LABEL_X 5
165 filegui__check_attrs_on_fs (const char *fs_path
)
170 if (!setup_copymove_persistent_attr
)
173 if (STATFS (fs_path
, &stfs
) != 0)
177 switch ((filegui_nonattrs_fs_t
) stfs
.f_type
)
179 case MSDOS_SUPER_MAGIC
:
182 case PROC_SUPER_MAGIC
:
183 case SMB_SUPER_MAGIC
:
184 case NCP_SUPER_MAGIC
:
185 case USBDEVICE_SUPER_MAGIC
:
188 # elif defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) \
189 || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
190 if (!strcmp(stfs
.f_fstypename
, "msdos")
191 || !strcmp(stfs
.f_fstypename
, "msdosfs")
192 || !strcmp(stfs
.f_fstypename
, "ntfs")
193 || !strcmp(stfs
.f_fstypename
, "procfs")
194 || !strcmp(stfs
.f_fstypename
, "smbfs")
195 || strstr(stfs
.f_fstypename
, "fusefs"))
197 # elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
198 if (!strcmp(stfs
.f_basetype
, "pcfs")
199 || !strcmp(stfs
.f_basetype
, "ntfs")
200 || !strcmp(stfs
.f_basetype
, "proc")
201 || !strcmp(stfs
.f_basetype
, "smbfs")
202 || !strcmp(stfs
.f_basetype
, "fuse"))
210 static FileProgressStatus
211 check_progress_buttons (FileOpContext
*ctx
)
222 event
.x
= -1; /* Don't show the GPM cursor */
223 c
= tty_get_event (&event
, FALSE
, FALSE
);
227 /* Reinitialize to avoid old values after events other than
228 selecting a button */
229 ui
->op_dlg
->ret_value
= FILE_CONT
;
231 dlg_process_event (ui
->op_dlg
, c
, &event
);
232 switch (ui
->op_dlg
->ret_value
) {
243 /* {{{ File progress display routines */
246 file_op_context_create_ui_without_init (FileOpContext
*ctx
, gboolean with_eta
)
255 g_return_if_fail (ctx
!= NULL
);
256 g_return_if_fail (ctx
->ui
== NULL
);
258 ui
= g_new0 (FileOpContextUI
, 1);
261 minus
= verbose
? 0 : 3;
262 eta_offset
= with_eta
? (WX_ETA_EXTRA
) / 2 : 0;
267 ctx
->recursive_result
= RECURSIVE_YES
;
269 ui
->replace_result
= REPLACE_YES
;
270 ui
->showing_eta
= with_eta
;
271 ui
->showing_bps
= with_eta
;
272 ui
->eta_extra
= with_eta
? WX_ETA_EXTRA
: 0;
273 x_size
= (WX
+ 4) + ui
->eta_extra
;
276 create_dlg (0, 0, WY
- minus
+ 4, x_size
, dialog_colors
, NULL
,
277 NULL
, op_names
[ctx
->operation
],
278 DLG_CENTER
| DLG_REVERSE
);
280 last_hint_line
= the_hint
->widget
.y
;
281 if ((ui
->op_dlg
->y
+ ui
->op_dlg
->lines
) > last_hint_line
)
282 the_hint
->widget
.y
= ui
->op_dlg
->y
+ ui
->op_dlg
->lines
+ 1;
284 add_widget (ui
->op_dlg
,
285 button_new (BY
- minus
, WX
- 19 + eta_offset
, FILE_ABORT
,
286 NORMAL_BUTTON
, _("&Abort"), 0));
287 add_widget (ui
->op_dlg
,
288 button_new (BY
- minus
, 14 + eta_offset
, FILE_SKIP
,
289 NORMAL_BUTTON
, _("&Skip"), 0));
291 add_widget (ui
->op_dlg
, ui
->progress_gauge
[2] =
292 gauge_new (7, FCOPY_GAUGE_X
, 0, 100, 0));
293 add_widget (ui
->op_dlg
, ui
->progress_label
[2] =
294 label_new (7, FCOPY_LABEL_X
, fifteen
));
295 add_widget (ui
->op_dlg
, ui
->bps_label
= label_new (7, WX
, ""));
297 add_widget (ui
->op_dlg
, ui
->progress_gauge
[1] =
298 gauge_new (8, FCOPY_GAUGE_X
, 0, 100, 0));
299 add_widget (ui
->op_dlg
, ui
->progress_label
[1] =
300 label_new (8, FCOPY_LABEL_X
, fifteen
));
301 add_widget (ui
->op_dlg
, ui
->stalled_label
= label_new (8, WX
, ""));
303 add_widget (ui
->op_dlg
, ui
->progress_gauge
[0] =
304 gauge_new (6, FCOPY_GAUGE_X
, 0, 100, 0));
305 add_widget (ui
->op_dlg
, ui
->progress_label
[0] =
306 label_new (6, FCOPY_LABEL_X
, fifteen
));
307 add_widget (ui
->op_dlg
, ui
->eta_label
= label_new (6, WX
, ""));
309 add_widget (ui
->op_dlg
, ui
->file_string
[1] =
310 label_new (4, FCOPY_GAUGE_X
, sixty
));
311 add_widget (ui
->op_dlg
, ui
->file_label
[1] =
312 label_new (4, FCOPY_LABEL_X
, fifteen
));
313 add_widget (ui
->op_dlg
, ui
->file_string
[0] =
314 label_new (3, FCOPY_GAUGE_X
, sixty
));
315 add_widget (ui
->op_dlg
, ui
->file_label
[0] =
316 label_new (3, FCOPY_LABEL_X
, fifteen
));
320 file_op_context_create_ui (FileOpContext
*ctx
, gboolean with_eta
)
324 g_return_if_fail (ctx
!= NULL
);
325 g_return_if_fail (ctx
->ui
== NULL
);
327 file_op_context_create_ui_without_init (ctx
, with_eta
);
330 /* We will manage the dialog without any help, that's why
331 we have to call init_dlg */
332 init_dlg (ui
->op_dlg
);
333 ui
->op_dlg
->running
= 1;
337 file_op_context_destroy_ui (FileOpContext
*ctx
)
341 g_return_if_fail (ctx
!= NULL
);
346 dlg_run_done (ui
->op_dlg
);
347 destroy_dlg (ui
->op_dlg
);
351 the_hint
->widget
.y
= last_hint_line
;
356 static FileProgressStatus
357 show_no_bar (FileOpContext
*ctx
, int n
)
367 label_set_text (ui
->progress_label
[n
], "");
368 gauge_show (ui
->progress_gauge
[n
], 0);
370 return check_progress_buttons (ctx
);
373 static FileProgressStatus
374 show_bar (FileOpContext
*ctx
, int n
, double done
, double total
)
384 * Gauge needs integers, so give it with integers between 0 and 1023.
385 * This precision should be quite reasonable.
387 gauge_set_value (ui
->progress_gauge
[n
], 1024,
388 (int) (1024 * done
/ total
));
389 gauge_show (ui
->progress_gauge
[n
], 1);
390 return check_progress_buttons (ctx
);
394 file_eta_show (FileOpContext
*ctx
)
396 int eta_hours
, eta_mins
, eta_s
;
397 char eta_buffer
[BUF_TINY
];
405 if (!ui
->showing_eta
)
408 if (ctx
->eta_secs
> 0.5) {
409 eta_hours
= ctx
->eta_secs
/ (60 * 60);
410 eta_mins
= (ctx
->eta_secs
- (eta_hours
* 60 * 60)) / 60;
411 eta_s
= ctx
->eta_secs
- (eta_hours
* 60 * 60 + eta_mins
* 60);
412 g_snprintf (eta_buffer
, sizeof (eta_buffer
), _("ETA %d:%02d.%02d"),
413 eta_hours
, eta_mins
, eta_s
);
417 label_set_text (ui
->eta_label
, eta_buffer
);
421 file_bps_show (FileOpContext
*ctx
)
423 char bps_buffer
[BUF_TINY
];
431 if (!ui
->showing_bps
)
434 if (ctx
->bps
> 1024 * 1024) {
435 g_snprintf (bps_buffer
, sizeof (bps_buffer
), _("%.2f MB/s"),
436 ctx
->bps
/ (1024 * 1024.0));
437 } else if (ctx
->bps
> 1024) {
438 g_snprintf (bps_buffer
, sizeof (bps_buffer
), _("%.2f KB/s"),
440 } else if (ctx
->bps
> 1) {
441 g_snprintf (bps_buffer
, sizeof (bps_buffer
), _("%ld B/s"),
446 label_set_text (ui
->bps_label
, bps_buffer
);
450 file_progress_show (FileOpContext
*ctx
, off_t done
, off_t total
)
454 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
462 return check_progress_buttons (ctx
);
464 label_set_text (ui
->progress_label
[0], _("File"));
467 return show_bar (ctx
, 0, done
, total
);
469 return show_no_bar (ctx
, 0);
473 file_progress_show_count (FileOpContext
*ctx
, off_t done
, off_t total
)
477 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
485 return check_progress_buttons (ctx
);
487 label_set_text (ui
->progress_label
[1], _("Count"));
488 return show_bar (ctx
, 1, done
, total
);
490 return show_no_bar (ctx
, 1);
494 file_progress_show_bytes (FileOpContext
*ctx
, double done
, double total
)
498 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
506 return check_progress_buttons (ctx
);
508 label_set_text (ui
->progress_label
[2], _("Bytes"));
509 return show_bar (ctx
, 2, done
, total
);
511 return show_no_bar (ctx
, 2);
516 #define truncFileString(ui, s) str_trunc (s, ui->eta_extra + 47)
517 #define truncFileStringSecure(ui, s) path_trunc (s, ui->eta_extra + 47)
520 file_progress_show_source (FileOpContext
*ctx
, const char *s
)
524 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
532 #ifdef WITH_FULL_PATHS
533 int i
= strlen (current_panel
->cwd
);
535 /* We remove the full path we have added before */
536 if (!strncmp (s
, current_panel
->cwd
, i
)) {
537 if (s
[i
] == PATH_SEP
)
540 #endif /* WITH_FULL_PATHS */
542 label_set_text (ui
->file_label
[0], _("Source"));
543 label_set_text (ui
->file_string
[0], truncFileString (ui
, s
));
544 return check_progress_buttons (ctx
);
546 label_set_text (ui
->file_label
[0], "");
547 label_set_text (ui
->file_string
[0], "");
548 return check_progress_buttons (ctx
);
553 file_progress_show_target (FileOpContext
*ctx
, const char *s
)
557 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
565 label_set_text (ui
->file_label
[1], _("Target"));
566 label_set_text (ui
->file_string
[1], truncFileStringSecure (ui
, s
));
567 return check_progress_buttons (ctx
);
569 label_set_text (ui
->file_label
[1], "");
570 label_set_text (ui
->file_string
[1], "");
571 return check_progress_buttons (ctx
);
576 file_progress_show_deleting (FileOpContext
*ctx
, const char *s
)
580 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
587 label_set_text (ui
->file_label
[0], _("Deleting"));
588 label_set_text (ui
->file_label
[0], truncFileStringSecure (ui
, s
));
589 return check_progress_buttons (ctx
);
593 * FIXME: probably it is better to replace this with quick dialog machinery,
594 * but actually I'm not familiar with it and have not much time :(
597 static replace_action_t
598 overwrite_query_dialog (FileOpContext
*ctx
, enum OperationMode mode
)
600 #define ADD_RD_BUTTON(i)\
601 add_widget (ui->replace_dlg,\
602 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value,\
603 NORMAL_BUTTON, rd_widgets [i].text, 0))
605 #define ADD_RD_LABEL(i, p1, p2)\
606 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2);\
607 add_widget (ui->replace_dlg,\
608 label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer))
611 const int rd_ylen
= 17;
617 int value
; /* 0 for labels */
619 /* 0 */ { N_("Target file already exists!"), 3, 4, 0 },
620 /* 1 */ { "%s", 4, 4, 0 },
621 #if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || (defined _LARGE_FILES && _LARGE_FILES)
622 /* 2 */ { N_("Source date: %s, size %llu"), 6, 4, 0 },
623 /* 3 */ { N_("Target date: %s, size %llu"), 7, 4, 0 },
625 /* 2 */ { N_("Source date: %s, size %u"), 6, 4, 0 },
626 /* 3 */ { N_("Target date: %s, size %u"), 7, 4, 0 },
628 /* 4 */ { N_("&Abort"), 14, 25, REPLACE_ABORT
},
629 /* 5 */ { N_("If &size differs"), 12, 28, REPLACE_SIZE
},
630 /* 6 */ { N_("Non&e"), 11, 47, REPLACE_NEVER
},
631 /* 7 */ { N_("&Update"), 11, 36, REPLACE_UPDATE
},
632 /* 8 */ { N_("A&ll"), 11, 28, REPLACE_ALWAYS
},
633 /* 9 */ { N_("Overwrite all targets?"), 11, 4, 0 },
634 /* 10 */ { N_("&Reget"), 10, 28, REPLACE_REGET
},
635 /* 11 */ { N_("A&ppend"), 9, 45, REPLACE_APPEND
},
636 /* 12 */ { N_("&No"), 9, 37, REPLACE_NO
},
637 /* 13 */ { N_("&Yes"), 9, 28, REPLACE_YES
},
638 /* 14 */ { N_("Overwrite this target?"), 9, 4, 0 }
641 const int num
= sizeof (rd_widgets
) / sizeof (rd_widgets
[0]);
644 FileOpContextUI
*ui
= ctx
->ui
;
646 char buffer
[BUF_SMALL
];
648 const char *stripped_name
= strip_home_and_password (ui
->replace_filename
);
649 int stripped_name_len
;
653 widgets_len
= g_new0 (int, num
);
655 if (mode
== Foreground
)
656 title
= _(" File exists ");
658 title
= _(" Background process: File exists ");
660 stripped_name_len
= str_term_width1 (stripped_name
);
663 int i
, l1
, l2
, l
, row
;
665 for (i
= 0; i
< num
; i
++) {
667 if (i
!= 1) /* skip filename */
668 rd_widgets
[i
].text
= _(rd_widgets
[i
].text
);
669 #endif /* ENABLE_NLS */
670 widgets_len
[i
] = str_term_width1 (rd_widgets
[i
].text
);
674 * longest of "Overwrite..." labels
675 * (assume "Target date..." are short enough)
677 l1
= max (widgets_len
[9], widgets_len
[14]);
679 /* longest of button rows */
681 for (row
= l
= l2
= 0; i
--;)
682 if (rd_widgets
[i
].value
!= 0) {
683 if (row
!= rd_widgets
[i
].ypos
) {
684 row
= rd_widgets
[i
].ypos
;
688 l
+= widgets_len
[i
] + 4;
691 l2
= max (l2
, l
); /* last row */
692 rd_xlen
= max (rd_xlen
, l1
+ l2
+ 8);
693 rd_xlen
= max (rd_xlen
, str_term_width1 (title
) + 2);
694 rd_xlen
= max (rd_xlen
, min (COLS
, stripped_name_len
+ 8));
696 /* Now place widgets */
697 l1
+= 5; /* start of first button in the row */
699 for (l
= l1
, row
= 0; --i
> 1;)
700 if (rd_widgets
[i
].value
!= 0) {
701 if (row
!= rd_widgets
[i
].ypos
) {
702 row
= rd_widgets
[i
].ypos
;
705 rd_widgets
[i
].xpos
= l
;
706 l
+= widgets_len
[i
] + 4;
709 /* Abort button is centered */
710 rd_widgets
[4].xpos
= (rd_xlen
- widgets_len
[4] - 3) / 2;
713 /* FIXME - missing help node */
715 create_dlg (0, 0, rd_ylen
, rd_xlen
, alarm_colors
, NULL
, "[Replace]",
716 title
, DLG_CENTER
| DLG_REVERSE
);
718 /* prompt -- centered */
719 add_widget (ui
->replace_dlg
,
720 label_new (rd_widgets
[0].ypos
,
721 (rd_xlen
- widgets_len
[0]) / 2,
722 rd_widgets
[0].text
));
723 /* file name -- centered */
724 stripped_name
= str_trunc (stripped_name
, rd_xlen
- 8);
725 stripped_name_len
= str_term_width1 (stripped_name
);
726 add_widget (ui
->replace_dlg
,
727 label_new (rd_widgets
[1].ypos
,
728 (rd_xlen
- stripped_name_len
) / 2,
732 ADD_RD_LABEL (2, file_date (ui
->s_stat
->st_mtime
),
733 (off_t
) ui
->s_stat
->st_size
);
734 /* destination date */
735 ADD_RD_LABEL (3, file_date (ui
->d_stat
->st_mtime
),
736 (off_t
) ui
->d_stat
->st_size
);
738 ADD_RD_BUTTON (4); /* Abort */
739 ADD_RD_BUTTON (5); /* If size differs */
740 ADD_RD_BUTTON (6); /* None */
741 ADD_RD_BUTTON (7); /* Update */
742 ADD_RD_BUTTON (8); /* All" */
743 ADD_RD_LABEL (9, 0, 0); /* Overwrite all targets? */
745 /* "this target..." widgets */
746 if (!S_ISDIR (ui
->d_stat
->st_mode
)) {
747 if ((ctx
->operation
== OP_COPY
) && (ui
->d_stat
->st_size
!= 0)
748 && (ui
->s_stat
->st_size
> ui
->d_stat
->st_size
))
749 ADD_RD_BUTTON (10); /* Reget */
751 ADD_RD_BUTTON (11); /* Append */
753 ADD_RD_BUTTON (12); /* No */
754 ADD_RD_BUTTON (13); /* Yes */
755 ADD_RD_LABEL (14, 0, 0); /* Overwrite this target? */
757 result
= run_dlg (ui
->replace_dlg
);
758 destroy_dlg (ui
->replace_dlg
);
760 g_free (widgets_len
);
762 return (result
== B_CANCEL
) ? REPLACE_ABORT
: (replace_action_t
) result
;
768 file_progress_set_stalled_label (FileOpContext
*ctx
, const char *stalled_msg
)
772 g_return_if_fail (ctx
!= NULL
);
773 g_return_if_fail (ctx
->ui
!= NULL
);
776 label_set_text (ui
->stalled_label
, stalled_msg
);
780 file_progress_real_query_replace (FileOpContext
*ctx
,
781 enum OperationMode mode
, const char *destname
,
782 struct stat
*_s_stat
,
783 struct stat
*_d_stat
)
787 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
788 g_return_val_if_fail (ctx
->ui
!= NULL
, FILE_CONT
);
792 if (ui
->replace_result
< REPLACE_ALWAYS
) {
793 ui
->replace_filename
= destname
;
794 ui
->s_stat
= _s_stat
;
795 ui
->d_stat
= _d_stat
;
796 ui
->replace_result
= overwrite_query_dialog (ctx
, mode
);
799 switch (ui
->replace_result
) {
802 if (_s_stat
->st_mtime
> _d_stat
->st_mtime
)
809 if (_s_stat
->st_size
== _d_stat
->st_size
)
815 /* Careful: we fall through and set do_append */
816 ctx
->do_reget
= _d_stat
->st_size
;
819 ctx
->do_append
= TRUE
;
836 is_wildcarded (char *p
)
841 if (*p
== '\\' && p
[1] >= '1' && p
[1] <= '9')
848 file_mask_dialog (FileOpContext
*ctx
, FileOperation operation
,
850 const char *format
, const void *text
,
851 const char *def_text
, gboolean
*do_background
)
853 const size_t FMDY
= 13;
854 const size_t FMDX
= 68;
858 const size_t gap
= 1;
859 size_t b0_len
, b2_len
;
862 int source_easy_patterns
= easy_patterns
;
864 char fmd_buf
[BUF_MEDIUM
];
865 char *source_mask
, *orig_mask
, *dest_dir
, *tmp
;
866 char *def_text_secure
;
869 QuickWidget fmd_widgets
[] =
871 /* 0 */ QUICK_BUTTON (42, 64, 10, FMDY
, N_("&Cancel"), B_CANCEL
, NULL
),
872 #ifdef WITH_BACKGROUND
873 /* 1 */ QUICK_BUTTON (25, 64, 10, FMDY
, N_("&Background"), B_USER
, NULL
),
877 #endif /* WITH_BACKGROUND */
879 QUICK_BUTTON (14, FMDX
, 10, FMDY
, N_("&OK"), B_ENTER
, NULL
),
881 QUICK_CHECKBOX (42, FMDX
, 8, FMDY
, N_("&Stable Symlinks"), &ctx
->stable_symlinks
),
883 QUICK_CHECKBOX (31, FMDX
, 7, FMDY
, N_("di&Ve into subdir if exists"), &ctx
->dive_into_subdirs
),
885 QUICK_CHECKBOX (3, FMDX
, 8, FMDY
, N_("preserve &Attributes"), &ctx
->op_preserve
),
887 QUICK_CHECKBOX (3, FMDX
, 7, FMDY
, N_("follow &Links"), &ctx
->follow_links
),
889 QUICK_INPUT (3, FMDX
, 6, FMDY
, "", 58, 0, "input2", &dest_dir
),
891 QUICK_LABEL (3, FMDX
, 5, FMDY
, N_("to:")),
893 QUICK_CHECKBOX (37, FMDX
, 4, FMDY
, N_("&Using shell patterns"), &source_easy_patterns
),
895 QUICK_INPUT (3, FMDX
, 3, FMDY
, easy_patterns
? "*" : "^\\(.*\\)$", 58, 0, "input-def", &source_mask
),
897 QUICK_LABEL (3, FMDX
, 2, FMDY
, fmd_buf
),
901 g_return_val_if_fail (ctx
!= NULL
, NULL
);
905 for (i
= 0; i
<= 2 - OFFSET
; i
++)
906 fmd_widgets
[i
].u
.button
.text
= _(fmd_widgets
[i
].u
.button
.text
);
909 for (i
= 3 - OFFSET
; i
<= 9 - OFFSET
; i
++)
911 fmd_widgets
[i
].u
.checkbox
.text
= _(fmd_widgets
[i
].u
.checkbox
.text
);
912 #endif /* !ENABLE_NLS */
914 fmd_xlen
= max (FMDX
, (size_t) COLS
* 2/3);
916 len
= str_term_width1 (fmd_widgets
[6 - OFFSET
].u
.checkbox
.text
)
917 + str_term_width1 (fmd_widgets
[4 - OFFSET
].u
.checkbox
.text
) + 15;
918 fmd_xlen
= max (fmd_xlen
, len
);
920 len
= str_term_width1 (fmd_widgets
[5 - OFFSET
].u
.checkbox
.text
)
921 + str_term_width1 (fmd_widgets
[3 - OFFSET
].u
.checkbox
.text
) + 15;
922 fmd_xlen
= max (fmd_xlen
, len
);
925 b2_len
= str_term_width1 (fmd_widgets
[2 - OFFSET
].u
.button
.text
) + 6 + gap
; /* OK */
926 #ifdef WITH_BACKGROUND
927 b1_len
= str_term_width1 (fmd_widgets
[1].u
.button
.text
) + 4 + gap
; /* Background */
929 b0_len
= str_term_width1 (fmd_widgets
[0].u
.button
.text
) + 4; /* Cancel */
930 len
= b0_len
+ b1_len
+ b2_len
;
931 fmd_xlen
= min (max (fmd_xlen
, len
+ 6), (size_t) COLS
);
936 flen
= str_term_width1 (format
);
937 i
= fmd_xlen
- flen
- 4; /* FIXME */
938 g_snprintf (fmd_buf
, sizeof (fmd_buf
),
939 format
, str_trunc ((const char *) text
, i
));
941 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, *(const int *) text
);
942 fmd_xlen
= max (fmd_xlen
, (size_t) str_term_width1 (fmd_buf
) + 6);
945 for (i
= sizeof (fmd_widgets
) / sizeof (fmd_widgets
[0]); i
> 0; )
946 fmd_widgets
[--i
].x_divisions
= fmd_xlen
;
948 i
= (fmd_xlen
- len
)/2;
950 fmd_widgets
[2 - OFFSET
].relative_x
= i
;
952 #ifdef WITH_BACKGROUND
953 /* Background button */
954 fmd_widgets
[1].relative_x
= i
;
958 fmd_widgets
[0].relative_x
= i
;
960 #define chkbox_xpos(i) \
961 fmd_widgets [i].relative_x = fmd_xlen - str_term_width1 (fmd_widgets [i].u.checkbox.text) - 6
962 chkbox_xpos (3 - OFFSET
);
963 chkbox_xpos (4 - OFFSET
);
964 chkbox_xpos (9 - OFFSET
);
968 fmd_widgets
[ 7 - OFFSET
].u
.input
.len
=
969 fmd_widgets
[10 - OFFSET
].u
.input
.len
= fmd_xlen
- 6;
971 /* unselect checkbox if target filesystem don't support attributes */
972 ctx
->op_preserve
= filegui__check_attrs_on_fs (def_text
);
974 /* filter out a possible password from def_text */
975 tmp
= strip_password (g_strdup (def_text
), 1);
976 if (source_easy_patterns
)
977 def_text_secure
= strutils_glob_escape (tmp
);
979 def_text_secure
= strutils_regex_escape (tmp
);
983 fmd_widgets
[7 - OFFSET
].u
.input
.text
= def_text_secure
;
985 ctx
->stable_symlinks
= FALSE
;
986 *do_background
= FALSE
;
991 QuickDialog Quick_input
=
993 fmd_xlen
, FMDY
, -1, -1, op_names
[operation
],
994 "[Mask Copy/Rename]", fmd_widgets
, TRUE
998 val
= quick_dialog_skip (&Quick_input
, 4);
1000 if (val
== B_CANCEL
) {
1001 g_free (def_text_secure
);
1005 if (ctx
->follow_links
)
1006 ctx
->stat_func
= mc_stat
;
1008 ctx
->stat_func
= mc_lstat
;
1010 if (ctx
->op_preserve
) {
1011 ctx
->preserve
= TRUE
;
1012 ctx
->umask_kill
= 0777777;
1013 ctx
->preserve_uidgid
= (geteuid () == 0);
1016 ctx
->preserve
= ctx
->preserve_uidgid
= FALSE
;
1019 ctx
->umask_kill
= i2
^ 0777777;
1022 if ((dest_dir
== NULL
) || (*dest_dir
== '\0')) {
1023 g_free (def_text_secure
);
1024 g_free (source_mask
);
1028 ctx
->search_handle
= mc_search_new(source_mask
,-1);
1030 if (ctx
->search_handle
== NULL
) {
1031 message (D_ERROR
, MSG_ERROR
, _("Invalid source pattern `%s'"),
1034 g_free (source_mask
);
1038 g_free (def_text_secure
);
1039 g_free (source_mask
);
1041 ctx
->search_handle
->is_case_sentitive
= TRUE
;
1042 if (source_easy_patterns
)
1043 ctx
->search_handle
->search_type
= MC_SEARCH_T_GLOB
;
1045 ctx
->search_handle
->search_type
= MC_SEARCH_T_REGEX
;
1048 dest_dir
= tilde_expand (tmp
);
1051 ctx
->dest_mask
= strrchr (dest_dir
, PATH_SEP
);
1052 if (ctx
->dest_mask
== NULL
)
1053 ctx
->dest_mask
= dest_dir
;
1056 orig_mask
= ctx
->dest_mask
;
1057 if (!*ctx
->dest_mask
1058 || (!ctx
->dive_into_subdirs
&& !is_wildcarded (ctx
->dest_mask
)
1060 || (!mc_stat (dest_dir
, &buf
) && S_ISDIR (buf
.st_mode
))))
1061 || (ctx
->dive_into_subdirs
1062 && ((!only_one
&& !is_wildcarded (ctx
->dest_mask
))
1063 || (only_one
&& !mc_stat (dest_dir
, &buf
)
1064 && S_ISDIR (buf
.st_mode
)))))
1065 ctx
->dest_mask
= g_strdup ("\\0");
1067 ctx
->dest_mask
= g_strdup (ctx
->dest_mask
);
1072 dest_dir
= g_strdup ("./");
1075 *do_background
= TRUE
;