replaced calls to g_strdup() by mhl_str_dup()
[midnight-commander.git] / src / filegui.c
blobc2c71f5ee09481775c37cb2f205102a244a7d3de
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 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
13 * The copy code was based in GNU's cp, and was written by:
14 * Torbjorn Granlund, David MacKenzie, and Jim Meyering.
16 * The move code was based in GNU's mv, and was written by:
17 * Mike Parker and David MacKenzie.
19 * Janne Kukonlehto added much error recovery to them for being used
20 * in an interactive program.
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
38 * Please note that all dialogs used here must be safe for background
39 * operations.
43 /* {{{ Include files */
45 #include <config.h>
47 #include <errno.h>
48 #include <ctype.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <unistd.h>
55 #include <mhl/string.h>
57 #include "global.h"
58 #include "setup.h" /* verbose */
59 #include "dialog.h" /* do_refresh() */
60 #include "color.h" /* dialog_colors */
61 #include "widget.h" /* WLabel */
62 #include "main-widgets.h"
63 #include "main.h" /* the_hint */
64 #include "wtools.h" /* QuickDialog */
65 #include "panel.h" /* current_panel */
66 #include "fileopctx.h" /* FILE_CONT */
67 #include "filegui.h"
68 #include "key.h" /* get_event */
69 #include "util.h" /* strip_password() */
71 /* }}} */
73 /* Hack: the vfs code should not rely on this */
74 #define WITH_FULL_PATHS 1
76 /* This structure describes the UI and internal data required by a file
77 * operation context.
79 typedef struct {
80 /* ETA and bps */
82 int showing_eta;
83 int showing_bps;
84 int eta_extra;
86 /* Dialog and widgets for the operation progress window */
88 Dlg_head *op_dlg;
90 WLabel *file_label[2];
91 WLabel *file_string[2];
92 WLabel *progress_label[3];
93 WGauge *progress_gauge[3];
94 WLabel *eta_label;
95 WLabel *bps_label;
96 WLabel *stalled_label;
98 /* Query replace dialog */
100 Dlg_head *replace_dlg;
101 const char *replace_filename;
102 int replace_result;
103 struct stat *s_stat, *d_stat;
104 } FileOpContextUI;
107 /* Used to save the hint line */
108 static int last_hint_line;
110 /* File operate window sizes */
111 #define WX 62
112 #define WY 10
113 #define BY 10
114 #define WX_ETA_EXTRA 12
116 #define FCOPY_GAUGE_X 14
117 #define FCOPY_LABEL_X 5
119 /* Used for button result values */
120 enum {
121 REPLACE_YES = B_USER,
122 REPLACE_NO,
123 REPLACE_APPEND,
124 REPLACE_ALWAYS,
125 REPLACE_UPDATE,
126 REPLACE_NEVER,
127 REPLACE_ABORT,
128 REPLACE_SIZE,
129 REPLACE_REGET
132 static FileProgressStatus
133 check_progress_buttons (FileOpContext *ctx)
135 int c;
136 Gpm_Event event;
137 FileOpContextUI *ui;
139 if (ctx->ui == NULL)
140 return FILE_CONT;
142 ui = ctx->ui;
144 event.x = -1; /* Don't show the GPM cursor */
145 c = get_event (&event, 0, 0);
146 if (c == EV_NONE)
147 return FILE_CONT;
149 /* Reinitialize to avoid old values after events other than
150 selecting a button */
151 ui->op_dlg->ret_value = FILE_CONT;
153 dlg_process_event (ui->op_dlg, c, &event);
154 switch (ui->op_dlg->ret_value) {
155 case FILE_SKIP:
156 return FILE_SKIP;
157 break;
158 case B_CANCEL:
159 case FILE_ABORT:
160 return FILE_ABORT;
161 break;
162 default:
163 return FILE_CONT;
167 /* {{{ File progress display routines */
169 void
170 file_op_context_create_ui (FileOpContext *ctx, int with_eta)
172 FileOpContextUI *ui;
173 int x_size;
174 int minus;
175 int eta_offset;
176 const char *sixty;
177 const char *fifteen;
179 g_return_if_fail (ctx != NULL);
180 g_return_if_fail (ctx->ui == NULL);
182 ui = g_new0 (FileOpContextUI, 1);
183 ctx->ui = ui;
185 minus = verbose ? 0 : 3;
186 eta_offset = with_eta ? (WX_ETA_EXTRA) / 2 : 0;
188 sixty = "";
189 fifteen = "";
191 ctx->recursive_result = 0;
193 ui->replace_result = 0;
194 ui->showing_eta = with_eta;
195 ui->showing_bps = with_eta;
196 ui->eta_extra = with_eta ? WX_ETA_EXTRA : 0;
197 x_size = (WX + 4) + ui->eta_extra;
199 ui->op_dlg =
200 create_dlg (0, 0, WY - minus + 4, x_size, dialog_colors, NULL,
201 NULL, op_names[ctx->operation],
202 DLG_CENTER | DLG_REVERSE);
204 last_hint_line = the_hint->widget.y;
205 if ((ui->op_dlg->y + ui->op_dlg->lines) > last_hint_line)
206 the_hint->widget.y = ui->op_dlg->y + ui->op_dlg->lines + 1;
208 add_widget (ui->op_dlg,
209 button_new (BY - minus, WX - 19 + eta_offset, FILE_ABORT,
210 NORMAL_BUTTON, _("&Abort"), 0));
211 add_widget (ui->op_dlg,
212 button_new (BY - minus, 14 + eta_offset, FILE_SKIP,
213 NORMAL_BUTTON, _("&Skip"), 0));
215 add_widget (ui->op_dlg, ui->progress_gauge[2] =
216 gauge_new (7, FCOPY_GAUGE_X, 0, 100, 0));
217 add_widget (ui->op_dlg, ui->progress_label[2] =
218 label_new (7, FCOPY_LABEL_X, fifteen));
219 add_widget (ui->op_dlg, ui->bps_label = label_new (7, WX, ""));
221 add_widget (ui->op_dlg, ui->progress_gauge[1] =
222 gauge_new (8, FCOPY_GAUGE_X, 0, 100, 0));
223 add_widget (ui->op_dlg, ui->progress_label[1] =
224 label_new (8, FCOPY_LABEL_X, fifteen));
225 add_widget (ui->op_dlg, ui->stalled_label = label_new (8, WX, ""));
227 add_widget (ui->op_dlg, ui->progress_gauge[0] =
228 gauge_new (6, FCOPY_GAUGE_X, 0, 100, 0));
229 add_widget (ui->op_dlg, ui->progress_label[0] =
230 label_new (6, FCOPY_LABEL_X, fifteen));
231 add_widget (ui->op_dlg, ui->eta_label = label_new (6, WX, ""));
233 add_widget (ui->op_dlg, ui->file_string[1] =
234 label_new (4, FCOPY_GAUGE_X, sixty));
235 add_widget (ui->op_dlg, ui->file_label[1] =
236 label_new (4, FCOPY_LABEL_X, fifteen));
237 add_widget (ui->op_dlg, ui->file_string[0] =
238 label_new (3, FCOPY_GAUGE_X, sixty));
239 add_widget (ui->op_dlg, ui->file_label[0] =
240 label_new (3, FCOPY_LABEL_X, fifteen));
242 /* We will manage the dialog without any help, that's why
243 we have to call init_dlg */
244 init_dlg (ui->op_dlg);
245 ui->op_dlg->running = 1;
248 void
249 file_op_context_destroy_ui (FileOpContext *ctx)
251 FileOpContextUI *ui;
253 g_return_if_fail (ctx != NULL);
255 if (ctx->ui) {
256 ui = ctx->ui;
258 dlg_run_done (ui->op_dlg);
259 destroy_dlg (ui->op_dlg);
260 g_free (ui);
263 the_hint->widget.y = last_hint_line;
265 ctx->ui = NULL;
268 static FileProgressStatus
269 show_no_bar (FileOpContext *ctx, int n)
271 FileOpContextUI *ui;
273 if (ctx->ui == NULL)
274 return FILE_CONT;
276 ui = ctx->ui;
278 if (n >= 0) {
279 label_set_text (ui->progress_label[n], "");
280 gauge_show (ui->progress_gauge[n], 0);
282 return check_progress_buttons (ctx);
285 static FileProgressStatus
286 show_bar (FileOpContext *ctx, int n, double done, double total)
288 FileOpContextUI *ui;
290 if (ctx->ui == NULL)
291 return FILE_CONT;
293 ui = ctx->ui;
296 * Gauge needs integers, so give it with integers between 0 and 1023.
297 * This precision should be quite reasonable.
299 gauge_set_value (ui->progress_gauge[n], 1024,
300 (int) (1024 * done / total));
301 gauge_show (ui->progress_gauge[n], 1);
302 return check_progress_buttons (ctx);
305 static void
306 file_eta_show (FileOpContext *ctx)
308 int eta_hours, eta_mins, eta_s;
309 char eta_buffer[BUF_TINY];
310 FileOpContextUI *ui;
312 if (ctx->ui == NULL)
313 return;
315 ui = ctx->ui;
317 if (!ui->showing_eta)
318 return;
320 if (ctx->eta_secs > 0.5) {
321 eta_hours = ctx->eta_secs / (60 * 60);
322 eta_mins = (ctx->eta_secs - (eta_hours * 60 * 60)) / 60;
323 eta_s = ctx->eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
324 g_snprintf (eta_buffer, sizeof (eta_buffer), _("ETA %d:%02d.%02d"),
325 eta_hours, eta_mins, eta_s);
326 } else
327 *eta_buffer = 0;
329 label_set_text (ui->eta_label, eta_buffer);
332 static void
333 file_bps_show (FileOpContext *ctx)
335 char bps_buffer[BUF_TINY];
336 FileOpContextUI *ui;
338 if (ctx->ui == NULL)
339 return;
341 ui = ctx->ui;
343 if (!ui->showing_bps)
344 return;
346 if (ctx->bps > 1024 * 1024) {
347 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f MB/s"),
348 ctx->bps / (1024 * 1024.0));
349 } else if (ctx->bps > 1024) {
350 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f KB/s"),
351 ctx->bps / 1024.0);
352 } else if (ctx->bps > 1) {
353 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%ld B/s"),
354 ctx->bps);
355 } else
356 *bps_buffer = 0;
358 label_set_text (ui->bps_label, bps_buffer);
361 FileProgressStatus
362 file_progress_show (FileOpContext *ctx, off_t done, off_t total)
364 FileOpContextUI *ui;
366 g_return_val_if_fail (ctx != NULL, FILE_CONT);
368 if (ctx->ui == NULL)
369 return FILE_CONT;
371 ui = ctx->ui;
373 if (!verbose)
374 return check_progress_buttons (ctx);
375 if (total > 0) {
376 label_set_text (ui->progress_label[0], _("File"));
377 file_eta_show (ctx);
378 file_bps_show (ctx);
379 return show_bar (ctx, 0, done, total);
380 } else
381 return show_no_bar (ctx, 0);
384 FileProgressStatus
385 file_progress_show_count (FileOpContext *ctx, off_t done, off_t total)
387 FileOpContextUI *ui;
389 g_return_val_if_fail (ctx != NULL, FILE_CONT);
391 if (ctx->ui == NULL)
392 return FILE_CONT;
394 ui = ctx->ui;
396 if (!verbose)
397 return check_progress_buttons (ctx);
398 if (total > 0) {
399 label_set_text (ui->progress_label[1], _("Count"));
400 return show_bar (ctx, 1, done, total);
401 } else
402 return show_no_bar (ctx, 1);
405 FileProgressStatus
406 file_progress_show_bytes (FileOpContext *ctx, double done, double total)
408 FileOpContextUI *ui;
410 g_return_val_if_fail (ctx != NULL, FILE_CONT);
412 if (ctx->ui == NULL)
413 return FILE_CONT;
415 ui = ctx->ui;
417 if (!verbose)
418 return check_progress_buttons (ctx);
419 if (total > 0) {
420 label_set_text (ui->progress_label[2], _("Bytes"));
421 return show_bar (ctx, 2, done, total);
422 } else
423 return show_no_bar (ctx, 2);
426 /* }}} */
428 #define truncFileString(ui, s) name_trunc (s, ui->eta_extra + 47)
429 #define truncFileStringSecure(ui, s) path_trunc (s, ui->eta_extra + 47)
431 FileProgressStatus
432 file_progress_show_source (FileOpContext *ctx, const char *s)
434 FileOpContextUI *ui;
436 g_return_val_if_fail (ctx != NULL, FILE_CONT);
438 if (ctx->ui == NULL)
439 return FILE_CONT;
441 ui = ctx->ui;
443 if (s != NULL) {
444 #ifdef WITH_FULL_PATHS
445 int i = strlen (current_panel->cwd);
447 /* We remove the full path we have added before */
448 if (!strncmp (s, current_panel->cwd, i)) {
449 if (s[i] == PATH_SEP)
450 s += i + 1;
452 #endif /* WITH_FULL_PATHS */
454 label_set_text (ui->file_label[0], _("Source"));
455 label_set_text (ui->file_string[0], truncFileString (ui, s));
456 return check_progress_buttons (ctx);
457 } else {
458 label_set_text (ui->file_label[0], "");
459 label_set_text (ui->file_string[0], "");
460 return check_progress_buttons (ctx);
464 FileProgressStatus
465 file_progress_show_target (FileOpContext *ctx, const char *s)
467 FileOpContextUI *ui;
469 g_return_val_if_fail (ctx != NULL, FILE_CONT);
471 if (ctx->ui == NULL)
472 return FILE_CONT;
474 ui = ctx->ui;
476 if (s != NULL) {
477 label_set_text (ui->file_label[1], _("Target"));
478 label_set_text (ui->file_string[1], truncFileStringSecure (ui, s));
479 return check_progress_buttons (ctx);
480 } else {
481 label_set_text (ui->file_label[1], "");
482 label_set_text (ui->file_string[1], "");
483 return check_progress_buttons (ctx);
487 FileProgressStatus
488 file_progress_show_deleting (FileOpContext *ctx, const char *s)
490 FileOpContextUI *ui;
492 g_return_val_if_fail (ctx != NULL, FILE_CONT);
494 if (ctx->ui == NULL)
495 return FILE_CONT;
497 ui = ctx->ui;
499 label_set_text (ui->file_label[0], _("Deleting"));
500 label_set_text (ui->file_label[0], truncFileStringSecure (ui, s));
501 return check_progress_buttons (ctx);
504 #define X_TRUNC 52
507 * FIXME: probably it is better to replace this with quick dialog machinery,
508 * but actually I'm not familiar with it and have not much time :(
509 * alex
511 static struct {
512 const char *text;
513 int ypos, xpos;
514 int value; /* 0 for labels */
515 } rd_widgets[] = {
517 N_("Target file \"%s\" already exists!"), 3, 4, 0}, {
518 N_("&Abort"), BY + 3, 25, REPLACE_ABORT}, {
519 N_("If &size differs"), BY + 1, 28, REPLACE_SIZE}, {
520 N_("Non&e"), BY, 47, REPLACE_NEVER}, {
521 N_("&Update"), BY, 36, REPLACE_UPDATE}, {
522 N_("A&ll"), BY, 28, REPLACE_ALWAYS}, {
523 N_("Overwrite all targets?"), BY, 4, 0}, {
524 N_("&Reget"), BY - 1, 28, REPLACE_REGET}, {
525 N_("A&ppend"), BY - 2, 45, REPLACE_APPEND}, {
526 N_("&No"), BY - 2, 37, REPLACE_NO}, {
527 N_("&Yes"), BY - 2, 28, REPLACE_YES}, {
528 N_("Overwrite this target?"), BY - 2, 4, 0}, {
529 #if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || (defined _LARGE_FILES && _LARGE_FILES)
530 N_("Target date: %s, size %llu"), 6, 4, 0}, {
531 N_("Source date: %s, size %llu"), 5, 4, 0}
532 #else
533 N_("Target date: %s, size %u"), 6, 4, 0}, {
534 N_("Source date: %s, size %u"), 5, 4, 0}
535 #endif
538 #define ADD_RD_BUTTON(i)\
539 add_widget (ui->replace_dlg,\
540 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value,\
541 NORMAL_BUTTON, rd_widgets [i].text, 0))
543 #define ADD_RD_LABEL(ui,i,p1,p2)\
544 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2);\
545 add_widget (ui->replace_dlg,\
546 label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer))
548 static void
549 init_replace (FileOpContext *ctx, enum OperationMode mode)
551 FileOpContextUI *ui;
552 char buffer[BUF_SMALL];
553 const char *title;
554 static int rd_xlen = 60, rd_trunc = X_TRUNC;
556 #ifdef ENABLE_NLS
557 static int i18n_flag;
558 if (!i18n_flag) {
559 int l1, l2, l, row;
560 register int i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
561 while (i--)
562 rd_widgets[i].text = _(rd_widgets[i].text);
565 * longest of "Overwrite..." labels
566 * (assume "Target date..." are short enough)
568 l1 = max (strlen (rd_widgets[6].text),
569 strlen (rd_widgets[11].text));
571 /* longest of button rows */
572 i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
573 for (row = l = l2 = 0; i--;) {
574 if (rd_widgets[i].value != 0) {
575 if (row != rd_widgets[i].ypos) {
576 row = rd_widgets[i].ypos;
577 l2 = max (l2, l);
578 l = 0;
580 l += strlen (rd_widgets[i].text) + 4;
583 l2 = max (l2, l); /* last row */
584 rd_xlen = max (rd_xlen, l1 + l2 + 8);
585 rd_trunc = rd_xlen - 6;
587 /* Now place buttons */
588 l1 += 5; /* start of first button in the row */
589 i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
591 for (l = l1, row = 0; --i > 1;) {
592 if (rd_widgets[i].value != 0) {
593 if (row != rd_widgets[i].ypos) {
594 row = rd_widgets[i].ypos;
595 l = l1;
597 rd_widgets[i].xpos = l;
598 l += strlen (rd_widgets[i].text) + 4;
601 /* Abort button is centered */
602 rd_widgets[1].xpos =
603 (rd_xlen - strlen (rd_widgets[1].text) - 3) / 2;
605 #endif /* ENABLE_NLS */
607 ui = ctx->ui;
609 if (mode == Foreground)
610 title = _(" File exists ");
611 else
612 title = _(" Background process: File exists ");
614 /* FIXME - missing help node */
615 ui->replace_dlg =
616 create_dlg (0, 0, 16, rd_xlen, alarm_colors, NULL, "[Replace]",
617 title, DLG_CENTER | DLG_REVERSE);
620 ADD_RD_LABEL (ui, 0,
621 name_trunc (ui->replace_filename,
622 rd_trunc - strlen (rd_widgets[0].text)), 0);
623 ADD_RD_BUTTON (1);
625 ADD_RD_BUTTON (2);
626 ADD_RD_BUTTON (3);
627 ADD_RD_BUTTON (4);
628 ADD_RD_BUTTON (5);
629 ADD_RD_LABEL (ui, 6, 0, 0);
631 /* "this target..." widgets */
632 if (!S_ISDIR (ui->d_stat->st_mode)) {
633 if ((ctx->operation == OP_COPY) && (ui->d_stat->st_size != 0)
634 && (ui->s_stat->st_size > ui->d_stat->st_size))
635 ADD_RD_BUTTON (7); /* reget */
637 ADD_RD_BUTTON (8); /* Overwrite all targets? */
639 ADD_RD_BUTTON (9);
640 ADD_RD_BUTTON (10);
641 ADD_RD_LABEL (ui, 11, 0, 0);
643 ADD_RD_LABEL (ui, 12, file_date (ui->d_stat->st_mtime),
644 (off_t) ui->d_stat->st_size);
645 ADD_RD_LABEL (ui, 13, file_date (ui->s_stat->st_mtime),
646 (off_t) ui->s_stat->st_size);
649 void
650 file_progress_set_stalled_label (FileOpContext *ctx, const char *stalled_msg)
652 FileOpContextUI *ui;
654 g_return_if_fail (ctx != NULL);
656 if (ctx->ui == NULL)
657 return;
659 ui = ctx->ui;
661 label_set_text (ui->stalled_label, stalled_msg);
664 FileProgressStatus
665 file_progress_real_query_replace (FileOpContext *ctx,
666 enum OperationMode mode, const char *destname,
667 struct stat *_s_stat,
668 struct stat *_d_stat)
670 FileOpContextUI *ui;
672 g_return_val_if_fail (ctx != NULL, FILE_CONT);
673 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
675 ui = ctx->ui;
677 if (ui->replace_result < REPLACE_ALWAYS) {
678 ui->replace_filename = destname;
679 ui->s_stat = _s_stat;
680 ui->d_stat = _d_stat;
681 init_replace (ctx, mode);
682 run_dlg (ui->replace_dlg);
683 ui->replace_result = ui->replace_dlg->ret_value;
684 if (ui->replace_result == B_CANCEL)
685 ui->replace_result = REPLACE_ABORT;
686 destroy_dlg (ui->replace_dlg);
689 switch (ui->replace_result) {
690 case REPLACE_UPDATE:
691 do_refresh ();
692 if (_s_stat->st_mtime > _d_stat->st_mtime)
693 return FILE_CONT;
694 else
695 return FILE_SKIP;
697 case REPLACE_SIZE:
698 do_refresh ();
699 if (_s_stat->st_size == _d_stat->st_size)
700 return FILE_SKIP;
701 else
702 return FILE_CONT;
704 case REPLACE_REGET:
705 /* Careful: we fall through and set do_append */
706 ctx->do_reget = _d_stat->st_size;
708 case REPLACE_APPEND:
709 ctx->do_append = 1;
711 case REPLACE_YES:
712 case REPLACE_ALWAYS:
713 do_refresh ();
714 return FILE_CONT;
715 case REPLACE_NO:
716 case REPLACE_NEVER:
717 do_refresh ();
718 return FILE_SKIP;
719 case REPLACE_ABORT:
720 default:
721 return FILE_ABORT;
725 #define FMDY 13
726 #define FMD_XLEN 64
727 extern int fmd_xlen;
728 static QuickWidget fmd_widgets[] = {
730 #define FMCB0 FMDC
731 #define FMCB12 0
732 #define FMCB11 1
733 /* follow symlinks and preserve Attributes must be the first */
734 {quick_checkbox, 3, 64, 8, FMDY, N_("preserve &Attributes"), 9, 0,
735 0 /* &op_preserve */ , 0, NULL},
736 {quick_checkbox, 3, 64, 7, FMDY, N_("follow &Links"), 7, 0,
737 0 /* &file_mask_op_follow_links */ , 0, NULL},
738 {quick_label, 3, 64, 5, FMDY, N_("to:"), 0, 0, 0, 0, NULL},
739 {quick_checkbox, 37, 64, 4, FMDY, N_("&Using shell patterns"), 0, 0,
740 0 /* &source_easy_patterns */ , 0, NULL},
741 {quick_input, 3, 64, 3, FMDY, "", 58,
742 0, 0, 0, "input-def"},
743 #define FMDI1 4
744 #define FMDI2 5
745 #define FMDC 3
746 {quick_input, 3, 64, 6, FMDY, "", 58, 0,
747 0, 0, "input2"},
748 #define FMDI0 6
749 {quick_label, 3, 64, 2, FMDY, "", 0, 0, 0, 0, NULL},
750 #define FMBRGT 7
751 {quick_button, 42, 64, 9, FMDY, N_("&Cancel"), 0, B_CANCEL, 0, 0,
752 NULL},
753 #undef SKIP
754 #ifdef WITH_BACKGROUND
755 # define SKIP 5
756 # define FMCB21 11
757 # define FMCB22 10
758 # define FMBLFT 9
759 # define FMBMID 8
760 {quick_button, 25, 64, 9, FMDY, N_("&Background"), 0, B_USER, 0, 0,
761 NULL},
762 #else /* WITH_BACKGROUND */
763 # define SKIP 4
764 # define FMCB21 10
765 # define FMCB22 9
766 # define FMBLFT 8
767 # undef FMBMID
768 #endif
769 {quick_button, 14, 64, 9, FMDY, N_("&OK"), 0, B_ENTER, 0, 0, NULL},
770 {quick_checkbox, 42, 64, 8, FMDY, N_("&Stable Symlinks"), 0, 0,
771 0 /* &file_mask_stable_symlinks */ , 0, NULL},
772 {quick_checkbox, 31, 64, 7, FMDY, N_("&Dive into subdir if exists"), 0,
774 0 /* &dive_into_subdirs */ , 0, NULL},
775 NULL_QuickWidget
778 static int
779 is_wildcarded (char *p)
781 for (; *p; p++) {
782 if (*p == '*')
783 return 1;
784 else if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
785 return 1;
787 return 0;
790 void
791 fmd_init_i18n (int force)
793 #ifdef ENABLE_NLS
794 static int initialized = FALSE;
795 register int i;
796 int len;
798 if (initialized && !force)
799 return;
801 for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
802 op_names[i] = _(op_names[i]);
804 i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
805 while (i--)
806 if (fmd_widgets[i].text[0] != '\0')
807 fmd_widgets[i].text = _(fmd_widgets[i].text);
809 len = strlen (fmd_widgets[FMCB11].text)
810 + strlen (fmd_widgets[FMCB21].text) + 15;
811 fmd_xlen = max (fmd_xlen, len);
813 len = strlen (fmd_widgets[FMCB12].text)
814 + strlen (fmd_widgets[FMCB22].text) + 15;
815 fmd_xlen = max (fmd_xlen, len);
817 len = strlen (fmd_widgets[FMBRGT].text)
818 + strlen (fmd_widgets[FMBLFT].text) + 11;
820 #ifdef FMBMID
821 len += strlen (fmd_widgets[FMBMID].text) + 6;
822 #endif
824 fmd_xlen = max (fmd_xlen, len + 4);
826 len = (fmd_xlen - (len + 6)) / 2;
827 i = fmd_widgets[FMBLFT].relative_x = len + 3;
828 i += strlen (fmd_widgets[FMBLFT].text) + 8;
830 #ifdef FMBMID
831 fmd_widgets[FMBMID].relative_x = i;
832 i += strlen (fmd_widgets[FMBMID].text) + 6;
833 #endif
835 fmd_widgets[FMBRGT].relative_x = i;
837 #define chkbox_xpos(i) \
838 fmd_widgets [i].relative_x = fmd_xlen - strlen (fmd_widgets [i].text) - 6
840 chkbox_xpos (FMCB0);
841 chkbox_xpos (FMCB21);
842 chkbox_xpos (FMCB22);
844 if (fmd_xlen != FMD_XLEN) {
845 i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
846 while (i--)
847 fmd_widgets[i].x_divisions = fmd_xlen;
849 fmd_widgets[FMDI1].hotkey_pos =
850 fmd_widgets[FMDI2].hotkey_pos = fmd_xlen - 6;
852 #undef chkbox_xpos
854 initialized = TRUE;
855 #endif /* !ENABLE_NLS */
858 char *
859 file_mask_dialog (FileOpContext *ctx, FileOperation operation, const char *text,
860 const char *def_text, int only_one, int *do_background)
862 int source_easy_patterns = easy_patterns;
863 char *source_mask, *orig_mask, *dest_dir, *tmpdest;
864 const char *error;
865 char *def_text_secure;
866 struct stat buf;
867 int val;
868 QuickDialog Quick_input;
870 g_return_val_if_fail (ctx != NULL, NULL);
871 #if 0
872 message (D_ERROR, __FUNCTION__, "text = `%s' \n def_text = `%s'", text,
873 def_text);
874 #endif
875 fmd_init_i18n (FALSE);
877 /* Set up the result pointers */
879 fmd_widgets[FMCB12].result = &ctx->op_preserve;
880 fmd_widgets[FMCB11].result = &ctx->follow_links;
881 fmd_widgets[FMCB22].result = &ctx->stable_symlinks;
882 fmd_widgets[FMCB21].result = &ctx->dive_into_subdirs;
884 /* filter out a possible password from def_text */
885 def_text_secure = strip_password (mhl_str_dup (def_text), 1);
887 /* Create the dialog */
889 ctx->stable_symlinks = 0;
890 fmd_widgets[FMDC].result = &source_easy_patterns;
891 fmd_widgets[FMDI1].text = easy_patterns ? "*" : "^\\(.*\\)$";
892 Quick_input.xlen = fmd_xlen;
893 Quick_input.xpos = -1;
894 Quick_input.title = op_names[operation];
895 Quick_input.help = "[Mask Copy/Rename]";
896 Quick_input.ylen = FMDY;
897 Quick_input.i18n = 1;
898 Quick_input.widgets = fmd_widgets;
899 fmd_widgets[FMDI0].text = text;
900 fmd_widgets[FMDI2].text = def_text_secure;
901 fmd_widgets[FMDI2].str_result = &dest_dir;
902 fmd_widgets[FMDI1].str_result = &source_mask;
904 *do_background = 0;
905 ask_file_mask:
907 if ((val = quick_dialog_skip (&Quick_input, SKIP)) == B_CANCEL) {
908 g_free (def_text_secure);
909 return 0;
911 g_free (def_text_secure);
913 if (ctx->follow_links)
914 ctx->stat_func = mc_stat;
915 else
916 ctx->stat_func = mc_lstat;
918 if (ctx->op_preserve) {
919 ctx->preserve = 1;
920 ctx->umask_kill = 0777777;
921 ctx->preserve_uidgid = (geteuid () == 0) ? 1 : 0;
922 } else {
923 int i;
924 ctx->preserve = ctx->preserve_uidgid = 0;
925 i = umask (0);
926 umask (i);
927 ctx->umask_kill = i ^ 0777777;
930 orig_mask = source_mask;
931 if (!dest_dir || !*dest_dir) {
932 g_free (source_mask);
933 return dest_dir;
935 if (source_easy_patterns) {
936 source_easy_patterns = easy_patterns;
937 easy_patterns = 1;
938 source_mask = convert_pattern (source_mask, match_file, 1);
939 easy_patterns = source_easy_patterns;
940 error =
941 re_compile_pattern (source_mask, strlen (source_mask),
942 &ctx->rx);
943 g_free (source_mask);
944 } else
945 error =
946 re_compile_pattern (source_mask, strlen (source_mask),
947 &ctx->rx);
949 if (error) {
950 message (D_ERROR, MSG_ERROR, _("Invalid source pattern `%s' \n %s "),
951 orig_mask, error);
952 g_free (orig_mask);
953 goto ask_file_mask;
955 g_free (orig_mask);
957 tmpdest = dest_dir;
958 dest_dir = tilde_expand(tmpdest);
959 g_free(tmpdest);
961 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
962 if (ctx->dest_mask == NULL)
963 ctx->dest_mask = dest_dir;
964 else
965 ctx->dest_mask++;
966 orig_mask = ctx->dest_mask;
967 if (!*ctx->dest_mask
968 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
969 && (!only_one
970 || (!mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode))))
971 || (ctx->dive_into_subdirs
972 && ((!only_one && !is_wildcarded (ctx->dest_mask))
973 || (only_one && !mc_stat (dest_dir, &buf)
974 && S_ISDIR (buf.st_mode)))))
975 ctx->dest_mask = mhl_str_dup ("*");
976 else {
977 ctx->dest_mask = mhl_str_dup (ctx->dest_mask);
978 *orig_mask = 0;
980 if (!*dest_dir) {
981 g_free (dest_dir);
982 dest_dir = mhl_str_dup ("./");
984 if (val == B_USER)
985 *do_background = 1;
986 return dest_dir;