3 /* File managing. Important notes on this file:
5 * About the use of dialogs in this file:
6 * If you want to add a new dialog box (or call a routine that pops
7 * up a dialog box), you have to provide a wrapper for background
8 * operations (ie, background operations have to up-call to the parent
11 * For example, instead of using the message() routine, in this
12 * file, you should use one of the stubs that call message with the
13 * proper number of arguments (ie, message_1s, message_2s and so on).
15 * Actually, that is a rule that should be followed by any routines
16 * that may be called from this module.
20 /* File managing GUI for the text mode edition
22 * Copyright (C) 1994, 1995, 1996 The Free Software Foundation
24 * Written by: 1994, 1995 Janne Kukonlehto
25 * 1994, 1995 Fred Leeflang
26 * 1994, 1995, 1996 Miguel de Icaza
27 * 1995, 1996 Jakub Jelinek
28 * 1997 Norbert Warmuth
31 * The copy code was based in GNU's cp, and was written by:
32 * Torbjorn Granlund, David MacKenzie, and Jim Meyering.
34 * The move code was based in GNU's mv, and was written by:
35 * Mike Parker and David MacKenzie.
37 * Janne Kukonlehto added much error recovery to them for being used
38 * in an interactive program.
40 * This program is free software; you can redistribute it and/or modify
41 * it under the terms of the GNU General Public License as published by
42 * the Free Software Foundation; either version 2 of the License, or
43 * (at your option) any later version.
45 * This program is distributed in the hope that it will be useful,
46 * but WITHOUT ANY WARRANTY; without even the implied warranty of
47 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48 * GNU General Public License for more details.
50 * You should have received a copy of the GNU General Public License
51 * along with this program; if not, write to the Free Software
52 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
57 /* {{{ Include files */
60 /* Hack: the vfs code should not rely on this */
61 #define WITH_FULL_PATHS 1
63 #include <sys/types.h>
80 /* Needed by query_replace */
85 #include "widget.h" /* Note the sequence */
86 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
90 /* Needed for current_panel, other_panel and WTree */
95 #include "fileopctx.h"
98 #include "../vfs/vfs.h"
102 /* This structure describes the UI and internal data required by a file
112 /* Dialog and widgets for the operation progress window */
116 WLabel
*file_label
[2];
117 WLabel
*file_string
[2];
118 WLabel
*progress_label
[3];
119 WGauge
*progress_gauge
[3];
122 WLabel
*stalled_label
;
124 /* Query replace dialog */
126 Dlg_head
*replace_dlg
;
127 char *replace_filename
;
129 struct stat
*s_stat
, *d_stat
;
133 /* Replace dialog: color set */
134 static int replace_colors
[4];
136 /* Used to save the hint line */
137 static int last_hint_line
;
139 /* File operate window sizes */
143 #define WX_ETA_EXTRA 12
145 #define FCOPY_GAUGE_X 14
146 #define FCOPY_LABEL_X 5
148 /* Used for button result values */
150 REPLACE_YES
= B_USER
,
161 static FileProgressStatus
162 check_progress_buttons (FileOpContext
*ctx
)
173 event
.x
= -1; /* Don't show the GPM cursor */
174 c
= get_event (&event
, 0, 0);
177 dlg_process_event (ui
->op_dlg
, c
, &event
);
178 switch (ui
->op_dlg
->ret_value
) {
191 /* {{{ File progress display routines */
194 op_win_callback (struct Dlg_head
*h
, int id
, int msg
)
198 attrset (COLOR_NORMAL
);
200 draw_box (h
, 1, 2, h
->lines
-2, h
->cols
-4);
207 file_op_context_create_ui (FileOpContext
*ctx
, FileOperation op
, int with_eta
)
216 g_return_if_fail (ctx
!= NULL
);
217 g_return_if_fail (ctx
->ui
== NULL
);
219 ui
= g_new0 (FileOpContextUI
, 1);
222 minus
= verbose
? 0 : 3;
223 eta_offset
= with_eta
? (WX_ETA_EXTRA
) / 2 : 0;
228 ctx
->recursive_result
= 0;
230 ui
->replace_result
= 0;
231 ui
->showing_eta
= with_eta
;
232 ui
->showing_bps
= with_eta
;
233 ui
->eta_extra
= with_eta
? WX_ETA_EXTRA
: 0;
234 x_size
= (WX
+ 4) + ui
->eta_extra
;
236 ui
->op_dlg
= create_dlg (0, 0, WY
-minus
+4, x_size
, dialog_colors
,
237 op_win_callback
, "", "opwin", DLG_CENTER
);
239 last_hint_line
= the_hint
->widget
.y
;
240 if ((ui
->op_dlg
->y
+ ui
->op_dlg
->lines
) > last_hint_line
)
241 the_hint
->widget
.y
= ui
->op_dlg
->y
+ ui
->op_dlg
->lines
+1;
243 x_set_dialog_title (ui
->op_dlg
, "");
245 add_widget (ui
->op_dlg
, button_new (BY
-minus
, WX
- 19 + eta_offset
, FILE_ABORT
,
246 NORMAL_BUTTON
, _("&Abort"), 0, 0, "abort"));
247 add_widget (ui
->op_dlg
, button_new (BY
-minus
, 14 + eta_offset
, FILE_SKIP
,
248 NORMAL_BUTTON
, _("&Skip"), 0, 0, "skip"));
250 add_widget (ui
->op_dlg
, ui
->progress_gauge
[2] = gauge_new (7, FCOPY_GAUGE_X
, 0, 100, 0, "g-1"));
251 add_widget (ui
->op_dlg
, ui
->progress_label
[2] = label_new (7, FCOPY_LABEL_X
, fifteen
, "l-1"));
252 add_widget (ui
->op_dlg
, ui
->bps_label
= label_new (7, WX
, "", "bps-label"));
254 add_widget (ui
->op_dlg
, ui
->progress_gauge
[1] = gauge_new (8, FCOPY_GAUGE_X
, 0, 100, 0, "g-2"));
255 add_widget (ui
->op_dlg
, ui
->progress_label
[1] = label_new (8, FCOPY_LABEL_X
, fifteen
, "l-2"));
256 add_widget (ui
->op_dlg
, ui
->stalled_label
= label_new (8, WX
, "", "stalled"));
258 add_widget (ui
->op_dlg
, ui
->progress_gauge
[0] = gauge_new (6, FCOPY_GAUGE_X
, 0, 100, 0, "g-3"));
259 add_widget (ui
->op_dlg
, ui
->progress_label
[0] = label_new (6, FCOPY_LABEL_X
, fifteen
, "l-3"));
260 add_widget (ui
->op_dlg
, ui
->eta_label
= label_new (6, WX
, "", "eta_label"));
262 add_widget (ui
->op_dlg
, ui
->file_string
[1] = label_new (4, FCOPY_GAUGE_X
, sixty
, "fs-l-1"));
263 add_widget (ui
->op_dlg
, ui
->file_label
[1] = label_new (4, FCOPY_LABEL_X
, fifteen
, "fs-l-2"));
264 add_widget (ui
->op_dlg
, ui
->file_string
[0] = label_new (3, FCOPY_GAUGE_X
, sixty
, "fs-x-1"));
265 add_widget (ui
->op_dlg
, ui
->file_label
[0] = label_new (3, FCOPY_LABEL_X
, fifteen
, "fs-x-2"));
267 /* We will manage the dialog without any help, that's why
268 we have to call init_dlg */
269 init_dlg (ui
->op_dlg
);
270 ui
->op_dlg
->running
= 1;
274 file_op_context_destroy_ui (FileOpContext
*ctx
)
278 g_return_if_fail (ctx
!= NULL
);
283 dlg_run_done (ui
->op_dlg
);
284 destroy_dlg (ui
->op_dlg
);
288 the_hint
->widget
.y
= last_hint_line
;
293 static FileProgressStatus
294 show_no_bar (FileOpContext
*ctx
, int n
)
304 label_set_text (ui
->progress_label
[n
], "");
305 gauge_show (ui
->progress_gauge
[n
], 0);
307 return check_progress_buttons (ctx
);
310 static FileProgressStatus
311 show_bar (FileOpContext
*ctx
, int n
, double done
, double total
)
321 * Gauge needs integers, so give it with integers between 0 and 1023.
322 * This precision should be quite reasonable.
324 gauge_set_value (ui
->progress_gauge
[n
], 1024, (int) (1024 * done
/ total
));
325 gauge_show (ui
->progress_gauge
[n
], 1);
326 return check_progress_buttons (ctx
);
330 file_eta_show (FileOpContext
*ctx
)
332 int eta_hours
, eta_mins
, eta_s
;
333 char eta_buffer
[BUF_TINY
];
341 if (!ui
->showing_eta
)
344 if (ctx
->eta_secs
> 0.5) {
345 eta_hours
= ctx
->eta_secs
/ (60 * 60);
346 eta_mins
= (ctx
->eta_secs
- (eta_hours
* 60 * 60)) / 60;
347 eta_s
= ctx
->eta_secs
- (eta_hours
* 60 * 60 + eta_mins
* 60);
348 g_snprintf (eta_buffer
, sizeof (eta_buffer
), _("ETA %d:%02d.%02d"), eta_hours
, eta_mins
, eta_s
);
352 label_set_text (ui
->eta_label
, eta_buffer
);
356 file_bps_show (FileOpContext
*ctx
)
358 char bps_buffer
[BUF_TINY
];
366 if (!ui
->showing_bps
)
369 if (ctx
->bps
> 1024*1024) {
370 g_snprintf (bps_buffer
, sizeof (bps_buffer
), _("%.2f MB/s"), ctx
->bps
/ (1024*1024.0));
371 } else if (ctx
->bps
> 1024){
372 g_snprintf (bps_buffer
, sizeof (bps_buffer
), _("%.2f KB/s"), ctx
->bps
/ 1024.0);
373 } else if (ctx
->bps
> 1){
374 g_snprintf (bps_buffer
, sizeof (bps_buffer
), _("%ld B/s"), ctx
->bps
);
378 label_set_text (ui
->bps_label
, bps_buffer
);
382 file_progress_show (FileOpContext
*ctx
, off_t done
, off_t total
)
386 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
394 return check_progress_buttons (ctx
);
396 label_set_text (ui
->progress_label
[0], _("File"));
399 return show_bar (ctx
, 0, done
, total
);
401 return show_no_bar (ctx
, 0);
405 file_progress_show_count (FileOpContext
*ctx
, off_t done
, off_t total
)
409 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
417 return check_progress_buttons (ctx
);
419 label_set_text (ui
->progress_label
[1], _("Count"));
420 return show_bar (ctx
, 1, done
, total
);
422 return show_no_bar (ctx
, 1);
426 file_progress_show_bytes (FileOpContext
*ctx
, double done
, double total
)
430 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
438 return check_progress_buttons (ctx
);
440 label_set_text (ui
->progress_label
[2], _("Bytes"));
441 return show_bar (ctx
, 2, done
, total
);
443 return show_no_bar (ctx
, 2);
448 #define truncFileString(ui, s) name_trunc (s, ui->eta_extra + 47)
451 file_progress_show_source (FileOpContext
*ctx
, char *s
)
455 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
463 #ifdef WITH_FULL_PATHS
464 int i
= strlen (cpanel
->cwd
);
466 /* We remove the full path we have added before */
467 if (!strncmp (s
, cpanel
->cwd
, i
)){
468 if (s
[i
] == PATH_SEP
)
471 #endif /* WITH_FULL_PATHS */
473 label_set_text (ui
->file_label
[0], _("Source"));
474 label_set_text (ui
->file_string
[0], truncFileString (ui
, s
));
475 return check_progress_buttons (ctx
);
477 label_set_text (ui
->file_label
[0], "");
478 label_set_text (ui
->file_string
[0], "");
479 return check_progress_buttons (ctx
);
484 file_progress_show_target (FileOpContext
*ctx
, char *s
)
488 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
496 label_set_text (ui
->file_label
[1], _("Target"));
497 label_set_text (ui
->file_string
[1], truncFileString (ui
, s
));
498 return check_progress_buttons (ctx
);
500 label_set_text (ui
->file_label
[1], "");
501 label_set_text (ui
->file_string
[1], "");
502 return check_progress_buttons (ctx
);
507 file_progress_show_deleting (FileOpContext
*ctx
, char *s
)
511 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
518 label_set_text (ui
->file_label
[0], _("Deleting"));
519 label_set_text (ui
->file_label
[0], truncFileString (ui
, s
));
520 return check_progress_buttons (ctx
);
524 replace_callback (struct Dlg_head
*h
, int Id
, int Msg
)
528 dialog_repaint (h
, ERROR_COLOR
, ERROR_COLOR
);
537 * FIXME: probably it is better to replace this with quick dialog machinery,
538 * but actually I'm not familiar with it and have not much time :(
545 int value
; /* 0 for labels */
550 {N_("Target file \"%s\" already exists!"),
551 3, 4, 0, "target-e" },
552 {N_("&Abort"), BY
+ 3, 25, REPLACE_ABORT
, "abort" },
553 {N_("if &Size differs"),
554 BY
+ 1, 28, REPLACE_SIZE
, "if-size" },
555 {N_("non&E"), BY
, 47, REPLACE_NEVER
, "none" },
556 {N_("&Update"), BY
, 36, REPLACE_UPDATE
, "update" },
557 {N_("al&L"), BY
, 28, REPLACE_ALWAYS
, "all" },
558 {N_("Overwrite all targets?"),
559 BY
, 4, 0, "over-label" },
560 {N_("&Reget"), BY
- 1, 28, REPLACE_REGET
, "reget" },
561 {N_("ap&Pend"), BY
- 2, 45, REPLACE_APPEND
, "append" },
562 {N_("&No"), BY
- 2, 37, REPLACE_NO
, "no" },
563 {N_("&Yes"), BY
- 2, 28, REPLACE_YES
, "yes" },
564 {N_("Overwrite this target?"),
565 BY
- 2, 4, 0, "overlab" },
566 {N_("Target date: %s, size %d"),
567 6, 4, 0, "target-date" },
568 {N_("Source date: %s, size %d"),
569 5, 4, 0, "source-date" }
572 #define ADD_RD_BUTTON(i)\
573 add_widget (ui->replace_dlg,\
574 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value,\
575 NORMAL_BUTTON, rd_widgets [i].text, 0, 0, rd_widgets [i].tkname))
577 #define ADD_RD_LABEL(ui,i,p1,p2)\
578 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2);\
579 add_widget (ui->replace_dlg,\
580 label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer, rd_widgets [i].tkname))
583 init_replace (FileOpContext
*ctx
, enum OperationMode mode
)
586 char buffer
[BUF_SMALL
];
587 static int rd_xlen
= 60, rd_trunc
= X_TRUNC
;
590 static int i18n_flag
;
593 register int i
= sizeof (rd_widgets
) / sizeof (rd_widgets
[0]);
595 rd_widgets
[i
].text
= _(rd_widgets
[i
].text
);
598 *longest of "Overwrite..." labels
599 * (assume "Target date..." are short enough)
601 l1
= max (strlen (rd_widgets
[6].text
), strlen (rd_widgets
[11].text
));
603 /* longest of button rows */
604 i
= sizeof (rd_widgets
) / sizeof (rd_widgets
[0]);
605 for (row
= l
= l2
= 0; i
--;) {
606 if (rd_widgets
[i
].value
!= 0) {
607 if (row
!= rd_widgets
[i
].ypos
) {
608 row
= rd_widgets
[i
].ypos
;
612 l
+= strlen (rd_widgets
[i
].text
) + 4;
615 l2
= max (l2
, l
); /* last row */
616 rd_xlen
= max (rd_xlen
, l1
+ l2
+ 8);
617 rd_trunc
= rd_xlen
- 6;
619 /* Now place buttons */
620 l1
+= 5; /* start of first button in the row */
621 i
= sizeof (rd_widgets
) / sizeof (rd_widgets
[0]);
623 for (l
= l1
, row
= 0; --i
> 1;) {
624 if (rd_widgets
[i
].value
!= 0) {
625 if (row
!= rd_widgets
[i
].ypos
) {
626 row
= rd_widgets
[i
].ypos
;
629 rd_widgets
[i
].xpos
= l
;
630 l
+= strlen (rd_widgets
[i
].text
) + 4;
633 /* Abort button is centered */
634 rd_widgets
[1].xpos
= (rd_xlen
- strlen (rd_widgets
[1].text
) - 3) / 2;
636 #endif /* ENABLE_NLS */
640 replace_colors
[0] = ERROR_COLOR
;
641 replace_colors
[1] = COLOR_NORMAL
;
642 replace_colors
[2] = ERROR_COLOR
;
643 replace_colors
[3] = COLOR_NORMAL
;
645 ui
->replace_dlg
= create_dlg (0, 0, 16, rd_xlen
, replace_colors
, replace_callback
,
646 "[ Replace ]", "replace", DLG_CENTER
);
648 x_set_dialog_title (ui
->replace_dlg
,
651 : _(" Background process: File exists ")));
655 name_trunc (ui
->replace_filename
, rd_trunc
- strlen (rd_widgets
[0].text
)), 0);
662 ADD_RD_LABEL(ui
, 6, 0, 0);
664 /* "this target..." widgets */
665 if (!S_ISDIR (ui
->d_stat
->st_mode
)){
666 if ((ui
->d_stat
->st_size
&& ui
->s_stat
->st_size
> ui
->d_stat
->st_size
))
673 ADD_RD_LABEL(ui
, 11,0,0);
675 ADD_RD_LABEL(ui
, 12, file_date (ui
->d_stat
->st_mtime
), (int) ui
->d_stat
->st_size
);
676 ADD_RD_LABEL(ui
, 13, file_date (ui
->s_stat
->st_mtime
), (int) ui
->s_stat
->st_size
);
680 file_progress_set_stalled_label (FileOpContext
*ctx
, char *stalled_msg
)
684 g_return_if_fail (ctx
!= NULL
);
691 label_set_text (ui
->stalled_label
, stalled_msg
);
695 file_progress_real_query_replace (FileOpContext
*ctx
, enum OperationMode mode
,
696 char *destname
, struct stat
*_s_stat
,
697 struct stat
*_d_stat
)
701 g_return_val_if_fail (ctx
!= NULL
, FILE_CONT
);
702 g_return_val_if_fail (ctx
->ui
!= NULL
, FILE_CONT
);
706 if (ui
->replace_result
< REPLACE_ALWAYS
){
707 ui
->replace_filename
= destname
;
708 ui
->s_stat
= _s_stat
;
709 ui
->d_stat
= _d_stat
;
710 init_replace (ctx
, mode
);
711 run_dlg (ui
->replace_dlg
);
712 ui
->replace_result
= ui
->replace_dlg
->ret_value
;
713 if (ui
->replace_result
== B_CANCEL
)
714 ui
->replace_result
= REPLACE_ABORT
;
715 destroy_dlg (ui
->replace_dlg
);
718 switch (ui
->replace_result
){
721 if (_s_stat
->st_mtime
> _d_stat
->st_mtime
)
728 if (_s_stat
->st_size
== _d_stat
->st_size
)
734 /* Carefull: we fall through and set do_append */
735 ctx
->do_reget
= _d_stat
->st_size
;
757 static QuickWidget fmd_widgets
[] = {
762 /* follow symlinks and preserve Attributes must be the first */
763 { quick_checkbox
, 3, 64, 8, FMDY
, N_("preserve &Attributes"), 9, 0,
764 0 /* &op_preserve */, 0, "preserve" },
765 { quick_checkbox
, 3, 64, 7, FMDY
, N_("follow &Links"), 7, 0,
766 0 /* &file_mask_op_follow_links */, 0, "follow" },
767 { quick_label
, 3, 64, 5, FMDY
, N_("to:"), 0, 0, 0, 0,"to"},
768 { quick_checkbox
, 37, 64, 4, FMDY
, N_("&Using shell patterns"), 0, 0,
769 0 /* &source_easy_patterns */, 0, "using-shell" },
770 { quick_input
, 3, 64, 3, FMDY
, "", 58,
771 0, 0, 0, "input-def" },
775 { quick_input
, 3, 64, 6, FMDY
, "", 58, 0,
778 { quick_label
, 3, 64, 2, FMDY
, "", 0, 0, 0, 0, "ql" },
780 { quick_button
, 42, 64, 9, FMDY
, N_("&Cancel"), 0, B_CANCEL
, 0, 0,
783 #ifdef WITH_BACKGROUND
789 { quick_button
, 25, 64, 9, FMDY
, N_("&Background"), 0, B_USER
, 0, 0, "back" },
790 #else /* WITH_BACKGROUND */
797 { quick_button
, 14, 64, 9, FMDY
, N_("&Ok"), 0, B_ENTER
, 0, 0, "ok" },
798 { quick_checkbox
, 42, 64, 8, FMDY
, N_("&Stable Symlinks"), 0, 0,
799 0 /* &file_mask_stable_symlinks */, 0, "stab-sym" },
800 { quick_checkbox
, 31, 64, 7, FMDY
, N_("&Dive into subdir if exists"), 0, 0,
801 0 /* &dive_into_subdirs */, 0, "dive" },
805 fmd_init_i18n (int force
)
808 static int initialized
= FALSE
;
812 if (initialized
&& !force
)
815 for (i
= sizeof (op_names
) / sizeof (op_names
[0]); i
--;)
816 op_names
[i
] = _(op_names
[i
]);
818 i
= sizeof (fmd_widgets
) / sizeof (fmd_widgets
[0]) - 1;
820 if (fmd_widgets
[i
].text
[0] != '\0')
821 fmd_widgets
[i
].text
= _(fmd_widgets
[i
].text
);
823 len
= strlen (fmd_widgets
[FMCB11
].text
)
824 + strlen (fmd_widgets
[FMCB21
].text
) + 15;
825 fmd_xlen
= max (fmd_xlen
, len
);
827 len
= strlen (fmd_widgets
[FMCB12
].text
)
828 + strlen (fmd_widgets
[FMCB22
].text
) + 15;
829 fmd_xlen
= max (fmd_xlen
, len
);
831 len
= strlen (fmd_widgets
[FMBRGT
].text
)
832 + strlen (fmd_widgets
[FMBLFT
].text
) + 11;
835 len
+= strlen (fmd_widgets
[FMBMID
].text
) + 6;
838 fmd_xlen
= max (fmd_xlen
, len
+ 4);
840 len
= (fmd_xlen
- (len
+ 6)) / 2;
841 i
= fmd_widgets
[FMBLFT
].relative_x
= len
+ 3;
842 i
+= strlen (fmd_widgets
[FMBLFT
].text
) + 8;
845 fmd_widgets
[FMBMID
].relative_x
= i
;
846 i
+= strlen (fmd_widgets
[FMBMID
].text
) + 6;
849 fmd_widgets
[FMBRGT
].relative_x
= i
;
851 #define chkbox_xpos(i) \
852 fmd_widgets [i].relative_x = fmd_xlen - strlen (fmd_widgets [i].text) - 6
858 if (fmd_xlen
!= FMD_XLEN
)
860 i
= sizeof (fmd_widgets
) / sizeof (fmd_widgets
[0]) - 1;
862 fmd_widgets
[i
].x_divisions
= fmd_xlen
;
864 fmd_widgets
[FMDI1
].hotkey_pos
=
865 fmd_widgets
[FMDI2
].hotkey_pos
= fmd_xlen
- 6;
870 #endif /* !ENABLE_NLS */
874 file_mask_dialog (FileOpContext
*ctx
, FileOperation operation
, char *text
,
875 char *def_text
, int only_one
, int *do_background
)
877 int source_easy_patterns
= easy_patterns
;
878 char *source_mask
, *orig_mask
, *dest_dir
;
882 QuickDialog Quick_input
;
884 g_return_val_if_fail (ctx
!= NULL
, NULL
);
886 message_3s (1, __FUNCTION__
, "text = `%s' \n def_text = `%s'", text
, def_text
);
888 fmd_init_i18n (FALSE
);
890 /* Set up the result pointers */
892 fmd_widgets
[FMCB12
].result
= &ctx
->op_preserve
;
893 fmd_widgets
[FMCB11
].result
= &ctx
->follow_links
;
894 fmd_widgets
[FMCB22
].result
= &ctx
->stable_symlinks
;
895 fmd_widgets
[FMCB21
].result
= &ctx
->dive_into_subdirs
;
897 /* Create the dialog */
899 ctx
->stable_symlinks
= 0;
900 fmd_widgets
[FMDC
].result
= &source_easy_patterns
;
901 fmd_widgets
[FMDI1
].text
= easy_patterns
? "*" : "^\\(.*\\)$";
902 Quick_input
.xlen
= fmd_xlen
;
903 Quick_input
.xpos
= -1;
904 Quick_input
.title
= op_names
[operation
];
905 Quick_input
.help
= "[Mask Copy/Rename]";
906 Quick_input
.ylen
= FMDY
;
907 Quick_input
.i18n
= 1;
909 if (operation
== OP_COPY
) {
910 Quick_input
.class = "quick_file_mask_copy";
911 } else { /* operation == OP_MOVE */
912 Quick_input
.class = "quick_file_mask_move";
914 Quick_input
.widgets
= fmd_widgets
;
915 fmd_widgets
[FMDI0
].text
= text
;
916 fmd_widgets
[FMDI2
].text
= def_text
;
917 fmd_widgets
[FMDI2
].str_result
= &dest_dir
;
918 fmd_widgets
[FMDI1
].str_result
= &source_mask
;
923 if ((val
= quick_dialog_skip (&Quick_input
, SKIP
)) == B_CANCEL
)
926 if (ctx
->follow_links
)
927 ctx
->stat_func
= (mc_stat_fn
) mc_stat
;
929 ctx
->stat_func
= (mc_stat_fn
) mc_lstat
;
931 if (ctx
->op_preserve
) {
933 ctx
->umask_kill
= 0777777;
934 ctx
->preserve_uidgid
= (geteuid () == 0) ? 1 : 0;
937 ctx
->preserve
= ctx
->preserve_uidgid
= 0;
940 ctx
->umask_kill
= i
^ 0777777;
943 orig_mask
= source_mask
;
944 if (!dest_dir
|| !*dest_dir
){
946 g_free (source_mask
);
949 if (source_easy_patterns
){
950 source_easy_patterns
= easy_patterns
;
952 source_mask
= convert_pattern (source_mask
, match_file
, 1);
953 easy_patterns
= source_easy_patterns
;
954 error
= re_compile_pattern (source_mask
, strlen (source_mask
), &ctx
->rx
);
955 g_free (source_mask
);
957 error
= re_compile_pattern (source_mask
, strlen (source_mask
), &ctx
->rx
);
960 message_3s (1, MSG_ERROR
, _("Invalid source pattern `%s' \n %s "),
968 ctx
->dest_mask
= strrchr (dest_dir
, PATH_SEP
);
969 if (ctx
->dest_mask
== NULL
)
970 ctx
->dest_mask
= dest_dir
;
973 orig_mask
= ctx
->dest_mask
;
974 if (!*ctx
->dest_mask
|| (!ctx
->dive_into_subdirs
&& !is_wildcarded (ctx
->dest_mask
) &&
975 (!only_one
|| (!mc_stat (dest_dir
, &buf
) && S_ISDIR (buf
.st_mode
)))) ||
976 (ctx
->dive_into_subdirs
&& ((!only_one
&& !is_wildcarded (ctx
->dest_mask
)) ||
978 && !mc_stat (dest_dir
, &buf
) && S_ISDIR (buf
.st_mode
)))))
979 ctx
->dest_mask
= g_strdup ("*");
981 ctx
->dest_mask
= g_strdup (ctx
->dest_mask
);
986 dest_dir
= g_strdup ("./");