(file_mask_dialog): NULLify variables before use in next iteration.
[midnight-commander.git] / src / filemanager / filegui.c
blobffca554b362e8a515b614acbd7cddef464d2b6b8
1 /*
2 File management GUI for the text mode edition
4 The copy code was based in GNU's cp, and was written by:
5 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
7 The move code was based in GNU's mv, and was written by:
8 Mike Parker and David MacKenzie.
10 Janne Kukonlehto added much error recovery to them for being used
11 in an interactive program.
13 Copyright (C) 1994-2022
14 Free Software Foundation, Inc.
16 Written by:
17 Janne Kukonlehto, 1994, 1995
18 Fred Leeflang, 1994, 1995
19 Miguel de Icaza, 1994, 1995, 1996
20 Jakub Jelinek, 1995, 1996
21 Norbert Warmuth, 1997
22 Pavel Machek, 1998
23 Slava Zanko, 2009, 2010, 2011, 2012, 2013
24 Andrew Borodin <aborodin@vmail.ru>, 2009, 2010, 2011, 2012, 2013
26 This file is part of the Midnight Commander.
28 The Midnight Commander is free software: you can redistribute it
29 and/or modify it under the terms of the GNU General Public License as
30 published by the Free Software Foundation, either version 3 of the License,
31 or (at your option) any later version.
33 The Midnight Commander is distributed in the hope that it will be useful,
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 GNU General Public License for more details.
38 You should have received a copy of the GNU General Public License
39 along with this program. If not, see <http://www.gnu.org/licenses/>.
43 * Please note that all dialogs used here must be safe for background
44 * operations.
47 /** \file filegui.c
48 * \brief Source: file management GUI for the text mode edition
51 /* {{{ Include files */
53 #include <config.h>
55 #if ((defined STAT_STATVFS || defined STAT_STATVFS64) \
56 && (defined HAVE_STRUCT_STATVFS_F_BASETYPE || defined HAVE_STRUCT_STATVFS_F_FSTYPENAME \
57 || (! defined HAVE_STRUCT_STATFS_F_FSTYPENAME)))
58 #define USE_STATVFS 1
59 #else
60 #define USE_STATVFS 0
61 #endif
63 #include <errno.h>
64 #include <ctype.h>
65 #include <stdio.h>
66 #include <string.h>
67 #include <sys/types.h>
68 #include <sys/stat.h>
70 #if USE_STATVFS
71 #include <sys/statvfs.h>
72 #elif defined HAVE_SYS_VFS_H
73 #include <sys/vfs.h>
74 #elif defined HAVE_SYS_MOUNT_H && defined HAVE_SYS_PARAM_H
75 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
76 It does have statvfs.h, but shouldn't use it, since it doesn't
77 HAVE_STRUCT_STATVFS_F_BASETYPE. So find a clean way to fix it. */
78 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
79 #include <sys/param.h>
80 #include <sys/mount.h>
81 #elif defined HAVE_OS_H /* Haiku, also (obsolete) BeOS */
82 #include <fs_info.h>
83 #endif
85 #if USE_STATVFS
86 #if ! defined STAT_STATVFS && defined STAT_STATVFS64
87 #define STRUCT_STATVFS struct statvfs64
88 #define STATFS statvfs64
89 #else
90 #define STRUCT_STATVFS struct statvfs
91 #define STATFS statvfs
93 #if defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__)
94 #include <sys/utsname.h>
95 #include <sys/statfs.h>
96 #define STAT_STATFS2_BSIZE 1
97 #endif
98 #endif
100 #else
101 #define STATFS statfs
102 #define STRUCT_STATVFS struct statfs
103 #ifdef HAVE_OS_H /* Haiku, also (obsolete) BeOS */
104 /* BeOS has a statvfs function, but it does not return sensible values
105 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
106 f_fstypename. Use 'struct fs_info' instead. */
107 static int
108 statfs (char const *filename, struct fs_info *buf)
110 dev_t device;
112 device = dev_for_path (filename);
114 if (device < 0)
116 errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
117 : device == B_BAD_VALUE ? EINVAL
118 : device == B_NAME_TOO_LONG ? ENAMETOOLONG
119 : device == B_NO_MEMORY ? ENOMEM : device == B_FILE_ERROR ? EIO : 0);
120 return -1;
122 /* If successful, buf->dev will be == device. */
123 return fs_stat_dev (device, buf);
126 #define STRUCT_STATVFS struct fs_info
127 #else
128 #define STRUCT_STATVFS struct statfs
129 #endif
130 #endif
132 #ifdef HAVE_STRUCT_STATVFS_F_BASETYPE
133 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
134 #else
135 #if defined HAVE_STRUCT_STATVFS_F_FSTYPENAME || defined HAVE_STRUCT_STATFS_F_FSTYPENAME
136 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
137 #elif defined HAVE_OS_H /* Haiku, also (obsolete) BeOS */
138 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
139 #endif
140 #endif
142 #include <unistd.h>
144 #include "lib/global.h"
146 #include "lib/tty/key.h" /* tty_get_event */
147 #include "lib/mcconfig.h"
148 #include "lib/search.h"
149 #include "lib/vfs/vfs.h"
150 #include "lib/strescape.h"
151 #include "lib/strutil.h"
152 #include "lib/timefmt.h" /* file_date() */
153 #include "lib/util.h"
154 #include "lib/widget.h"
156 #include "src/setup.h" /* verbose, safe_overwrite */
158 #include "filemanager.h"
159 #include "fileopctx.h" /* FILE_CONT */
161 #include "filegui.h"
163 /* }}} */
165 /*** global variables ****************************************************************************/
167 gboolean classic_progressbar = TRUE;
169 /*** file scope macro definitions ****************************************************************/
171 #define truncFileString(dlg, s) str_trunc (s, WIDGET (dlg)->cols - 10)
172 #define truncFileStringSecure(dlg, s) path_trunc (s, WIDGET (dlg)->cols - 10)
174 /*** file scope type declarations ****************************************************************/
176 /* *INDENT-OFF* */
177 typedef enum {
178 MSDOS_SUPER_MAGIC = 0x4d44,
179 NTFS_SB_MAGIC = 0x5346544e,
180 FUSE_MAGIC = 0x65735546,
181 PROC_SUPER_MAGIC = 0x9fa0,
182 SMB_SUPER_MAGIC = 0x517B,
183 NCP_SUPER_MAGIC = 0x564c,
184 USBDEVICE_SUPER_MAGIC = 0x9fa2
185 } filegui_nonattrs_fs_t;
186 /* *INDENT-ON* */
188 /* Used for button result values */
189 typedef enum
191 REPLACE_YES = B_USER,
192 REPLACE_NO,
193 REPLACE_APPEND,
194 REPLACE_REGET,
195 REPLACE_ALL,
196 REPLACE_OLDER,
197 REPLACE_NONE,
198 REPLACE_SMALLER,
199 REPLACE_SIZE,
200 REPLACE_ABORT
201 } replace_action_t;
203 /* This structure describes the UI and internal data required by a file
204 * operation context.
206 typedef struct
208 /* ETA and bps */
209 gboolean showing_eta;
210 gboolean showing_bps;
212 /* Dialog and widgets for the operation progress window */
213 WDialog *op_dlg;
214 /* Source file: label and name */
215 WLabel *src_file_label;
216 WLabel *src_file;
217 /* Target file: label and name */
218 WLabel *tgt_file_label;
219 WLabel *tgt_file;
221 WGauge *progress_file_gauge;
222 WLabel *progress_file_label;
224 WGauge *progress_total_gauge;
226 WLabel *total_files_processed_label;
227 WLabel *time_label;
228 WHLine *total_bytes_label;
230 /* Query replace dialog */
231 WDialog *replace_dlg;
232 const char *src_filename;
233 const char *tgt_filename;
234 replace_action_t replace_result;
235 gboolean dont_overwrite_with_zero;
237 struct stat *src_stat, *dst_stat;
238 } file_op_context_ui_t;
240 /*** file scope variables ************************************************************************/
242 static struct
244 Widget *w;
245 FileProgressStatus action;
246 const char *text;
247 button_flags_t flags;
248 int len;
249 } progress_buttons[] =
251 /* *INDENT-OFF* */
252 { NULL, FILE_SKIP, N_("&Skip"), NORMAL_BUTTON, -1 },
253 { NULL, FILE_SUSPEND, N_("S&uspend"), NORMAL_BUTTON, -1 },
254 { NULL, FILE_SUSPEND, N_("Con&tinue"), NORMAL_BUTTON, -1 },
255 { NULL, FILE_ABORT, N_("&Abort"), NORMAL_BUTTON, -1 }
256 /* *INDENT-ON* */
259 /* --------------------------------------------------------------------------------------------- */
260 /*** file scope functions ************************************************************************/
261 /* --------------------------------------------------------------------------------------------- */
263 /* Return true if statvfs works. This is false for statvfs on systems
264 with GNU libc on Linux kernels before 2.6.36, which stats all
265 preceding entries in /proc/mounts; that makes df hang if even one
266 of the corresponding file systems is hard-mounted but not available. */
268 #if USE_STATVFS && ! (! defined STAT_STATVFS && defined STAT_STATVFS64)
269 static int
270 statvfs_works (void)
272 #if ! (defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__))
273 return 1;
274 #else
275 static int statvfs_works_cache = -1;
276 struct utsname name;
278 if (statvfs_works_cache < 0)
279 statvfs_works_cache = (uname (&name) == 0 && 0 <= str_verscmp (name.release, "2.6.36"));
280 return statvfs_works_cache;
281 #endif
283 #endif
285 /* --------------------------------------------------------------------------------------------- */
287 static gboolean
288 filegui__check_attrs_on_fs (const char *fs_path)
290 STRUCT_STATVFS stfs;
292 #if USE_STATVFS && defined(STAT_STATVFS)
293 if (statvfs_works () && statvfs (fs_path, &stfs) != 0)
294 return TRUE;
295 #else
296 if (STATFS (fs_path, &stfs) != 0)
297 return TRUE;
298 #endif
300 #if (USE_STATVFS && defined(HAVE_STRUCT_STATVFS_F_TYPE)) || \
301 (!USE_STATVFS && defined(HAVE_STRUCT_STATFS_F_TYPE))
302 switch ((filegui_nonattrs_fs_t) stfs.f_type)
304 case MSDOS_SUPER_MAGIC:
305 case NTFS_SB_MAGIC:
306 case PROC_SUPER_MAGIC:
307 case SMB_SUPER_MAGIC:
308 case NCP_SUPER_MAGIC:
309 case USBDEVICE_SUPER_MAGIC:
310 return FALSE;
311 default:
312 break;
314 #elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
315 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdos") == 0
316 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdosfs") == 0
317 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
318 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "procfs") == 0
319 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
320 || strstr (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fusefs") != NULL)
321 return FALSE;
322 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
323 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "pcfs") == 0
324 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
325 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "proc") == 0
326 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
327 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fuse") == 0)
328 return FALSE;
329 #endif
331 return TRUE;
334 /* --------------------------------------------------------------------------------------------- */
336 static void
337 file_frmt_time (char *buffer, double eta_secs)
339 int eta_hours, eta_mins, eta_s;
341 eta_hours = (int) (eta_secs / (60 * 60));
342 eta_mins = (int) ((eta_secs - (eta_hours * 60 * 60)) / 60);
343 eta_s = (int) (eta_secs - (eta_hours * 60 * 60 + eta_mins * 60));
344 g_snprintf (buffer, BUF_TINY, _("%d:%02d.%02d"), eta_hours, eta_mins, eta_s);
347 /* --------------------------------------------------------------------------------------------- */
349 static void
350 file_eta_prepare_for_show (char *buffer, double eta_secs, gboolean always_show)
352 char _fmt_buff[BUF_TINY];
354 if (eta_secs <= 0.5 && !always_show)
356 *buffer = '\0';
357 return;
360 if (eta_secs <= 0.5)
361 eta_secs = 1;
362 file_frmt_time (_fmt_buff, eta_secs);
363 g_snprintf (buffer, BUF_TINY, _("ETA %s"), _fmt_buff);
366 /* --------------------------------------------------------------------------------------------- */
368 static void
369 file_bps_prepare_for_show (char *buffer, long bps)
371 if (bps > 1024 * 1024)
372 g_snprintf (buffer, BUF_TINY, _("%.2f MB/s"), bps / (1024 * 1024.0));
373 else if (bps > 1024)
374 g_snprintf (buffer, BUF_TINY, _("%.2f KB/s"), bps / 1024.0);
375 else if (bps > 1)
376 g_snprintf (buffer, BUF_TINY, _("%ld B/s"), bps);
377 else
378 *buffer = '\0';
381 /* --------------------------------------------------------------------------------------------- */
383 /* The dialog layout:
385 * +---------------------- File exists -----------------------+
386 * | New : /path/to/original_file_name | // 0, 1
387 * | 1234567 feb 4 2017 13:38 | // 2, 3
388 * | Existing: /path/to/target_file_name | // 4, 5
389 * | 1234567890 feb 4 2017 13:37 | // 6, 7
390 * +----------------------------------------------------------+
391 * | Overwrite this file? | // 8
392 * | [ Yes ] [ No ] [ Append ] [ Reget ] | // 9, 10, 11, 12
393 * +----------------------------------------------------------+
394 * | Overwrite all files? | // 13
395 * | [ ] Don't overwrite with zero length file | // 14
396 * | [ All ] [ Older ] [None] [ Smaller ] [ Size differs ] | // 15, 16, 17, 18, 19
397 * +----------------------------------------------------------|
398 * | [ Abort ] | // 20
399 * +----------------------------------------------------------+
402 static replace_action_t
403 overwrite_query_dialog (file_op_context_t * ctx, enum OperationMode mode)
405 #define W(i) dlg_widgets[i].widget
406 #define WX(i) W(i)->x
407 #define WCOLS(i) W(i)->cols
409 #define NEW_LABEL(i, text) \
410 W(i) = WIDGET (label_new (dlg_widgets[i].y, dlg_widgets[i].x, text))
412 #define ADD_LABEL(i) \
413 group_add_widget_autopos (g, W(i), dlg_widgets[i].pos_flags, \
414 g->current != NULL ? g->current->data : NULL)
416 #define NEW_BUTTON(i) \
417 W(i) = WIDGET (button_new (dlg_widgets[i].y, dlg_widgets[i].x, \
418 dlg_widgets[i].value, NORMAL_BUTTON, dlg_widgets[i].text, NULL))
420 #define ADD_BUTTON(i) \
421 group_add_widget_autopos (g, W(i), dlg_widgets[i].pos_flags, g->current->data)
423 /* dialog sizes */
424 const int dlg_height = 17;
425 int dlg_width = 60;
427 struct
429 Widget *widget;
430 const char *text;
431 int y;
432 int x;
433 widget_pos_flags_t pos_flags;
434 int value; /* 0 for labels and checkbox */
435 } dlg_widgets[] =
437 /* *INDENT-OFF* */
438 /* 0 - label */
439 { NULL, N_("New :"), 2, 3, WPOS_KEEP_DEFAULT, 0 },
440 /* 1 - label - name */
441 { NULL, NULL, 2, 14, WPOS_KEEP_DEFAULT, 0 },
442 /* 2 - label - size */
443 { NULL, NULL, 3, 3, WPOS_KEEP_DEFAULT, 0 },
444 /* 3 - label - date & time */
445 { NULL, NULL, 3, 43, WPOS_KEEP_TOP | WPOS_KEEP_RIGHT, 0 },
446 /* 4 - label */
447 { NULL, N_("Existing:"), 4, 3, WPOS_KEEP_DEFAULT, 0 },
448 /* 5 - label - name */
449 { NULL, NULL, 4, 14, WPOS_KEEP_DEFAULT, 0 },
450 /* 6 - label - size */
451 { NULL, NULL, 5, 3, WPOS_KEEP_DEFAULT, 0 },
452 /* 7 - label - date & time */
453 { NULL, NULL, 5, 43, WPOS_KEEP_TOP | WPOS_KEEP_RIGHT, 0 },
454 /* --------------------------------------------------- */
455 /* 8 - label */
456 { NULL, N_("Overwrite this file?"), 7, 21, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
457 /* 9 - button */
458 { NULL, N_("&Yes"), 8, 14, WPOS_KEEP_DEFAULT, REPLACE_YES },
459 /* 10 - button */
460 { NULL, N_("&No"), 8, 22, WPOS_KEEP_DEFAULT, REPLACE_NO },
461 /* 11 - button */
462 { NULL, N_("A&ppend"), 8, 29, WPOS_KEEP_DEFAULT, REPLACE_APPEND },
463 /* 12 - button */
464 { NULL, N_("&Reget"), 8, 40, WPOS_KEEP_DEFAULT, REPLACE_REGET },
465 /* --------------------------------------------------- */
466 /* 13 - label */
467 { NULL, N_("Overwrite all files?"), 10, 21, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
468 /* 14 - checkbox */
469 { NULL, N_("Don't overwrite with &zero length file"), 11, 3, WPOS_KEEP_DEFAULT, 0 },
470 /* 15 - button */
471 { NULL, N_("A&ll"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_ALL },
472 /* 16 - button */
473 { NULL, N_("&Older"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_OLDER },
474 /* 17 - button */
475 { NULL, N_("Non&e"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_NONE },
476 /* 18 - button */
477 { NULL, N_("S&maller"), 12, 25, WPOS_KEEP_DEFAULT, REPLACE_SMALLER },
478 /* 19 - button */
479 { NULL, N_("&Size differs"), 12, 40, WPOS_KEEP_DEFAULT, REPLACE_SIZE },
480 /* --------------------------------------------------- */
481 /* 20 - button */
482 { NULL, N_("&Abort"), 14, 27, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, REPLACE_ABORT }
483 /* *INDENT-ON* */
486 const int gap = 1;
488 file_op_context_ui_t *ui = ctx->ui;
489 Widget *wd;
490 WGroup *g;
491 const char *title;
493 vfs_path_t *p;
494 char *s1;
495 const char *cs1;
496 char s2[BUF_SMALL];
497 int w, bw1, bw2;
498 unsigned short i;
500 gboolean do_append = FALSE, do_reget = FALSE;
501 unsigned long yes_id, no_id;
502 int result;
504 if (mode == Foreground)
505 title = _("File exists");
506 else
507 title = _("Background process: File exists");
509 #ifdef ENABLE_NLS
511 const unsigned short num = G_N_ELEMENTS (dlg_widgets);
513 for (i = 0; i < num; i++)
514 if (dlg_widgets[i].text != NULL)
515 dlg_widgets[i].text = _(dlg_widgets[i].text);
517 #endif /* ENABLE_NLS */
519 /* create widgets to get their real widths */
520 /* new file */
521 NEW_LABEL (0, dlg_widgets[0].text);
522 /* new file name */
523 p = vfs_path_from_str (ui->src_filename);
524 s1 = vfs_path_to_str_flags (p, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
525 NEW_LABEL (1, s1);
526 vfs_path_free (p, TRUE);
527 g_free (s1);
528 /* new file size */
529 size_trunc_len (s2, sizeof (s2), ui->src_stat->st_size, 0, panels_options.kilobyte_si);
530 NEW_LABEL (2, s2);
531 /* new file modification date & time */
532 cs1 = file_date (ui->src_stat->st_mtime);
533 NEW_LABEL (3, cs1);
535 /* existing file */
536 NEW_LABEL (4, dlg_widgets[4].text);
537 /* existing file name */
538 p = vfs_path_from_str (ui->tgt_filename);
539 s1 = vfs_path_to_str_flags (p, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
540 NEW_LABEL (5, s1);
541 vfs_path_free (p, TRUE);
542 g_free (s1);
543 /* existing file size */
544 size_trunc_len (s2, sizeof (s2), ui->dst_stat->st_size, 0, panels_options.kilobyte_si);
545 NEW_LABEL (6, s2);
546 /* existing file modification date & time */
547 cs1 = file_date (ui->dst_stat->st_mtime);
548 NEW_LABEL (7, cs1);
550 /* will "Append" and "Reget" buttons be in the dialog? */
551 do_append = !S_ISDIR (ui->dst_stat->st_mode);
552 do_reget = do_append && ctx->operation == OP_COPY && ui->dst_stat->st_size != 0
553 && ui->src_stat->st_size > ui->dst_stat->st_size;
555 NEW_LABEL (8, dlg_widgets[8].text);
556 NEW_BUTTON (9);
557 NEW_BUTTON (10);
558 if (do_append)
559 NEW_BUTTON (11);
560 if (do_reget)
561 NEW_BUTTON (12);
563 NEW_LABEL (13, dlg_widgets[13].text);
564 dlg_widgets[14].widget =
565 WIDGET (check_new (dlg_widgets[14].y, dlg_widgets[14].x, FALSE, dlg_widgets[14].text));
566 for (i = 15; i <= 20; i++)
567 NEW_BUTTON (i);
569 /* place widgets */
570 dlg_width -= 2 * (2 + gap); /* inside frame */
572 /* perhaps longest line is buttons */
573 bw1 = WCOLS (9) + gap + WCOLS (10);
574 if (do_append)
575 bw1 += gap + WCOLS (11);
576 if (do_reget)
577 bw1 += gap + WCOLS (12);
578 dlg_width = MAX (dlg_width, bw1);
580 bw2 = WCOLS (15);
581 for (i = 16; i <= 19; i++)
582 bw2 += gap + WCOLS (i);
583 dlg_width = MAX (dlg_width, bw2);
585 dlg_width = MAX (dlg_width, WCOLS (8));
586 dlg_width = MAX (dlg_width, WCOLS (13));
587 dlg_width = MAX (dlg_width, WCOLS (14));
589 /* truncate file names */
590 w = WCOLS (0) + gap + WCOLS (1);
591 if (w > dlg_width)
593 WLabel *l = LABEL (W (1));
595 w = dlg_width - gap - WCOLS (0);
596 label_set_text (l, str_trunc (l->text, w));
599 w = WCOLS (4) + gap + WCOLS (5);
600 if (w > dlg_width)
602 WLabel *l = LABEL (W (5));
604 w = dlg_width - gap - WCOLS (4);
605 label_set_text (l, str_trunc (l->text, w));
608 /* real dlalog width */
609 dlg_width += 2 * (2 + gap);
611 WX (1) = WX (0) + WCOLS (0) + gap;
612 WX (5) = WX (4) + WCOLS (4) + gap;
614 /* sizes: right alignment */
615 WX (2) = dlg_width / 2 - WCOLS (2);
616 WX (6) = dlg_width / 2 - WCOLS (6);
618 w = dlg_width - (2 + gap); /* right bound */
620 /* date & time */
621 WX (3) = w - WCOLS (3);
622 WX (7) = w - WCOLS (7);
624 /* buttons: center alignment */
625 WX (9) = dlg_width / 2 - bw1 / 2;
626 WX (10) = WX (9) + WCOLS (9) + gap;
627 if (do_append)
628 WX (11) = WX (10) + WCOLS (10) + gap;
629 if (do_reget)
630 WX (12) = WX (11) + WCOLS (11) + gap;
632 WX (15) = dlg_width / 2 - bw2 / 2;
633 for (i = 16; i <= 19; i++)
634 WX (i) = WX (i - 1) + WCOLS (i - 1) + gap;
636 /* TODO: write help (ticket #3970) */
637 ui->replace_dlg =
638 dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, alarm_colors, NULL, NULL,
639 "[Replace]", title);
640 wd = WIDGET (ui->replace_dlg);
641 g = GROUP (ui->replace_dlg);
643 /* file info */
644 for (i = 0; i <= 7; i++)
645 ADD_LABEL (i);
646 group_add_widget (g, hline_new (W (7)->y - wd->y + 1, -1, -1));
648 /* label & buttons */
649 ADD_LABEL (8); /* Overwrite this file? */
650 yes_id = ADD_BUTTON (9); /* Yes */
651 no_id = ADD_BUTTON (10); /* No */
652 if (do_append)
653 ADD_BUTTON (11); /* Append */
654 if (do_reget)
655 ADD_BUTTON (12); /* Reget */
656 group_add_widget (g, hline_new (W (10)->y - wd->y + 1, -1, -1));
658 /* label & buttons */
659 ADD_LABEL (13); /* Overwrite all files? */
660 group_add_widget (g, dlg_widgets[14].widget);
661 for (i = 15; i <= 19; i++)
662 ADD_BUTTON (i);
663 group_add_widget (g, hline_new (W (19)->y - wd->y + 1, -1, -1));
665 ADD_BUTTON (20); /* Abort */
667 group_select_widget_by_id (g, safe_overwrite ? no_id : yes_id);
669 result = dlg_run (ui->replace_dlg);
671 if (result != B_CANCEL)
672 ui->dont_overwrite_with_zero = CHECK (dlg_widgets[14].widget)->state;
674 widget_destroy (wd);
676 return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
678 #undef ADD_BUTTON
679 #undef NEW_BUTTON
680 #undef ADD_LABEL
681 #undef NEW_LABEL
682 #undef WCOLS
683 #undef WX
684 #undef W
687 /* --------------------------------------------------------------------------------------------- */
689 static gboolean
690 is_wildcarded (const char *p)
692 gboolean escaped = FALSE;
694 for (; *p != '\0'; p++)
696 if (*p == '\\')
698 if (p[1] >= '1' && p[1] <= '9' && !escaped)
699 return TRUE;
700 escaped = !escaped;
702 else
704 if ((*p == '*' || *p == '?') && !escaped)
705 return TRUE;
706 escaped = FALSE;
709 return FALSE;
712 /* --------------------------------------------------------------------------------------------- */
714 static void
715 place_progress_buttons (WDialog * h, gboolean suspended)
717 const size_t i = suspended ? 2 : 1;
718 Widget *w = WIDGET (h);
719 int buttons_width;
721 buttons_width = 2 + progress_buttons[0].len + progress_buttons[3].len;
722 buttons_width += progress_buttons[i].len;
723 button_set_text (BUTTON (progress_buttons[i].w), progress_buttons[i].text);
725 progress_buttons[0].w->x = w->x + (w->cols - buttons_width) / 2;
726 progress_buttons[i].w->x = progress_buttons[0].w->x + progress_buttons[0].len + 1;
727 progress_buttons[3].w->x = progress_buttons[i].w->x + progress_buttons[i].len + 1;
730 /* --------------------------------------------------------------------------------------------- */
732 static int
733 progress_button_callback (WButton * button, int action)
735 (void) button;
736 (void) action;
738 /* don't close dialog in any case */
739 return 0;
742 /* --------------------------------------------------------------------------------------------- */
743 /*** public functions ****************************************************************************/
744 /* --------------------------------------------------------------------------------------------- */
746 FileProgressStatus
747 check_progress_buttons (file_op_context_t * ctx)
749 int c;
750 Gpm_Event event;
751 file_op_context_ui_t *ui;
753 if (ctx == NULL || ctx->ui == NULL)
754 return FILE_CONT;
756 ui = ctx->ui;
758 get_event:
759 event.x = -1; /* Don't show the GPM cursor */
760 c = tty_get_event (&event, FALSE, ctx->suspended);
761 if (c == EV_NONE)
762 return FILE_CONT;
764 /* Reinitialize to avoid old values after events other than selecting a button */
765 ui->op_dlg->ret_value = FILE_CONT;
767 dlg_process_event (ui->op_dlg, c, &event);
768 switch (ui->op_dlg->ret_value)
770 case FILE_SKIP:
771 if (ctx->suspended)
773 /* redraw dialog in case of Skip after Suspend */
774 place_progress_buttons (ui->op_dlg, FALSE);
775 widget_draw (WIDGET (ui->op_dlg));
777 ctx->suspended = FALSE;
778 return FILE_SKIP;
779 case B_CANCEL:
780 case FILE_ABORT:
781 ctx->suspended = FALSE;
782 return FILE_ABORT;
783 case FILE_SUSPEND:
784 ctx->suspended = !ctx->suspended;
785 place_progress_buttons (ui->op_dlg, ctx->suspended);
786 widget_draw (WIDGET (ui->op_dlg));
787 MC_FALLTHROUGH;
788 default:
789 if (ctx->suspended)
790 goto get_event;
791 return FILE_CONT;
795 /* --------------------------------------------------------------------------------------------- */
796 /* {{{ File progress display routines */
798 void
799 file_op_context_create_ui (file_op_context_t * ctx, gboolean with_eta,
800 filegui_dialog_type_t dialog_type)
802 file_op_context_ui_t *ui;
803 Widget *w;
804 WGroup *g;
805 int buttons_width;
806 int dlg_width = 58, dlg_height = 17;
807 int y = 2, x = 3;
809 if (ctx == NULL || ctx->ui != NULL)
810 return;
812 #ifdef ENABLE_NLS
813 if (progress_buttons[0].len == -1)
815 size_t i;
817 for (i = 0; i < G_N_ELEMENTS (progress_buttons); i++)
818 progress_buttons[i].text = _(progress_buttons[i].text);
820 #endif
822 ctx->dialog_type = dialog_type;
823 ctx->recursive_result = RECURSIVE_YES;
824 ctx->ui = g_new0 (file_op_context_ui_t, 1);
826 ui = ctx->ui;
827 ui->replace_result = REPLACE_YES;
829 ui->op_dlg =
830 dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, dialog_colors, NULL,
831 NULL, NULL, op_names[ctx->operation]);
832 w = WIDGET (ui->op_dlg);
833 g = GROUP (ui->op_dlg);
835 if (dialog_type != FILEGUI_DIALOG_DELETE_ITEM)
837 ui->showing_eta = with_eta && ctx->progress_totals_computed;
838 ui->showing_bps = with_eta;
840 ui->src_file_label = label_new (y++, x, "");
841 group_add_widget (g, ui->src_file_label);
843 ui->src_file = label_new (y++, x, "");
844 group_add_widget (g, ui->src_file);
846 ui->tgt_file_label = label_new (y++, x, "");
847 group_add_widget (g, ui->tgt_file_label);
849 ui->tgt_file = label_new (y++, x, "");
850 group_add_widget (g, ui->tgt_file);
852 ui->progress_file_gauge = gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
853 if (!classic_progressbar && (current_panel == right_panel))
854 ui->progress_file_gauge->from_left_to_right = FALSE;
855 group_add_widget_autopos (g, ui->progress_file_gauge, WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
857 ui->progress_file_label = label_new (y++, x, "");
858 group_add_widget (g, ui->progress_file_label);
860 if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
862 ui->total_bytes_label = hline_new (y++, -1, -1);
863 group_add_widget (g, ui->total_bytes_label);
865 if (ctx->progress_totals_computed)
867 ui->progress_total_gauge =
868 gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
869 if (!classic_progressbar && (current_panel == right_panel))
870 ui->progress_total_gauge->from_left_to_right = FALSE;
871 group_add_widget_autopos (g, ui->progress_total_gauge,
872 WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
875 ui->total_files_processed_label = label_new (y++, x, "");
876 group_add_widget (g, ui->total_files_processed_label);
878 ui->time_label = label_new (y++, x, "");
879 group_add_widget (g, ui->time_label);
882 else
884 ui->src_file = label_new (y++, x, "");
885 group_add_widget (g, ui->src_file);
887 ui->total_files_processed_label = label_new (y++, x, "");
888 group_add_widget (g, ui->total_files_processed_label);
891 group_add_widget (g, hline_new (y++, -1, -1));
893 progress_buttons[0].w = WIDGET (button_new (y, 0, progress_buttons[0].action,
894 progress_buttons[0].flags, progress_buttons[0].text,
895 progress_button_callback));
896 if (progress_buttons[0].len == -1)
897 progress_buttons[0].len = button_get_len (BUTTON (progress_buttons[0].w));
899 progress_buttons[1].w = WIDGET (button_new (y, 0, progress_buttons[1].action,
900 progress_buttons[1].flags, progress_buttons[1].text,
901 progress_button_callback));
902 if (progress_buttons[1].len == -1)
903 progress_buttons[1].len = button_get_len (BUTTON (progress_buttons[1].w));
905 if (progress_buttons[2].len == -1)
907 /* create and destroy button to get it length */
908 progress_buttons[2].w = WIDGET (button_new (y, 0, progress_buttons[2].action,
909 progress_buttons[2].flags,
910 progress_buttons[2].text,
911 progress_button_callback));
912 progress_buttons[2].len = button_get_len (BUTTON (progress_buttons[2].w));
913 widget_destroy (progress_buttons[2].w);
915 progress_buttons[2].w = progress_buttons[1].w;
917 progress_buttons[3].w = WIDGET (button_new (y, 0, progress_buttons[3].action,
918 progress_buttons[3].flags, progress_buttons[3].text,
919 NULL));
920 if (progress_buttons[3].len == -1)
921 progress_buttons[3].len = button_get_len (BUTTON (progress_buttons[3].w));
923 group_add_widget (g, progress_buttons[0].w);
924 group_add_widget (g, progress_buttons[1].w);
925 group_add_widget (g, progress_buttons[3].w);
927 buttons_width = 2 +
928 progress_buttons[0].len + MAX (progress_buttons[1].len, progress_buttons[2].len) +
929 progress_buttons[3].len;
931 /* adjust dialog sizes */
932 widget_set_size (w, w->y, w->x, y + 3, MAX (COLS * 2 / 3, buttons_width + 6));
934 place_progress_buttons (ui->op_dlg, FALSE);
936 widget_select (progress_buttons[0].w);
938 /* We will manage the dialog without any help, that's why
939 we have to call dlg_init */
940 dlg_init (ui->op_dlg);
943 /* --------------------------------------------------------------------------------------------- */
945 void
946 file_op_context_destroy_ui (file_op_context_t * ctx)
948 if (ctx != NULL && ctx->ui != NULL)
950 file_op_context_ui_t *ui = (file_op_context_ui_t *) ctx->ui;
952 dlg_run_done (ui->op_dlg);
953 widget_destroy (WIDGET (ui->op_dlg));
954 MC_PTR_FREE (ctx->ui);
958 /* --------------------------------------------------------------------------------------------- */
960 show progressbar for file
963 void
964 file_progress_show (file_op_context_t * ctx, off_t done, off_t total,
965 const char *stalled_msg, gboolean force_update)
967 file_op_context_ui_t *ui;
969 if (!verbose || ctx == NULL || ctx->ui == NULL)
970 return;
972 ui = ctx->ui;
974 if (total == 0)
976 gauge_show (ui->progress_file_gauge, FALSE);
977 return;
980 gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
981 gauge_show (ui->progress_file_gauge, TRUE);
983 if (!force_update)
984 return;
986 if (!ui->showing_eta || ctx->eta_secs <= 0.5)
987 label_set_text (ui->progress_file_label, stalled_msg);
988 else
990 char buffer2[BUF_TINY];
992 file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
993 if (ctx->bps == 0)
994 label_set_textv (ui->progress_file_label, "%s %s", buffer2, stalled_msg);
995 else
997 char buffer3[BUF_TINY];
999 file_bps_prepare_for_show (buffer3, ctx->bps);
1000 label_set_textv (ui->progress_file_label, "%s (%s) %s", buffer2, buffer3, stalled_msg);
1006 /* --------------------------------------------------------------------------------------------- */
1008 void
1009 file_progress_show_count (file_op_context_t * ctx, size_t done, size_t total)
1011 file_op_context_ui_t *ui;
1013 if (ctx == NULL || ctx->ui == NULL)
1014 return;
1016 ui = ctx->ui;
1018 if (ui->total_files_processed_label == NULL)
1019 return;
1021 if (ctx->progress_totals_computed)
1022 label_set_textv (ui->total_files_processed_label, _("Files processed: %zu/%zu"), done,
1023 total);
1024 else
1025 label_set_textv (ui->total_files_processed_label, _("Files processed: %zu"), done);
1028 /* --------------------------------------------------------------------------------------------- */
1030 void
1031 file_progress_show_total (file_op_total_context_t * tctx, file_op_context_t * ctx,
1032 uintmax_t copied_bytes, gboolean show_summary)
1034 char buffer2[BUF_TINY];
1035 char buffer3[BUF_TINY];
1036 file_op_context_ui_t *ui;
1038 if (ctx == NULL || ctx->ui == NULL)
1039 return;
1041 ui = ctx->ui;
1043 if (ui->progress_total_gauge != NULL)
1045 if (ctx->progress_bytes == 0)
1046 gauge_show (ui->progress_total_gauge, FALSE);
1047 else
1049 gauge_set_value (ui->progress_total_gauge, 1024,
1050 (int) (1024 * copied_bytes / ctx->progress_bytes));
1051 gauge_show (ui->progress_total_gauge, TRUE);
1055 if (!show_summary && tctx->bps == 0)
1056 return;
1058 if (ui->time_label != NULL)
1060 gint64 tv_current;
1061 char buffer4[BUF_TINY];
1063 tv_current = g_get_monotonic_time ();
1064 file_frmt_time (buffer2, (tv_current - tctx->transfer_start) / G_USEC_PER_SEC);
1066 if (ctx->progress_totals_computed)
1068 file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
1069 if (tctx->bps == 0)
1070 label_set_textv (ui->time_label, _("Time: %s %s"), buffer2, buffer3);
1071 else
1073 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1074 label_set_textv (ui->time_label, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
1077 else
1079 if (tctx->bps == 0)
1080 label_set_textv (ui->time_label, _("Time: %s"), buffer2);
1081 else
1083 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1084 label_set_textv (ui->time_label, _("Time: %s (%s)"), buffer2, buffer4);
1089 if (ui->total_bytes_label != NULL)
1091 size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
1093 if (!ctx->progress_totals_computed)
1094 hline_set_textv (ui->total_bytes_label, _(" Total: %s "), buffer2);
1095 else
1097 size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
1098 hline_set_textv (ui->total_bytes_label, _(" Total: %s/%s "), buffer2, buffer3);
1103 /* }}} */
1105 /* --------------------------------------------------------------------------------------------- */
1107 void
1108 file_progress_show_source (file_op_context_t * ctx, const vfs_path_t * vpath)
1110 file_op_context_ui_t *ui;
1112 if (ctx == NULL || ctx->ui == NULL)
1113 return;
1115 ui = ctx->ui;
1117 if (vpath != NULL)
1119 char *s;
1121 s = vfs_path_tokens_get (vpath, -1, 1);
1122 label_set_text (ui->src_file_label, _("Source"));
1123 label_set_text (ui->src_file, truncFileString (ui->op_dlg, s));
1124 g_free (s);
1126 else
1128 label_set_text (ui->src_file_label, "");
1129 label_set_text (ui->src_file, "");
1133 /* --------------------------------------------------------------------------------------------- */
1135 void
1136 file_progress_show_target (file_op_context_t * ctx, const vfs_path_t * vpath)
1138 file_op_context_ui_t *ui;
1140 if (ctx == NULL || ctx->ui == NULL)
1141 return;
1143 ui = ctx->ui;
1145 if (vpath != NULL)
1147 label_set_text (ui->tgt_file_label, _("Target"));
1148 label_set_text (ui->tgt_file, truncFileStringSecure (ui->op_dlg, vfs_path_as_str (vpath)));
1150 else
1152 label_set_text (ui->tgt_file_label, "");
1153 label_set_text (ui->tgt_file, "");
1157 /* --------------------------------------------------------------------------------------------- */
1159 gboolean
1160 file_progress_show_deleting (file_op_context_t * ctx, const char *s, size_t * count)
1162 static gint64 timestamp = 0;
1163 /* update with 25 FPS rate */
1164 static const gint64 delay = G_USEC_PER_SEC / 25;
1166 gboolean ret;
1168 if (ctx == NULL || ctx->ui == NULL)
1169 return FALSE;
1171 ret = mc_time_elapsed (&timestamp, delay);
1173 if (ret)
1175 file_op_context_ui_t *ui;
1177 ui = ctx->ui;
1179 if (ui->src_file_label != NULL)
1180 label_set_text (ui->src_file_label, _("Deleting"));
1182 label_set_text (ui->src_file, truncFileStringSecure (ui->op_dlg, s));
1185 if (count != NULL)
1186 (*count)++;
1188 return ret;
1191 /* --------------------------------------------------------------------------------------------- */
1193 FileProgressStatus
1194 file_progress_real_query_replace (file_op_context_t * ctx, enum OperationMode mode,
1195 const char *src, struct stat * src_stat,
1196 const char *dst, struct stat * dst_stat)
1198 file_op_context_ui_t *ui;
1199 FileProgressStatus replace_with_zero;
1201 if (ctx == NULL || ctx->ui == NULL)
1202 return FILE_CONT;
1204 ui = ctx->ui;
1206 if (ui->replace_result == REPLACE_YES || ui->replace_result == REPLACE_NO
1207 || ui->replace_result == REPLACE_APPEND)
1209 ui->src_filename = src;
1210 ui->src_stat = src_stat;
1211 ui->tgt_filename = dst;
1212 ui->dst_stat = dst_stat;
1213 ui->replace_result = overwrite_query_dialog (ctx, mode);
1216 replace_with_zero = (src_stat->st_size == 0
1217 && ui->dont_overwrite_with_zero) ? FILE_SKIP : FILE_CONT;
1219 switch (ui->replace_result)
1221 case REPLACE_OLDER:
1222 do_refresh ();
1223 if (src_stat->st_mtime > dst_stat->st_mtime)
1224 return replace_with_zero;
1225 else
1226 return FILE_SKIP;
1228 case REPLACE_SIZE:
1229 do_refresh ();
1230 if (src_stat->st_size == dst_stat->st_size)
1231 return FILE_SKIP;
1232 else
1233 return replace_with_zero;
1235 case REPLACE_SMALLER:
1236 do_refresh ();
1237 if (src_stat->st_size > dst_stat->st_size)
1238 return FILE_CONT;
1239 else
1240 return FILE_SKIP;
1242 case REPLACE_ALL:
1243 do_refresh ();
1244 return replace_with_zero;
1246 case REPLACE_REGET:
1247 /* Careful: we fall through and set do_append */
1248 ctx->do_reget = dst_stat->st_size;
1249 MC_FALLTHROUGH;
1251 case REPLACE_APPEND:
1252 ctx->do_append = TRUE;
1253 MC_FALLTHROUGH;
1255 case REPLACE_YES:
1256 do_refresh ();
1257 return FILE_CONT;
1259 case REPLACE_NO:
1260 case REPLACE_NONE:
1261 do_refresh ();
1262 return FILE_SKIP;
1264 case REPLACE_ABORT:
1265 default:
1266 return FILE_ABORT;
1270 /* --------------------------------------------------------------------------------------------- */
1272 char *
1273 file_mask_dialog (file_op_context_t * ctx, FileOperation operation, gboolean only_one,
1274 const char *format, const void *text, const char *def_text, gboolean * do_bg)
1276 size_t fmd_xlen;
1277 vfs_path_t *vpath;
1278 gboolean source_easy_patterns = easy_patterns;
1279 char fmd_buf[BUF_MEDIUM];
1280 char *dest_dir = NULL;
1281 char *tmp;
1282 char *def_text_secure;
1284 if (ctx == NULL)
1285 return NULL;
1287 /* unselect checkbox if target filesystem doesn't support attributes */
1288 ctx->op_preserve = copymove_persistent_attr && filegui__check_attrs_on_fs (def_text);
1289 ctx->stable_symlinks = FALSE;
1290 *do_bg = FALSE;
1292 /* filter out a possible password from def_text */
1293 vpath = vfs_path_from_str_flags (def_text, only_one ? VPF_NO_CANON : VPF_NONE);
1294 tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1295 vfs_path_free (vpath, TRUE);
1297 if (source_easy_patterns)
1298 def_text_secure = strutils_glob_escape (tmp);
1299 else
1300 def_text_secure = strutils_regex_escape (tmp);
1301 g_free (tmp);
1303 if (only_one)
1305 int format_len, text_len;
1306 int max_len;
1308 format_len = str_term_width1 (format);
1309 text_len = str_term_width1 (text);
1310 max_len = COLS - 2 - 6;
1312 if (format_len + text_len <= max_len)
1314 fmd_xlen = format_len + text_len + 6;
1315 fmd_xlen = MAX (fmd_xlen, 68);
1317 else
1319 text = str_trunc ((const char *) text, max_len - format_len);
1320 fmd_xlen = max_len + 6;
1323 g_snprintf (fmd_buf, sizeof (fmd_buf), format, (const char *) text);
1325 else
1327 fmd_xlen = COLS * 2 / 3;
1328 fmd_xlen = MAX (fmd_xlen, 68);
1329 g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1333 char *source_mask = NULL;
1334 char *orig_mask;
1335 int val;
1336 struct stat buf;
1338 quick_widget_t quick_widgets[] = {
1339 /* *INDENT-OFF* */
1340 QUICK_LABELED_INPUT (fmd_buf, input_label_above, easy_patterns ? "*" : "^(.*)$",
1341 "input-def", &source_mask, NULL, FALSE, FALSE,
1342 INPUT_COMPLETE_FILENAMES),
1343 QUICK_START_COLUMNS,
1344 QUICK_SEPARATOR (FALSE),
1345 QUICK_NEXT_COLUMN,
1346 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns, NULL),
1347 QUICK_STOP_COLUMNS,
1348 QUICK_LABELED_INPUT (N_("to:"), input_label_above, def_text_secure, "input2", &dest_dir,
1349 NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1350 QUICK_SEPARATOR (TRUE),
1351 QUICK_START_COLUMNS,
1352 QUICK_CHECKBOX (N_("Follow &links"), &ctx->follow_links, NULL),
1353 QUICK_CHECKBOX (N_("Preserve &attributes"), &ctx->op_preserve, NULL),
1354 QUICK_NEXT_COLUMN,
1355 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx->dive_into_subdirs, NULL),
1356 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx->stable_symlinks, NULL),
1357 QUICK_STOP_COLUMNS,
1358 QUICK_START_BUTTONS (TRUE, TRUE),
1359 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
1360 #ifdef ENABLE_BACKGROUND
1361 QUICK_BUTTON (N_("&Background"), B_USER, NULL, NULL),
1362 #endif /* ENABLE_BACKGROUND */
1363 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1364 QUICK_END
1365 /* *INDENT-ON* */
1368 quick_dialog_t qdlg = {
1369 -1, -1, fmd_xlen,
1370 op_names[operation], "[Mask Copy/Rename]",
1371 quick_widgets, NULL, NULL
1374 ask_file_mask:
1375 val = quick_dialog_skip (&qdlg, 4);
1377 if (val == B_CANCEL)
1379 g_free (def_text_secure);
1380 return NULL;
1383 ctx->stat_func = ctx->follow_links ? mc_stat : mc_lstat;
1385 if (ctx->op_preserve)
1387 ctx->preserve = TRUE;
1388 ctx->umask_kill = 0777777;
1389 ctx->preserve_uidgid = (geteuid () == 0);
1391 else
1393 mode_t i2;
1395 ctx->preserve = ctx->preserve_uidgid = FALSE;
1396 i2 = umask (0);
1397 umask (i2);
1398 ctx->umask_kill = i2 ^ 0777777;
1401 if ((dest_dir == NULL) || (*dest_dir == '\0'))
1403 g_free (def_text_secure);
1404 g_free (source_mask);
1405 g_free (dest_dir);
1406 return NULL;
1409 ctx->search_handle = mc_search_new (source_mask, NULL);
1411 if (ctx->search_handle == NULL)
1413 message (D_ERROR, MSG_ERROR, _("Invalid source pattern '%s'"), source_mask);
1414 MC_PTR_FREE (dest_dir);
1415 MC_PTR_FREE (source_mask);
1416 goto ask_file_mask;
1419 g_free (def_text_secure);
1420 g_free (source_mask);
1422 ctx->search_handle->is_case_sensitive = TRUE;
1423 if (source_easy_patterns)
1424 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1425 else
1426 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1428 tmp = dest_dir;
1429 dest_dir = tilde_expand (tmp);
1430 g_free (tmp);
1431 vpath = vfs_path_from_str (dest_dir);
1433 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1434 if (ctx->dest_mask == NULL)
1435 ctx->dest_mask = dest_dir;
1436 else
1437 ctx->dest_mask++;
1439 orig_mask = ctx->dest_mask;
1441 if (*ctx->dest_mask == '\0'
1442 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1443 && (!only_one
1444 || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1445 || (ctx->dive_into_subdirs
1446 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1447 || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1448 ctx->dest_mask = g_strdup ("\\0");
1449 else
1451 ctx->dest_mask = g_strdup (ctx->dest_mask);
1452 *orig_mask = '\0';
1455 if (*dest_dir == '\0')
1457 g_free (dest_dir);
1458 dest_dir = g_strdup ("./");
1461 vfs_path_free (vpath, TRUE);
1463 if (val == B_USER)
1464 *do_bg = TRUE;
1467 return dest_dir;
1470 /* --------------------------------------------------------------------------------------------- */