src/filemanager/chattr.c: fix coding style.
[midnight-commander.git] / src / filemanager / filegui.c
blobb6b0032d852e247d632e5637ab42b41cd40470ce
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-2024
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-2023
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/strutil.h"
151 #include "lib/timefmt.h" /* file_date() */
152 #include "lib/util.h"
153 #include "lib/widget.h"
155 #include "src/setup.h" /* verbose, safe_overwrite */
157 #include "filemanager.h"
158 #include "fileopctx.h" /* FILE_CONT */
160 #include "filegui.h"
162 /* }}} */
164 /*** global variables ****************************************************************************/
166 gboolean classic_progressbar = TRUE;
168 /*** file scope macro definitions ****************************************************************/
170 #define truncFileStringSecure(dlg, s) path_trunc (s, WIDGET (dlg)->rect.cols - 10)
172 /*** file scope type declarations ****************************************************************/
174 /* *INDENT-OFF* */
175 typedef enum {
176 MSDOS_SUPER_MAGIC = 0x4d44,
177 NTFS_SB_MAGIC = 0x5346544e,
178 FUSE_MAGIC = 0x65735546,
179 PROC_SUPER_MAGIC = 0x9fa0,
180 SMB_SUPER_MAGIC = 0x517B,
181 NCP_SUPER_MAGIC = 0x564c,
182 USBDEVICE_SUPER_MAGIC = 0x9fa2
183 } filegui_nonattrs_fs_t;
184 /* *INDENT-ON* */
186 /* Used for button result values */
187 typedef enum
189 REPLACE_YES = B_USER,
190 REPLACE_NO,
191 REPLACE_APPEND,
192 REPLACE_REGET,
193 REPLACE_ALL,
194 REPLACE_OLDER,
195 REPLACE_NONE,
196 REPLACE_SMALLER,
197 REPLACE_SIZE,
198 REPLACE_ABORT
199 } replace_action_t;
201 /* This structure describes the UI and internal data required by a file
202 * operation context.
204 typedef struct
206 /* ETA and bps */
207 gboolean showing_eta;
208 gboolean showing_bps;
210 /* Dialog and widgets for the operation progress window */
211 WDialog *op_dlg;
212 /* Source file: label and name */
213 WLabel *src_file_label;
214 WLabel *src_file;
215 /* Target file: label and name */
216 WLabel *tgt_file_label;
217 WLabel *tgt_file;
219 WGauge *progress_file_gauge;
220 WLabel *progress_file_label;
222 WGauge *progress_total_gauge;
224 WLabel *total_files_processed_label;
225 WLabel *time_label;
226 WHLine *total_bytes_label;
228 /* Query replace dialog */
229 WDialog *replace_dlg;
230 const char *src_filename;
231 const char *tgt_filename;
232 replace_action_t replace_result;
233 gboolean dont_overwrite_with_zero;
235 struct stat *src_stat, *dst_stat;
236 } file_op_context_ui_t;
238 /*** forward declarations (file scope functions) *************************************************/
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[] = {
250 /* *INDENT-OFF* */
251 { NULL, FILE_SKIP, N_("&Skip"), NORMAL_BUTTON, -1 },
252 { NULL, FILE_SUSPEND, N_("S&uspend"), NORMAL_BUTTON, -1 },
253 { NULL, FILE_SUSPEND, N_("Con&tinue"), NORMAL_BUTTON, -1 },
254 { NULL, FILE_ABORT, N_("&Abort"), NORMAL_BUTTON, -1 }
255 /* *INDENT-ON* */
258 /* --------------------------------------------------------------------------------------------- */
259 /*** file scope functions ************************************************************************/
260 /* --------------------------------------------------------------------------------------------- */
262 /* Return true if statvfs works. This is false for statvfs on systems
263 with GNU libc on Linux kernels before 2.6.36, which stats all
264 preceding entries in /proc/mounts; that makes df hang if even one
265 of the corresponding file systems is hard-mounted but not available. */
267 #if USE_STATVFS && ! (! defined STAT_STATVFS && defined STAT_STATVFS64)
268 static int
269 statvfs_works (void)
271 #if ! (defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__))
272 return 1;
273 #else
274 static int statvfs_works_cache = -1;
275 struct utsname name;
277 if (statvfs_works_cache < 0)
278 statvfs_works_cache = (uname (&name) == 0 && 0 <= str_verscmp (name.release, "2.6.36"));
279 return statvfs_works_cache;
280 #endif
282 #endif
284 /* --------------------------------------------------------------------------------------------- */
286 static gboolean
287 filegui__check_attrs_on_fs (const char *fs_path)
289 STRUCT_STATVFS stfs;
291 #if USE_STATVFS && defined(STAT_STATVFS)
292 if (statvfs_works () && statvfs (fs_path, &stfs) != 0)
293 return TRUE;
294 #else
295 if (STATFS (fs_path, &stfs) != 0)
296 return TRUE;
297 #endif
299 #if (USE_STATVFS && defined(HAVE_STRUCT_STATVFS_F_TYPE)) || \
300 (!USE_STATVFS && defined(HAVE_STRUCT_STATFS_F_TYPE))
301 switch ((filegui_nonattrs_fs_t) stfs.f_type)
303 case MSDOS_SUPER_MAGIC:
304 case NTFS_SB_MAGIC:
305 case PROC_SUPER_MAGIC:
306 case SMB_SUPER_MAGIC:
307 case NCP_SUPER_MAGIC:
308 case USBDEVICE_SUPER_MAGIC:
309 return FALSE;
310 default:
311 break;
313 #elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
314 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdos") == 0
315 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdosfs") == 0
316 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
317 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "procfs") == 0
318 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
319 || strstr (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fusefs") != NULL)
320 return FALSE;
321 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
322 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "pcfs") == 0
323 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
324 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "proc") == 0
325 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
326 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fuse") == 0)
327 return FALSE;
328 #endif
330 return TRUE;
333 /* --------------------------------------------------------------------------------------------- */
335 static void
336 file_frmt_time (char *buffer, double eta_secs)
338 int eta_hours, eta_mins, eta_s;
340 eta_hours = (int) (eta_secs / (60 * 60));
341 eta_mins = (int) ((eta_secs - (eta_hours * 60 * 60)) / 60);
342 eta_s = (int) (eta_secs - (eta_hours * 60 * 60 + eta_mins * 60));
343 g_snprintf (buffer, BUF_TINY, _("%d:%02d:%02d"), eta_hours, eta_mins, eta_s);
346 /* --------------------------------------------------------------------------------------------- */
348 static void
349 file_eta_prepare_for_show (char *buffer, double eta_secs, gboolean always_show)
351 char _fmt_buff[BUF_TINY];
353 if (eta_secs <= 0.5 && !always_show)
355 *buffer = '\0';
356 return;
359 if (eta_secs <= 0.5)
360 eta_secs = 1;
361 file_frmt_time (_fmt_buff, eta_secs);
362 g_snprintf (buffer, BUF_TINY, _("ETA %s"), _fmt_buff);
365 /* --------------------------------------------------------------------------------------------- */
367 static void
368 file_bps_prepare_for_show (char *buffer, long bps)
370 if (bps > 1024 * 1024)
371 g_snprintf (buffer, BUF_TINY, _("%.2f MB/s"), bps / (1024 * 1024.0));
372 else if (bps > 1024)
373 g_snprintf (buffer, BUF_TINY, _("%.2f KB/s"), bps / 1024.0);
374 else if (bps > 1)
375 g_snprintf (buffer, BUF_TINY, _("%ld B/s"), bps);
376 else
377 *buffer = '\0';
380 /* --------------------------------------------------------------------------------------------- */
382 static cb_ret_t
383 file_ui_op_dlg_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
385 switch (msg)
387 case MSG_ACTION:
388 /* Do not close the dialog because the query dialog will be shown */
389 if (parm == CK_Cancel)
391 DIALOG (w)->ret_value = FILE_ABORT; /* for check_progress_buttons() */
392 return MSG_HANDLED;
394 return MSG_NOT_HANDLED;
396 default:
397 return dlg_default_callback (w, sender, msg, parm, data);
401 /* --------------------------------------------------------------------------------------------- */
403 /* The dialog layout:
405 * +---------------------- File exists -----------------------+
406 * | New : /path/to/original_file_name | // 0, 1
407 * | 1234567 feb 4 2017 13:38 | // 2, 3
408 * | Existing: /path/to/target_file_name | // 4, 5
409 * | 1234567890 feb 4 2017 13:37 | // 6, 7
410 * +----------------------------------------------------------+
411 * | Overwrite this file? | // 8
412 * | [ Yes ] [ No ] [ Append ] [ Reget ] | // 9, 10, 11, 12
413 * +----------------------------------------------------------+
414 * | Overwrite all files? | // 13
415 * | [ ] Don't overwrite with zero length file | // 14
416 * | [ All ] [ Older ] [None] [ Smaller ] [ Size differs ] | // 15, 16, 17, 18, 19
417 * +----------------------------------------------------------|
418 * | [ Abort ] | // 20
419 * +----------------------------------------------------------+
422 static replace_action_t
423 overwrite_query_dialog (file_op_context_t *ctx, enum OperationMode mode)
425 #define W(i) dlg_widgets[i].widget
426 #define WX(i) W(i)->rect.x
427 #define WY(i) W(i)->rect.y
428 #define WCOLS(i) W(i)->rect.cols
430 #define NEW_LABEL(i, text) \
431 W(i) = WIDGET (label_new (dlg_widgets[i].y, dlg_widgets[i].x, text))
433 #define ADD_LABEL(i) \
434 group_add_widget_autopos (g, W(i), dlg_widgets[i].pos_flags, \
435 g->current != NULL ? g->current->data : NULL)
437 #define NEW_BUTTON(i) \
438 W(i) = WIDGET (button_new (dlg_widgets[i].y, dlg_widgets[i].x, \
439 dlg_widgets[i].value, NORMAL_BUTTON, dlg_widgets[i].text, NULL))
441 #define ADD_BUTTON(i) \
442 group_add_widget_autopos (g, W(i), dlg_widgets[i].pos_flags, g->current->data)
444 /* dialog sizes */
445 const int dlg_height = 17;
446 int dlg_width = 60;
448 struct
450 Widget *widget;
451 const char *text;
452 int y;
453 int x;
454 widget_pos_flags_t pos_flags;
455 int value; /* 0 for labels and checkbox */
456 } dlg_widgets[] = {
457 /* *INDENT-OFF* */
458 /* 0 - label */
459 { NULL, N_("New :"), 2, 3, WPOS_KEEP_DEFAULT, 0 },
460 /* 1 - label - name */
461 { NULL, NULL, 2, 14, WPOS_KEEP_DEFAULT, 0 },
462 /* 2 - label - size */
463 { NULL, NULL, 3, 3, WPOS_KEEP_DEFAULT, 0 },
464 /* 3 - label - date & time */
465 { NULL, NULL, 3, 43, WPOS_KEEP_TOP | WPOS_KEEP_RIGHT, 0 },
466 /* 4 - label */
467 { NULL, N_("Existing:"), 4, 3, WPOS_KEEP_DEFAULT, 0 },
468 /* 5 - label - name */
469 { NULL, NULL, 4, 14, WPOS_KEEP_DEFAULT, 0 },
470 /* 6 - label - size */
471 { NULL, NULL, 5, 3, WPOS_KEEP_DEFAULT, 0 },
472 /* 7 - label - date & time */
473 { NULL, NULL, 5, 43, WPOS_KEEP_TOP | WPOS_KEEP_RIGHT, 0 },
474 /* --------------------------------------------------- */
475 /* 8 - label */
476 { NULL, N_("Overwrite this file?"), 7, 21, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
477 /* 9 - button */
478 { NULL, N_("&Yes"), 8, 14, WPOS_KEEP_DEFAULT, REPLACE_YES },
479 /* 10 - button */
480 { NULL, N_("&No"), 8, 22, WPOS_KEEP_DEFAULT, REPLACE_NO },
481 /* 11 - button */
482 { NULL, N_("A&ppend"), 8, 29, WPOS_KEEP_DEFAULT, REPLACE_APPEND },
483 /* 12 - button */
484 { NULL, N_("&Reget"), 8, 40, WPOS_KEEP_DEFAULT, REPLACE_REGET },
485 /* --------------------------------------------------- */
486 /* 13 - label */
487 { NULL, N_("Overwrite all files?"), 10, 21, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
488 /* 14 - checkbox */
489 { NULL, N_("Don't overwrite with &zero length file"), 11, 3, WPOS_KEEP_DEFAULT, 0 },
490 /* 15 - button */
491 { NULL, N_("A&ll"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_ALL },
492 /* 16 - button */
493 { NULL, N_("&Older"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_OLDER },
494 /* 17 - button */
495 { NULL, N_("Non&e"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_NONE },
496 /* 18 - button */
497 { NULL, N_("S&maller"), 12, 25, WPOS_KEEP_DEFAULT, REPLACE_SMALLER },
498 /* 19 - button */
499 { NULL, N_("&Size differs"), 12, 40, WPOS_KEEP_DEFAULT, REPLACE_SIZE },
500 /* --------------------------------------------------- */
501 /* 20 - button */
502 { NULL, N_("&Abort"), 14, 27, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, REPLACE_ABORT }
503 /* *INDENT-ON* */
506 const int gap = 1;
508 file_op_context_ui_t *ui = ctx->ui;
509 Widget *wd;
510 WGroup *g;
511 const char *title;
513 vfs_path_t *p;
514 char *s1;
515 const char *cs1;
516 char s2[BUF_SMALL];
517 int w, bw1, bw2;
518 unsigned short i;
520 gboolean do_append = FALSE, do_reget = FALSE;
521 unsigned long yes_id, no_id;
522 int result;
524 if (mode == Foreground)
525 title = _("File exists");
526 else
527 title = _("Background process: File exists");
529 #ifdef ENABLE_NLS
531 const unsigned short num = G_N_ELEMENTS (dlg_widgets);
533 for (i = 0; i < num; i++)
534 if (dlg_widgets[i].text != NULL)
535 dlg_widgets[i].text = _(dlg_widgets[i].text);
537 #endif /* ENABLE_NLS */
539 /* create widgets to get their real widths */
540 /* new file */
541 NEW_LABEL (0, dlg_widgets[0].text);
542 /* new file name */
543 p = vfs_path_from_str (ui->src_filename);
544 s1 = vfs_path_to_str_flags (p, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
545 NEW_LABEL (1, s1);
546 vfs_path_free (p, TRUE);
547 g_free (s1);
548 /* new file size */
549 size_trunc_len (s2, sizeof (s2), ui->src_stat->st_size, 0, panels_options.kilobyte_si);
550 NEW_LABEL (2, s2);
551 /* new file modification date & time */
552 cs1 = file_date (ui->src_stat->st_mtime);
553 NEW_LABEL (3, cs1);
555 /* existing file */
556 NEW_LABEL (4, dlg_widgets[4].text);
557 /* existing file name */
558 p = vfs_path_from_str (ui->tgt_filename);
559 s1 = vfs_path_to_str_flags (p, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
560 NEW_LABEL (5, s1);
561 vfs_path_free (p, TRUE);
562 g_free (s1);
563 /* existing file size */
564 size_trunc_len (s2, sizeof (s2), ui->dst_stat->st_size, 0, panels_options.kilobyte_si);
565 NEW_LABEL (6, s2);
566 /* existing file modification date & time */
567 cs1 = file_date (ui->dst_stat->st_mtime);
568 NEW_LABEL (7, cs1);
570 /* will "Append" and "Reget" buttons be in the dialog? */
571 do_append = !S_ISDIR (ui->dst_stat->st_mode);
572 do_reget = do_append && ui->dst_stat->st_size != 0
573 && ui->src_stat->st_size > ui->dst_stat->st_size;
575 NEW_LABEL (8, dlg_widgets[8].text);
576 NEW_BUTTON (9);
577 NEW_BUTTON (10);
578 if (do_append)
579 NEW_BUTTON (11);
580 if (do_reget)
581 NEW_BUTTON (12);
583 NEW_LABEL (13, dlg_widgets[13].text);
584 dlg_widgets[14].widget =
585 WIDGET (check_new (dlg_widgets[14].y, dlg_widgets[14].x, FALSE, dlg_widgets[14].text));
586 for (i = 15; i <= 20; i++)
587 NEW_BUTTON (i);
589 /* place widgets */
590 dlg_width -= 2 * (2 + gap); /* inside frame */
592 /* perhaps longest line is buttons */
593 bw1 = WCOLS (9) + gap + WCOLS (10);
594 if (do_append)
595 bw1 += gap + WCOLS (11);
596 if (do_reget)
597 bw1 += gap + WCOLS (12);
598 dlg_width = MAX (dlg_width, bw1);
600 bw2 = WCOLS (15);
601 for (i = 16; i <= 19; i++)
602 bw2 += gap + WCOLS (i);
603 dlg_width = MAX (dlg_width, bw2);
605 dlg_width = MAX (dlg_width, WCOLS (8));
606 dlg_width = MAX (dlg_width, WCOLS (13));
607 dlg_width = MAX (dlg_width, WCOLS (14));
609 /* truncate file names */
610 w = WCOLS (0) + gap + WCOLS (1);
611 if (w > dlg_width)
613 WLabel *l = LABEL (W (1));
615 w = dlg_width - gap - WCOLS (0);
616 label_set_text (l, str_trunc (l->text, w));
619 w = WCOLS (4) + gap + WCOLS (5);
620 if (w > dlg_width)
622 WLabel *l = LABEL (W (5));
624 w = dlg_width - gap - WCOLS (4);
625 label_set_text (l, str_trunc (l->text, w));
628 /* real dlalog width */
629 dlg_width += 2 * (2 + gap);
631 WX (1) = WX (0) + WCOLS (0) + gap;
632 WX (5) = WX (4) + WCOLS (4) + gap;
634 /* sizes: right alignment */
635 WX (2) = dlg_width / 2 - WCOLS (2);
636 WX (6) = dlg_width / 2 - WCOLS (6);
638 w = dlg_width - (2 + gap); /* right bound */
640 /* date & time */
641 WX (3) = w - WCOLS (3);
642 WX (7) = w - WCOLS (7);
644 /* buttons: center alignment */
645 WX (9) = dlg_width / 2 - bw1 / 2;
646 WX (10) = WX (9) + WCOLS (9) + gap;
647 if (do_append)
648 WX (11) = WX (10) + WCOLS (10) + gap;
649 if (do_reget)
650 WX (12) = WX (11) + WCOLS (11) + gap;
652 WX (15) = dlg_width / 2 - bw2 / 2;
653 for (i = 16; i <= 19; i++)
654 WX (i) = WX (i - 1) + WCOLS (i - 1) + gap;
656 /* TODO: write help (ticket #3970) */
657 ui->replace_dlg =
658 dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, alarm_colors, NULL, NULL,
659 "[Replace]", title);
660 wd = WIDGET (ui->replace_dlg);
661 g = GROUP (ui->replace_dlg);
663 /* file info */
664 for (i = 0; i <= 7; i++)
665 ADD_LABEL (i);
666 group_add_widget (g, hline_new (WY (7) - wd->rect.y + 1, -1, -1));
668 /* label & buttons */
669 ADD_LABEL (8); /* Overwrite this file? */
670 yes_id = ADD_BUTTON (9); /* Yes */
671 no_id = ADD_BUTTON (10); /* No */
672 if (do_append)
673 ADD_BUTTON (11); /* Append */
674 if (do_reget)
675 ADD_BUTTON (12); /* Reget */
676 group_add_widget (g, hline_new (WY (10) - wd->rect.y + 1, -1, -1));
678 /* label & buttons */
679 ADD_LABEL (13); /* Overwrite all files? */
680 group_add_widget (g, dlg_widgets[14].widget);
681 for (i = 15; i <= 19; i++)
682 ADD_BUTTON (i);
683 group_add_widget (g, hline_new (WY (19) - wd->rect.y + 1, -1, -1));
685 ADD_BUTTON (20); /* Abort */
687 group_select_widget_by_id (g, safe_overwrite ? no_id : yes_id);
689 result = dlg_run (ui->replace_dlg);
691 if (result != B_CANCEL)
692 ui->dont_overwrite_with_zero = CHECK (dlg_widgets[14].widget)->state;
694 widget_destroy (wd);
696 return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
698 #undef ADD_BUTTON
699 #undef NEW_BUTTON
700 #undef ADD_LABEL
701 #undef NEW_LABEL
702 #undef WCOLS
703 #undef WX
704 #undef W
707 /* --------------------------------------------------------------------------------------------- */
709 static gboolean
710 is_wildcarded (const char *p)
712 gboolean escaped = FALSE;
714 for (; *p != '\0'; p++)
716 if (*p == '\\')
718 if (p[1] >= '1' && p[1] <= '9' && !escaped)
719 return TRUE;
720 escaped = !escaped;
722 else
724 if ((*p == '*' || *p == '?') && !escaped)
725 return TRUE;
726 escaped = FALSE;
729 return FALSE;
732 /* --------------------------------------------------------------------------------------------- */
734 static void
735 place_progress_buttons (WDialog *h, gboolean suspended)
737 const size_t i = suspended ? 2 : 1;
738 Widget *w = WIDGET (h);
739 int buttons_width;
741 buttons_width = 2 + progress_buttons[0].len + progress_buttons[3].len;
742 buttons_width += progress_buttons[i].len;
743 button_set_text (BUTTON (progress_buttons[i].w), progress_buttons[i].text);
745 progress_buttons[0].w->rect.x = w->rect.x + (w->rect.cols - buttons_width) / 2;
746 progress_buttons[i].w->rect.x = progress_buttons[0].w->rect.x + progress_buttons[0].len + 1;
747 progress_buttons[3].w->rect.x = progress_buttons[i].w->rect.x + progress_buttons[i].len + 1;
750 /* --------------------------------------------------------------------------------------------- */
752 static int
753 progress_button_callback (WButton *button, int action)
755 (void) button;
756 (void) action;
758 /* don't close dialog in any case */
759 return 0;
762 /* --------------------------------------------------------------------------------------------- */
763 /*** public functions ****************************************************************************/
764 /* --------------------------------------------------------------------------------------------- */
766 FileProgressStatus
767 check_progress_buttons (file_op_context_t *ctx)
769 int c;
770 Gpm_Event event;
771 file_op_context_ui_t *ui;
773 if (ctx == NULL || ctx->ui == NULL)
774 return FILE_CONT;
776 ui = ctx->ui;
778 get_event:
779 event.x = -1; /* Don't show the GPM cursor */
780 c = tty_get_event (&event, FALSE, ctx->suspended);
781 if (c == EV_NONE)
782 return FILE_CONT;
784 /* Reinitialize to avoid old values after events other than selecting a button */
785 ui->op_dlg->ret_value = FILE_CONT;
787 dlg_process_event (ui->op_dlg, c, &event);
788 switch (ui->op_dlg->ret_value)
790 case FILE_SKIP:
791 if (ctx->suspended)
793 /* redraw dialog in case of Skip after Suspend */
794 place_progress_buttons (ui->op_dlg, FALSE);
795 widget_draw (WIDGET (ui->op_dlg));
797 ctx->suspended = FALSE;
798 return FILE_SKIP;
799 case B_CANCEL:
800 case FILE_ABORT:
801 ctx->suspended = FALSE;
802 return FILE_ABORT;
803 case FILE_SUSPEND:
804 ctx->suspended = !ctx->suspended;
805 place_progress_buttons (ui->op_dlg, ctx->suspended);
806 widget_draw (WIDGET (ui->op_dlg));
807 MC_FALLTHROUGH;
808 default:
809 if (ctx->suspended)
810 goto get_event;
811 return FILE_CONT;
815 /* --------------------------------------------------------------------------------------------- */
816 /* {{{ File progress display routines */
818 void
819 file_op_context_create_ui (file_op_context_t *ctx, gboolean with_eta,
820 filegui_dialog_type_t dialog_type)
822 file_op_context_ui_t *ui;
823 Widget *w;
824 WGroup *g;
825 int buttons_width;
826 int dlg_width = 58, dlg_height = 17;
827 int y = 2, x = 3;
828 WRect r;
830 if (ctx == NULL || ctx->ui != NULL)
831 return;
833 #ifdef ENABLE_NLS
834 if (progress_buttons[0].len == -1)
836 size_t i;
838 for (i = 0; i < G_N_ELEMENTS (progress_buttons); i++)
839 progress_buttons[i].text = _(progress_buttons[i].text);
841 #endif
843 ctx->dialog_type = dialog_type;
844 ctx->recursive_result = RECURSIVE_YES;
845 ctx->ui = g_new0 (file_op_context_ui_t, 1);
847 ui = ctx->ui;
848 ui->replace_result = REPLACE_YES;
850 ui->op_dlg = dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, dialog_colors,
851 file_ui_op_dlg_callback, NULL, NULL, op_names[ctx->operation]);
852 w = WIDGET (ui->op_dlg);
853 g = GROUP (ui->op_dlg);
855 if (dialog_type != FILEGUI_DIALOG_DELETE_ITEM)
857 ui->showing_eta = with_eta && ctx->progress_totals_computed;
858 ui->showing_bps = with_eta;
860 ui->src_file_label = label_new (y++, x, NULL);
861 group_add_widget (g, ui->src_file_label);
863 ui->src_file = label_new (y++, x, NULL);
864 group_add_widget (g, ui->src_file);
866 ui->tgt_file_label = label_new (y++, x, NULL);
867 group_add_widget (g, ui->tgt_file_label);
869 ui->tgt_file = label_new (y++, x, NULL);
870 group_add_widget (g, ui->tgt_file);
872 ui->progress_file_gauge = gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
873 if (!classic_progressbar && (current_panel == right_panel))
874 ui->progress_file_gauge->from_left_to_right = FALSE;
875 group_add_widget_autopos (g, ui->progress_file_gauge, WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
877 ui->progress_file_label = label_new (y++, x, NULL);
878 group_add_widget (g, ui->progress_file_label);
880 if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
882 ui->total_bytes_label = hline_new (y++, -1, -1);
883 group_add_widget (g, ui->total_bytes_label);
885 if (ctx->progress_totals_computed)
887 ui->progress_total_gauge =
888 gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
889 if (!classic_progressbar && (current_panel == right_panel))
890 ui->progress_total_gauge->from_left_to_right = FALSE;
891 group_add_widget_autopos (g, ui->progress_total_gauge,
892 WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
895 ui->total_files_processed_label = label_new (y++, x, NULL);
896 group_add_widget (g, ui->total_files_processed_label);
898 ui->time_label = label_new (y++, x, NULL);
899 group_add_widget (g, ui->time_label);
902 else
904 ui->src_file = label_new (y++, x, NULL);
905 group_add_widget (g, ui->src_file);
907 ui->total_files_processed_label = label_new (y++, x, NULL);
908 group_add_widget (g, ui->total_files_processed_label);
911 group_add_widget (g, hline_new (y++, -1, -1));
913 progress_buttons[0].w = WIDGET (button_new (y, 0, progress_buttons[0].action,
914 progress_buttons[0].flags, progress_buttons[0].text,
915 progress_button_callback));
916 if (progress_buttons[0].len == -1)
917 progress_buttons[0].len = button_get_len (BUTTON (progress_buttons[0].w));
919 progress_buttons[1].w = WIDGET (button_new (y, 0, progress_buttons[1].action,
920 progress_buttons[1].flags, progress_buttons[1].text,
921 progress_button_callback));
922 if (progress_buttons[1].len == -1)
923 progress_buttons[1].len = button_get_len (BUTTON (progress_buttons[1].w));
925 if (progress_buttons[2].len == -1)
927 /* create and destroy button to get it length */
928 progress_buttons[2].w = WIDGET (button_new (y, 0, progress_buttons[2].action,
929 progress_buttons[2].flags,
930 progress_buttons[2].text,
931 progress_button_callback));
932 progress_buttons[2].len = button_get_len (BUTTON (progress_buttons[2].w));
933 widget_destroy (progress_buttons[2].w);
935 progress_buttons[2].w = progress_buttons[1].w;
937 progress_buttons[3].w = WIDGET (button_new (y, 0, progress_buttons[3].action,
938 progress_buttons[3].flags, progress_buttons[3].text,
939 progress_button_callback));
940 if (progress_buttons[3].len == -1)
941 progress_buttons[3].len = button_get_len (BUTTON (progress_buttons[3].w));
943 group_add_widget (g, progress_buttons[0].w);
944 group_add_widget (g, progress_buttons[1].w);
945 group_add_widget (g, progress_buttons[3].w);
947 buttons_width = 2 +
948 progress_buttons[0].len + MAX (progress_buttons[1].len, progress_buttons[2].len) +
949 progress_buttons[3].len;
951 /* adjust dialog sizes */
952 r = w->rect;
953 r.lines = y + 3;
954 r.cols = MAX (COLS * 2 / 3, buttons_width + 6);
955 widget_set_size_rect (w, &r);
957 place_progress_buttons (ui->op_dlg, FALSE);
959 widget_select (progress_buttons[0].w);
961 /* We will manage the dialog without any help, that's why
962 we have to call dlg_init */
963 dlg_init (ui->op_dlg);
966 /* --------------------------------------------------------------------------------------------- */
968 void
969 file_op_context_destroy_ui (file_op_context_t *ctx)
971 if (ctx != NULL && ctx->ui != NULL)
973 file_op_context_ui_t *ui = (file_op_context_ui_t *) ctx->ui;
975 dlg_run_done (ui->op_dlg);
976 widget_destroy (WIDGET (ui->op_dlg));
977 MC_PTR_FREE (ctx->ui);
981 /* --------------------------------------------------------------------------------------------- */
983 show progressbar for file
986 void
987 file_progress_show (file_op_context_t *ctx, off_t done, off_t total,
988 const char *stalled_msg, gboolean force_update)
990 file_op_context_ui_t *ui;
992 if (!verbose || ctx == NULL || ctx->ui == NULL)
993 return;
995 ui = ctx->ui;
997 if (total == 0)
999 gauge_show (ui->progress_file_gauge, FALSE);
1000 return;
1003 gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
1004 gauge_show (ui->progress_file_gauge, TRUE);
1006 if (!force_update)
1007 return;
1009 if (!ui->showing_eta || ctx->eta_secs <= 0.5)
1010 label_set_text (ui->progress_file_label, stalled_msg);
1011 else
1013 char buffer2[BUF_TINY];
1015 file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
1016 if (ctx->bps == 0)
1017 label_set_textv (ui->progress_file_label, "%s %s", buffer2, stalled_msg);
1018 else
1020 char buffer3[BUF_TINY];
1022 file_bps_prepare_for_show (buffer3, ctx->bps);
1023 label_set_textv (ui->progress_file_label, "%s (%s) %s", buffer2, buffer3, stalled_msg);
1029 /* --------------------------------------------------------------------------------------------- */
1031 void
1032 file_progress_show_count (file_op_context_t *ctx, size_t done, size_t total)
1034 file_op_context_ui_t *ui;
1036 if (ctx == NULL || ctx->ui == NULL)
1037 return;
1039 ui = ctx->ui;
1041 if (ui->total_files_processed_label == NULL)
1042 return;
1044 if (ctx->progress_totals_computed)
1045 label_set_textv (ui->total_files_processed_label, _("Files processed: %zu / %zu"), done,
1046 total);
1047 else
1048 label_set_textv (ui->total_files_processed_label, _("Files processed: %zu"), done);
1051 /* --------------------------------------------------------------------------------------------- */
1053 void
1054 file_progress_show_total (file_op_total_context_t *tctx, file_op_context_t *ctx,
1055 uintmax_t copied_bytes, gboolean show_summary)
1057 char buffer2[BUF_TINY];
1058 char buffer3[BUF_TINY];
1059 file_op_context_ui_t *ui;
1061 if (ctx == NULL || ctx->ui == NULL)
1062 return;
1064 ui = ctx->ui;
1066 if (ui->progress_total_gauge != NULL)
1068 if (ctx->progress_bytes == 0)
1069 gauge_show (ui->progress_total_gauge, FALSE);
1070 else
1072 gauge_set_value (ui->progress_total_gauge, 1024,
1073 (int) (1024 * copied_bytes / ctx->progress_bytes));
1074 gauge_show (ui->progress_total_gauge, TRUE);
1078 if (!show_summary && tctx->bps == 0)
1079 return;
1081 if (ui->time_label != NULL)
1083 gint64 tv_current;
1084 char buffer4[BUF_TINY];
1086 tv_current = g_get_monotonic_time ();
1087 file_frmt_time (buffer2, (tv_current - tctx->transfer_start) / G_USEC_PER_SEC);
1089 if (ctx->progress_totals_computed)
1091 file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
1092 if (tctx->bps == 0)
1093 label_set_textv (ui->time_label, _("Time: %s %s"), buffer2, buffer3);
1094 else
1096 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1097 label_set_textv (ui->time_label, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
1100 else
1102 if (tctx->bps == 0)
1103 label_set_textv (ui->time_label, _("Time: %s"), buffer2);
1104 else
1106 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1107 label_set_textv (ui->time_label, _("Time: %s (%s)"), buffer2, buffer4);
1112 if (ui->total_bytes_label != NULL)
1114 size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
1116 if (!ctx->progress_totals_computed)
1117 hline_set_textv (ui->total_bytes_label, _(" Total: %s "), buffer2);
1118 else
1120 size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
1121 hline_set_textv (ui->total_bytes_label, _(" Total: %s / %s "), buffer2, buffer3);
1126 /* }}} */
1128 /* --------------------------------------------------------------------------------------------- */
1130 void
1131 file_progress_show_source (file_op_context_t *ctx, const vfs_path_t *vpath)
1133 file_op_context_ui_t *ui;
1135 if (ctx == NULL || ctx->ui == NULL)
1136 return;
1138 ui = ctx->ui;
1140 if (vpath != NULL)
1142 label_set_text (ui->src_file_label, _("Source"));
1143 label_set_text (ui->src_file, truncFileStringSecure (ui->op_dlg, vfs_path_as_str (vpath)));
1145 else
1147 label_set_text (ui->src_file_label, NULL);
1148 label_set_text (ui->src_file, NULL);
1152 /* --------------------------------------------------------------------------------------------- */
1154 void
1155 file_progress_show_target (file_op_context_t *ctx, const vfs_path_t *vpath)
1157 file_op_context_ui_t *ui;
1159 if (ctx == NULL || ctx->ui == NULL)
1160 return;
1162 ui = ctx->ui;
1164 if (vpath != NULL)
1166 label_set_text (ui->tgt_file_label, _("Target"));
1167 label_set_text (ui->tgt_file, truncFileStringSecure (ui->op_dlg, vfs_path_as_str (vpath)));
1169 else
1171 label_set_text (ui->tgt_file_label, NULL);
1172 label_set_text (ui->tgt_file, NULL);
1176 /* --------------------------------------------------------------------------------------------- */
1178 gboolean
1179 file_progress_show_deleting (file_op_context_t *ctx, const vfs_path_t *vpath, size_t *count)
1181 static gint64 timestamp = 0;
1182 /* update with 25 FPS rate */
1183 static const gint64 delay = G_USEC_PER_SEC / 25;
1185 gboolean ret;
1187 if (ctx == NULL || ctx->ui == NULL)
1188 return FALSE;
1190 ret = mc_time_elapsed (&timestamp, delay);
1192 if (ret)
1194 file_op_context_ui_t *ui;
1195 const char *s;
1197 ui = ctx->ui;
1199 if (ui->src_file_label != NULL)
1200 label_set_text (ui->src_file_label, _("Deleting"));
1202 s = vfs_path_as_str (vpath);
1203 label_set_text (ui->src_file, truncFileStringSecure (ui->op_dlg, s));
1206 if (count != NULL)
1207 (*count)++;
1209 return ret;
1212 /* --------------------------------------------------------------------------------------------- */
1214 FileProgressStatus
1215 file_progress_real_query_replace (file_op_context_t *ctx, enum OperationMode mode,
1216 const char *src, struct stat *src_stat,
1217 const char *dst, struct stat *dst_stat)
1219 file_op_context_ui_t *ui;
1220 FileProgressStatus replace_with_zero;
1222 if (ctx == NULL || ctx->ui == NULL)
1223 return FILE_CONT;
1225 ui = ctx->ui;
1227 if (ui->replace_result == REPLACE_YES || ui->replace_result == REPLACE_NO
1228 || ui->replace_result == REPLACE_APPEND)
1230 ui->src_filename = src;
1231 ui->src_stat = src_stat;
1232 ui->tgt_filename = dst;
1233 ui->dst_stat = dst_stat;
1234 ui->replace_result = overwrite_query_dialog (ctx, mode);
1237 replace_with_zero = (src_stat->st_size == 0
1238 && ui->dont_overwrite_with_zero) ? FILE_SKIP : FILE_CONT;
1240 switch (ui->replace_result)
1242 case REPLACE_OLDER:
1243 do_refresh ();
1244 if (src_stat->st_mtime > dst_stat->st_mtime)
1245 return replace_with_zero;
1246 else
1247 return FILE_SKIP;
1249 case REPLACE_SIZE:
1250 do_refresh ();
1251 if (src_stat->st_size == dst_stat->st_size)
1252 return FILE_SKIP;
1253 else
1254 return replace_with_zero;
1256 case REPLACE_SMALLER:
1257 do_refresh ();
1258 if (src_stat->st_size > dst_stat->st_size)
1259 return FILE_CONT;
1260 else
1261 return FILE_SKIP;
1263 case REPLACE_ALL:
1264 do_refresh ();
1265 return replace_with_zero;
1267 case REPLACE_REGET:
1268 /* Careful: we fall through and set do_append */
1269 ctx->do_reget = dst_stat->st_size;
1270 MC_FALLTHROUGH;
1272 case REPLACE_APPEND:
1273 ctx->do_append = TRUE;
1274 MC_FALLTHROUGH;
1276 case REPLACE_YES:
1277 do_refresh ();
1278 return FILE_CONT;
1280 case REPLACE_NO:
1281 case REPLACE_NONE:
1282 do_refresh ();
1283 return FILE_SKIP;
1285 case REPLACE_ABORT:
1286 default:
1287 return FILE_ABORT;
1291 /* --------------------------------------------------------------------------------------------- */
1293 char *
1294 file_mask_dialog (file_op_context_t *ctx, gboolean only_one, const char *format, const void *text,
1295 const char *def_text, gboolean *do_bg)
1297 gboolean preserve;
1298 size_t fmd_xlen;
1299 vfs_path_t *vpath;
1300 gboolean source_easy_patterns = easy_patterns;
1301 char fmd_buf[BUF_MEDIUM];
1302 char *dest_dir = NULL;
1303 char *tmp;
1304 char *def_text_secure;
1306 if (ctx == NULL)
1307 return NULL;
1309 /* unselect checkbox if target filesystem doesn't support attributes */
1310 preserve = copymove_persistent_attr && filegui__check_attrs_on_fs (def_text);
1312 ctx->stable_symlinks = FALSE;
1313 *do_bg = FALSE;
1315 /* filter out a possible password from def_text */
1316 vpath = vfs_path_from_str_flags (def_text, only_one ? VPF_NO_CANON : VPF_NONE);
1317 tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1318 vfs_path_free (vpath, TRUE);
1320 if (source_easy_patterns)
1321 def_text_secure = str_glob_escape (tmp);
1322 else
1323 def_text_secure = str_regex_escape (tmp);
1324 g_free (tmp);
1326 if (only_one)
1328 int format_len, text_len;
1329 int max_len;
1331 format_len = str_term_width1 (format);
1332 text_len = str_term_width1 (text);
1333 max_len = COLS - 2 - 6;
1335 if (format_len + text_len <= max_len)
1337 fmd_xlen = format_len + text_len + 6;
1338 fmd_xlen = MAX (fmd_xlen, 68);
1340 else
1342 text = str_trunc ((const char *) text, max_len - format_len);
1343 fmd_xlen = max_len + 6;
1346 g_snprintf (fmd_buf, sizeof (fmd_buf), format, (const char *) text);
1348 else
1350 fmd_xlen = COLS * 2 / 3;
1351 fmd_xlen = MAX (fmd_xlen, 68);
1352 g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1356 char *source_mask = NULL;
1357 char *orig_mask;
1358 int val;
1359 struct stat buf;
1361 quick_widget_t quick_widgets[] = {
1362 /* *INDENT-OFF* */
1363 QUICK_LABELED_INPUT (fmd_buf, input_label_above, easy_patterns ? "*" : "^(.*)$",
1364 "input-def", &source_mask, NULL, FALSE, FALSE,
1365 INPUT_COMPLETE_FILENAMES),
1366 QUICK_START_COLUMNS,
1367 QUICK_SEPARATOR (FALSE),
1368 QUICK_NEXT_COLUMN,
1369 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns, NULL),
1370 QUICK_STOP_COLUMNS,
1371 QUICK_LABELED_INPUT (N_("to:"), input_label_above, def_text_secure, "input2", &dest_dir,
1372 NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1373 QUICK_SEPARATOR (TRUE),
1374 QUICK_START_COLUMNS,
1375 QUICK_CHECKBOX (N_("Follow &links"), &ctx->follow_links, NULL),
1376 QUICK_CHECKBOX (N_("Preserve &attributes"), &preserve, NULL),
1377 QUICK_NEXT_COLUMN,
1378 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx->dive_into_subdirs, NULL),
1379 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx->stable_symlinks, NULL),
1380 QUICK_STOP_COLUMNS,
1381 QUICK_START_BUTTONS (TRUE, TRUE),
1382 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
1383 #ifdef ENABLE_BACKGROUND
1384 QUICK_BUTTON (N_("&Background"), B_USER, NULL, NULL),
1385 #endif /* ENABLE_BACKGROUND */
1386 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1387 QUICK_END
1388 /* *INDENT-ON* */
1391 WRect r = { -1, -1, 0, fmd_xlen };
1393 quick_dialog_t qdlg = {
1394 r, op_names[ctx->operation], "[Mask Copy/Rename]",
1395 quick_widgets, NULL, NULL
1398 while (TRUE)
1400 val = quick_dialog_skip (&qdlg, 4);
1402 if (val == B_CANCEL)
1404 g_free (def_text_secure);
1405 return NULL;
1408 ctx->stat_func = ctx->follow_links ? mc_stat : mc_lstat;
1410 if (preserve)
1412 ctx->preserve = TRUE;
1413 ctx->umask_kill = (mode_t) (~0);
1414 ctx->preserve_uidgid = (geteuid () == 0);
1416 else
1418 mode_t i2;
1420 ctx->preserve = ctx->preserve_uidgid = FALSE;
1421 i2 = umask (0);
1422 umask (i2);
1423 ctx->umask_kill = i2 ^ ((mode_t) (~0));
1426 if (*dest_dir == '\0')
1428 g_free (def_text_secure);
1429 g_free (source_mask);
1430 g_free (dest_dir);
1431 return NULL;
1434 ctx->search_handle = mc_search_new (source_mask, NULL);
1435 if (ctx->search_handle != NULL)
1436 break;
1438 message (D_ERROR, MSG_ERROR, _("Invalid source pattern '%s'"), source_mask);
1439 MC_PTR_FREE (dest_dir);
1440 MC_PTR_FREE (source_mask);
1443 g_free (def_text_secure);
1444 g_free (source_mask);
1446 ctx->search_handle->is_case_sensitive = TRUE;
1447 if (source_easy_patterns)
1448 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1449 else
1450 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1452 tmp = dest_dir;
1453 dest_dir = tilde_expand (tmp);
1454 g_free (tmp);
1455 vpath = vfs_path_from_str (dest_dir);
1457 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1458 if (ctx->dest_mask == NULL)
1459 ctx->dest_mask = dest_dir;
1460 else
1461 ctx->dest_mask++;
1463 orig_mask = ctx->dest_mask;
1465 if (*ctx->dest_mask == '\0'
1466 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1467 && (!only_one
1468 || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1469 || (ctx->dive_into_subdirs
1470 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1471 || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1472 ctx->dest_mask = g_strdup ("\\0");
1473 else
1475 ctx->dest_mask = g_strdup (ctx->dest_mask);
1476 *orig_mask = '\0';
1479 if (*dest_dir == '\0')
1481 g_free (dest_dir);
1482 dest_dir = g_strdup ("./");
1485 vfs_path_free (vpath, TRUE);
1487 if (val == B_USER)
1488 *do_bg = TRUE;
1491 return dest_dir;
1494 /* --------------------------------------------------------------------------------------------- */