This source file includes following definitions.
- statfs
- statvfs_works
- filegui__check_attrs_on_fs
- file_frmt_time
- file_eta_prepare_for_show
- file_bps_prepare_for_show
- file_ui_op_dlg_callback
- overwrite_query_dialog
- is_wildcarded
- place_progress_buttons
- progress_button_callback
- check_progress_buttons
- file_op_context_create_ui
- file_op_context_destroy_ui
- file_progress_show
- file_progress_show_count
- file_progress_show_total
- file_progress_show_source
- file_progress_show_target
- file_progress_show_deleting
- file_progress_real_query_replace
- file_mask_dialog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 #include <config.h>
54
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
62
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>
69
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
76
77
78
79 #include <sys/param.h>
80 #include <sys/mount.h>
81 #elif defined HAVE_OS_H
82 #include <fs_info.h>
83 #endif
84
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
92
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
99
100 #else
101 #define STATFS statfs
102 #define STRUCT_STATVFS struct statfs
103 #ifdef HAVE_OS_H
104
105
106
107 static int
108 statfs (char const *filename, struct fs_info *buf)
109 {
110 dev_t device;
111
112 device = dev_for_path (filename);
113
114 if (device < 0)
115 {
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;
121 }
122
123 return fs_stat_dev (device, buf);
124 }
125
126 #define STRUCT_STATVFS struct fs_info
127 #else
128 #define STRUCT_STATVFS struct statfs
129 #endif
130 #endif
131
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
138 #define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
139 #endif
140 #endif
141
142 #include <unistd.h>
143
144 #include "lib/global.h"
145
146 #include "lib/tty/key.h"
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"
153 #include "lib/util.h"
154 #include "lib/widget.h"
155
156 #include "src/setup.h"
157
158 #include "filemanager.h"
159 #include "fileopctx.h"
160
161 #include "filegui.h"
162
163
164
165
166
167 gboolean classic_progressbar = TRUE;
168
169
170
171 #define truncFileString(dlg, s) str_trunc (s, WIDGET (dlg)->rect.cols - 10)
172 #define truncFileStringSecure(dlg, s) path_trunc (s, WIDGET (dlg)->rect.cols - 10)
173
174
175
176
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
187
188
189 typedef enum
190 {
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;
202
203
204
205
206 typedef struct
207 {
208
209 gboolean showing_eta;
210 gboolean showing_bps;
211
212
213 WDialog *op_dlg;
214
215 WLabel *src_file_label;
216 WLabel *src_file;
217
218 WLabel *tgt_file_label;
219 WLabel *tgt_file;
220
221 WGauge *progress_file_gauge;
222 WLabel *progress_file_label;
223
224 WGauge *progress_total_gauge;
225
226 WLabel *total_files_processed_label;
227 WLabel *time_label;
228 WHLine *total_bytes_label;
229
230
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;
236
237 struct stat *src_stat, *dst_stat;
238 } file_op_context_ui_t;
239
240
241
242
243
244 static struct
245 {
246 Widget *w;
247 FileProgressStatus action;
248 const char *text;
249 button_flags_t flags;
250 int len;
251 } progress_buttons[] =
252 {
253
254 { NULL, FILE_SKIP, N_("&Skip"), NORMAL_BUTTON, -1 },
255 { NULL, FILE_SUSPEND, N_("S&uspend"), NORMAL_BUTTON, -1 },
256 { NULL, FILE_SUSPEND, N_("Con&tinue"), NORMAL_BUTTON, -1 },
257 { NULL, FILE_ABORT, N_("&Abort"), NORMAL_BUTTON, -1 }
258
259 };
260
261
262
263
264
265
266
267
268
269
270 #if USE_STATVFS && ! (! defined STAT_STATVFS && defined STAT_STATVFS64)
271 static int
272 statvfs_works (void)
273 {
274 #if ! (defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__))
275 return 1;
276 #else
277 static int statvfs_works_cache = -1;
278 struct utsname name;
279
280 if (statvfs_works_cache < 0)
281 statvfs_works_cache = (uname (&name) == 0 && 0 <= str_verscmp (name.release, "2.6.36"));
282 return statvfs_works_cache;
283 #endif
284 }
285 #endif
286
287
288
289 static gboolean
290 filegui__check_attrs_on_fs (const char *fs_path)
291 {
292 STRUCT_STATVFS stfs;
293
294 #if USE_STATVFS && defined(STAT_STATVFS)
295 if (statvfs_works () && statvfs (fs_path, &stfs) != 0)
296 return TRUE;
297 #else
298 if (STATFS (fs_path, &stfs) != 0)
299 return TRUE;
300 #endif
301
302 #if (USE_STATVFS && defined(HAVE_STRUCT_STATVFS_F_TYPE)) || \
303 (!USE_STATVFS && defined(HAVE_STRUCT_STATFS_F_TYPE))
304 switch ((filegui_nonattrs_fs_t) stfs.f_type)
305 {
306 case MSDOS_SUPER_MAGIC:
307 case NTFS_SB_MAGIC:
308 case PROC_SUPER_MAGIC:
309 case SMB_SUPER_MAGIC:
310 case NCP_SUPER_MAGIC:
311 case USBDEVICE_SUPER_MAGIC:
312 return FALSE;
313 default:
314 break;
315 }
316 #elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
317 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdos") == 0
318 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "msdosfs") == 0
319 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
320 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "procfs") == 0
321 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
322 || strstr (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fusefs") != NULL)
323 return FALSE;
324 #elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
325 if (strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "pcfs") == 0
326 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "ntfs") == 0
327 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "proc") == 0
328 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "smbfs") == 0
329 || strcmp (stfs.STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME, "fuse") == 0)
330 return FALSE;
331 #endif
332
333 return TRUE;
334 }
335
336
337
338 static void
339 file_frmt_time (char *buffer, double eta_secs)
340 {
341 int eta_hours, eta_mins, eta_s;
342
343 eta_hours = (int) (eta_secs / (60 * 60));
344 eta_mins = (int) ((eta_secs - (eta_hours * 60 * 60)) / 60);
345 eta_s = (int) (eta_secs - (eta_hours * 60 * 60 + eta_mins * 60));
346 g_snprintf (buffer, BUF_TINY, _("%d:%02d:%02d"), eta_hours, eta_mins, eta_s);
347 }
348
349
350
351 static void
352 file_eta_prepare_for_show (char *buffer, double eta_secs, gboolean always_show)
353 {
354 char _fmt_buff[BUF_TINY];
355
356 if (eta_secs <= 0.5 && !always_show)
357 {
358 *buffer = '\0';
359 return;
360 }
361
362 if (eta_secs <= 0.5)
363 eta_secs = 1;
364 file_frmt_time (_fmt_buff, eta_secs);
365 g_snprintf (buffer, BUF_TINY, _("ETA %s"), _fmt_buff);
366 }
367
368
369
370 static void
371 file_bps_prepare_for_show (char *buffer, long bps)
372 {
373 if (bps > 1024 * 1024)
374 g_snprintf (buffer, BUF_TINY, _("%.2f MB/s"), bps / (1024 * 1024.0));
375 else if (bps > 1024)
376 g_snprintf (buffer, BUF_TINY, _("%.2f KB/s"), bps / 1024.0);
377 else if (bps > 1)
378 g_snprintf (buffer, BUF_TINY, _("%ld B/s"), bps);
379 else
380 *buffer = '\0';
381 }
382
383
384
385 static cb_ret_t
386 file_ui_op_dlg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
387 {
388 switch (msg)
389 {
390 case MSG_ACTION:
391
392 if (parm == CK_Cancel)
393 {
394 DIALOG (w)->ret_value = FILE_ABORT;
395 return MSG_HANDLED;
396 }
397 return MSG_NOT_HANDLED;
398
399 default:
400 return dlg_default_callback (w, sender, msg, parm, data);
401 }
402 }
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425 static replace_action_t
426 overwrite_query_dialog (file_op_context_t * ctx, enum OperationMode mode)
427 {
428 #define W(i) dlg_widgets[i].widget
429 #define WX(i) W(i)->rect.x
430 #define WY(i) W(i)->rect.y
431 #define WCOLS(i) W(i)->rect.cols
432
433 #define NEW_LABEL(i, text) \
434 W(i) = WIDGET (label_new (dlg_widgets[i].y, dlg_widgets[i].x, text))
435
436 #define ADD_LABEL(i) \
437 group_add_widget_autopos (g, W(i), dlg_widgets[i].pos_flags, \
438 g->current != NULL ? g->current->data : NULL)
439
440 #define NEW_BUTTON(i) \
441 W(i) = WIDGET (button_new (dlg_widgets[i].y, dlg_widgets[i].x, \
442 dlg_widgets[i].value, NORMAL_BUTTON, dlg_widgets[i].text, NULL))
443
444 #define ADD_BUTTON(i) \
445 group_add_widget_autopos (g, W(i), dlg_widgets[i].pos_flags, g->current->data)
446
447
448 const int dlg_height = 17;
449 int dlg_width = 60;
450
451 struct
452 {
453 Widget *widget;
454 const char *text;
455 int y;
456 int x;
457 widget_pos_flags_t pos_flags;
458 int value;
459 } dlg_widgets[] =
460 {
461
462
463 { NULL, N_("New :"), 2, 3, WPOS_KEEP_DEFAULT, 0 },
464
465 { NULL, NULL, 2, 14, WPOS_KEEP_DEFAULT, 0 },
466
467 { NULL, NULL, 3, 3, WPOS_KEEP_DEFAULT, 0 },
468
469 { NULL, NULL, 3, 43, WPOS_KEEP_TOP | WPOS_KEEP_RIGHT, 0 },
470
471 { NULL, N_("Existing:"), 4, 3, WPOS_KEEP_DEFAULT, 0 },
472
473 { NULL, NULL, 4, 14, WPOS_KEEP_DEFAULT, 0 },
474
475 { NULL, NULL, 5, 3, WPOS_KEEP_DEFAULT, 0 },
476
477 { NULL, NULL, 5, 43, WPOS_KEEP_TOP | WPOS_KEEP_RIGHT, 0 },
478
479
480 { NULL, N_("Overwrite this file?"), 7, 21, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
481
482 { NULL, N_("&Yes"), 8, 14, WPOS_KEEP_DEFAULT, REPLACE_YES },
483
484 { NULL, N_("&No"), 8, 22, WPOS_KEEP_DEFAULT, REPLACE_NO },
485
486 { NULL, N_("A&ppend"), 8, 29, WPOS_KEEP_DEFAULT, REPLACE_APPEND },
487
488 { NULL, N_("&Reget"), 8, 40, WPOS_KEEP_DEFAULT, REPLACE_REGET },
489
490
491 { NULL, N_("Overwrite all files?"), 10, 21, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 0 },
492
493 { NULL, N_("Don't overwrite with &zero length file"), 11, 3, WPOS_KEEP_DEFAULT, 0 },
494
495 { NULL, N_("A&ll"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_ALL },
496
497 { NULL, N_("&Older"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_OLDER },
498
499 { NULL, N_("Non&e"), 12, 12, WPOS_KEEP_DEFAULT, REPLACE_NONE },
500
501 { NULL, N_("S&maller"), 12, 25, WPOS_KEEP_DEFAULT, REPLACE_SMALLER },
502
503 { NULL, N_("&Size differs"), 12, 40, WPOS_KEEP_DEFAULT, REPLACE_SIZE },
504
505
506 { NULL, N_("&Abort"), 14, 27, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, REPLACE_ABORT }
507
508 };
509
510 const int gap = 1;
511
512 file_op_context_ui_t *ui = ctx->ui;
513 Widget *wd;
514 WGroup *g;
515 const char *title;
516
517 vfs_path_t *p;
518 char *s1;
519 const char *cs1;
520 char s2[BUF_SMALL];
521 int w, bw1, bw2;
522 unsigned short i;
523
524 gboolean do_append = FALSE, do_reget = FALSE;
525 unsigned long yes_id, no_id;
526 int result;
527
528 if (mode == Foreground)
529 title = _("File exists");
530 else
531 title = _("Background process: File exists");
532
533 #ifdef ENABLE_NLS
534 {
535 const unsigned short num = G_N_ELEMENTS (dlg_widgets);
536
537 for (i = 0; i < num; i++)
538 if (dlg_widgets[i].text != NULL)
539 dlg_widgets[i].text = _(dlg_widgets[i].text);
540 }
541 #endif
542
543
544
545 NEW_LABEL (0, dlg_widgets[0].text);
546
547 p = vfs_path_from_str (ui->src_filename);
548 s1 = vfs_path_to_str_flags (p, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
549 NEW_LABEL (1, s1);
550 vfs_path_free (p, TRUE);
551 g_free (s1);
552
553 size_trunc_len (s2, sizeof (s2), ui->src_stat->st_size, 0, panels_options.kilobyte_si);
554 NEW_LABEL (2, s2);
555
556 cs1 = file_date (ui->src_stat->st_mtime);
557 NEW_LABEL (3, cs1);
558
559
560 NEW_LABEL (4, dlg_widgets[4].text);
561
562 p = vfs_path_from_str (ui->tgt_filename);
563 s1 = vfs_path_to_str_flags (p, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
564 NEW_LABEL (5, s1);
565 vfs_path_free (p, TRUE);
566 g_free (s1);
567
568 size_trunc_len (s2, sizeof (s2), ui->dst_stat->st_size, 0, panels_options.kilobyte_si);
569 NEW_LABEL (6, s2);
570
571 cs1 = file_date (ui->dst_stat->st_mtime);
572 NEW_LABEL (7, cs1);
573
574
575 do_append = !S_ISDIR (ui->dst_stat->st_mode);
576 do_reget = do_append && ctx->operation == OP_COPY && ui->dst_stat->st_size != 0
577 && ui->src_stat->st_size > ui->dst_stat->st_size;
578
579 NEW_LABEL (8, dlg_widgets[8].text);
580 NEW_BUTTON (9);
581 NEW_BUTTON (10);
582 if (do_append)
583 NEW_BUTTON (11);
584 if (do_reget)
585 NEW_BUTTON (12);
586
587 NEW_LABEL (13, dlg_widgets[13].text);
588 dlg_widgets[14].widget =
589 WIDGET (check_new (dlg_widgets[14].y, dlg_widgets[14].x, FALSE, dlg_widgets[14].text));
590 for (i = 15; i <= 20; i++)
591 NEW_BUTTON (i);
592
593
594 dlg_width -= 2 * (2 + gap);
595
596
597 bw1 = WCOLS (9) + gap + WCOLS (10);
598 if (do_append)
599 bw1 += gap + WCOLS (11);
600 if (do_reget)
601 bw1 += gap + WCOLS (12);
602 dlg_width = MAX (dlg_width, bw1);
603
604 bw2 = WCOLS (15);
605 for (i = 16; i <= 19; i++)
606 bw2 += gap + WCOLS (i);
607 dlg_width = MAX (dlg_width, bw2);
608
609 dlg_width = MAX (dlg_width, WCOLS (8));
610 dlg_width = MAX (dlg_width, WCOLS (13));
611 dlg_width = MAX (dlg_width, WCOLS (14));
612
613
614 w = WCOLS (0) + gap + WCOLS (1);
615 if (w > dlg_width)
616 {
617 WLabel *l = LABEL (W (1));
618
619 w = dlg_width - gap - WCOLS (0);
620 label_set_text (l, str_trunc (l->text, w));
621 }
622
623 w = WCOLS (4) + gap + WCOLS (5);
624 if (w > dlg_width)
625 {
626 WLabel *l = LABEL (W (5));
627
628 w = dlg_width - gap - WCOLS (4);
629 label_set_text (l, str_trunc (l->text, w));
630 }
631
632
633 dlg_width += 2 * (2 + gap);
634
635 WX (1) = WX (0) + WCOLS (0) + gap;
636 WX (5) = WX (4) + WCOLS (4) + gap;
637
638
639 WX (2) = dlg_width / 2 - WCOLS (2);
640 WX (6) = dlg_width / 2 - WCOLS (6);
641
642 w = dlg_width - (2 + gap);
643
644
645 WX (3) = w - WCOLS (3);
646 WX (7) = w - WCOLS (7);
647
648
649 WX (9) = dlg_width / 2 - bw1 / 2;
650 WX (10) = WX (9) + WCOLS (9) + gap;
651 if (do_append)
652 WX (11) = WX (10) + WCOLS (10) + gap;
653 if (do_reget)
654 WX (12) = WX (11) + WCOLS (11) + gap;
655
656 WX (15) = dlg_width / 2 - bw2 / 2;
657 for (i = 16; i <= 19; i++)
658 WX (i) = WX (i - 1) + WCOLS (i - 1) + gap;
659
660
661 ui->replace_dlg =
662 dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, alarm_colors, NULL, NULL,
663 "[Replace]", title);
664 wd = WIDGET (ui->replace_dlg);
665 g = GROUP (ui->replace_dlg);
666
667
668 for (i = 0; i <= 7; i++)
669 ADD_LABEL (i);
670 group_add_widget (g, hline_new (WY (7) - wd->rect.y + 1, -1, -1));
671
672
673 ADD_LABEL (8);
674 yes_id = ADD_BUTTON (9);
675 no_id = ADD_BUTTON (10);
676 if (do_append)
677 ADD_BUTTON (11);
678 if (do_reget)
679 ADD_BUTTON (12);
680 group_add_widget (g, hline_new (WY (10) - wd->rect.y + 1, -1, -1));
681
682
683 ADD_LABEL (13);
684 group_add_widget (g, dlg_widgets[14].widget);
685 for (i = 15; i <= 19; i++)
686 ADD_BUTTON (i);
687 group_add_widget (g, hline_new (WY (19) - wd->rect.y + 1, -1, -1));
688
689 ADD_BUTTON (20);
690
691 group_select_widget_by_id (g, safe_overwrite ? no_id : yes_id);
692
693 result = dlg_run (ui->replace_dlg);
694
695 if (result != B_CANCEL)
696 ui->dont_overwrite_with_zero = CHECK (dlg_widgets[14].widget)->state;
697
698 widget_destroy (wd);
699
700 return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result;
701
702 #undef ADD_BUTTON
703 #undef NEW_BUTTON
704 #undef ADD_LABEL
705 #undef NEW_LABEL
706 #undef WCOLS
707 #undef WX
708 #undef W
709 }
710
711
712
713 static gboolean
714 is_wildcarded (const char *p)
715 {
716 gboolean escaped = FALSE;
717
718 for (; *p != '\0'; p++)
719 {
720 if (*p == '\\')
721 {
722 if (p[1] >= '1' && p[1] <= '9' && !escaped)
723 return TRUE;
724 escaped = !escaped;
725 }
726 else
727 {
728 if ((*p == '*' || *p == '?') && !escaped)
729 return TRUE;
730 escaped = FALSE;
731 }
732 }
733 return FALSE;
734 }
735
736
737
738 static void
739 place_progress_buttons (WDialog * h, gboolean suspended)
740 {
741 const size_t i = suspended ? 2 : 1;
742 Widget *w = WIDGET (h);
743 int buttons_width;
744
745 buttons_width = 2 + progress_buttons[0].len + progress_buttons[3].len;
746 buttons_width += progress_buttons[i].len;
747 button_set_text (BUTTON (progress_buttons[i].w), progress_buttons[i].text);
748
749 progress_buttons[0].w->rect.x = w->rect.x + (w->rect.cols - buttons_width) / 2;
750 progress_buttons[i].w->rect.x = progress_buttons[0].w->rect.x + progress_buttons[0].len + 1;
751 progress_buttons[3].w->rect.x = progress_buttons[i].w->rect.x + progress_buttons[i].len + 1;
752 }
753
754
755
756 static int
757 progress_button_callback (WButton * button, int action)
758 {
759 (void) button;
760 (void) action;
761
762
763 return 0;
764 }
765
766
767
768
769
770 FileProgressStatus
771 check_progress_buttons (file_op_context_t * ctx)
772 {
773 int c;
774 Gpm_Event event;
775 file_op_context_ui_t *ui;
776
777 if (ctx == NULL || ctx->ui == NULL)
778 return FILE_CONT;
779
780 ui = ctx->ui;
781
782 get_event:
783 event.x = -1;
784 c = tty_get_event (&event, FALSE, ctx->suspended);
785 if (c == EV_NONE)
786 return FILE_CONT;
787
788
789 ui->op_dlg->ret_value = FILE_CONT;
790
791 dlg_process_event (ui->op_dlg, c, &event);
792 switch (ui->op_dlg->ret_value)
793 {
794 case FILE_SKIP:
795 if (ctx->suspended)
796 {
797
798 place_progress_buttons (ui->op_dlg, FALSE);
799 widget_draw (WIDGET (ui->op_dlg));
800 }
801 ctx->suspended = FALSE;
802 return FILE_SKIP;
803 case B_CANCEL:
804 case FILE_ABORT:
805 ctx->suspended = FALSE;
806 return FILE_ABORT;
807 case FILE_SUSPEND:
808 ctx->suspended = !ctx->suspended;
809 place_progress_buttons (ui->op_dlg, ctx->suspended);
810 widget_draw (WIDGET (ui->op_dlg));
811 MC_FALLTHROUGH;
812 default:
813 if (ctx->suspended)
814 goto get_event;
815 return FILE_CONT;
816 }
817 }
818
819
820
821
822 void
823 file_op_context_create_ui (file_op_context_t * ctx, gboolean with_eta,
824 filegui_dialog_type_t dialog_type)
825 {
826 file_op_context_ui_t *ui;
827 Widget *w;
828 WGroup *g;
829 int buttons_width;
830 int dlg_width = 58, dlg_height = 17;
831 int y = 2, x = 3;
832 WRect r;
833
834 if (ctx == NULL || ctx->ui != NULL)
835 return;
836
837 #ifdef ENABLE_NLS
838 if (progress_buttons[0].len == -1)
839 {
840 size_t i;
841
842 for (i = 0; i < G_N_ELEMENTS (progress_buttons); i++)
843 progress_buttons[i].text = _(progress_buttons[i].text);
844 }
845 #endif
846
847 ctx->dialog_type = dialog_type;
848 ctx->recursive_result = RECURSIVE_YES;
849 ctx->ui = g_new0 (file_op_context_ui_t, 1);
850
851 ui = ctx->ui;
852 ui->replace_result = REPLACE_YES;
853
854 ui->op_dlg = dlg_create (TRUE, 0, 0, dlg_height, dlg_width, WPOS_CENTER, FALSE, dialog_colors,
855 file_ui_op_dlg_callback, NULL, NULL, op_names[ctx->operation]);
856 w = WIDGET (ui->op_dlg);
857 g = GROUP (ui->op_dlg);
858
859 if (dialog_type != FILEGUI_DIALOG_DELETE_ITEM)
860 {
861 ui->showing_eta = with_eta && ctx->progress_totals_computed;
862 ui->showing_bps = with_eta;
863
864 ui->src_file_label = label_new (y++, x, NULL);
865 group_add_widget (g, ui->src_file_label);
866
867 ui->src_file = label_new (y++, x, NULL);
868 group_add_widget (g, ui->src_file);
869
870 ui->tgt_file_label = label_new (y++, x, NULL);
871 group_add_widget (g, ui->tgt_file_label);
872
873 ui->tgt_file = label_new (y++, x, NULL);
874 group_add_widget (g, ui->tgt_file);
875
876 ui->progress_file_gauge = gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
877 if (!classic_progressbar && (current_panel == right_panel))
878 ui->progress_file_gauge->from_left_to_right = FALSE;
879 group_add_widget_autopos (g, ui->progress_file_gauge, WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
880
881 ui->progress_file_label = label_new (y++, x, NULL);
882 group_add_widget (g, ui->progress_file_label);
883
884 if (verbose && dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
885 {
886 ui->total_bytes_label = hline_new (y++, -1, -1);
887 group_add_widget (g, ui->total_bytes_label);
888
889 if (ctx->progress_totals_computed)
890 {
891 ui->progress_total_gauge =
892 gauge_new (y++, x + 3, dlg_width - (x + 3) * 2, FALSE, 100, 0);
893 if (!classic_progressbar && (current_panel == right_panel))
894 ui->progress_total_gauge->from_left_to_right = FALSE;
895 group_add_widget_autopos (g, ui->progress_total_gauge,
896 WPOS_KEEP_TOP | WPOS_KEEP_HORZ, NULL);
897 }
898
899 ui->total_files_processed_label = label_new (y++, x, NULL);
900 group_add_widget (g, ui->total_files_processed_label);
901
902 ui->time_label = label_new (y++, x, NULL);
903 group_add_widget (g, ui->time_label);
904 }
905 }
906 else
907 {
908 ui->src_file = label_new (y++, x, NULL);
909 group_add_widget (g, ui->src_file);
910
911 ui->total_files_processed_label = label_new (y++, x, NULL);
912 group_add_widget (g, ui->total_files_processed_label);
913 }
914
915 group_add_widget (g, hline_new (y++, -1, -1));
916
917 progress_buttons[0].w = WIDGET (button_new (y, 0, progress_buttons[0].action,
918 progress_buttons[0].flags, progress_buttons[0].text,
919 progress_button_callback));
920 if (progress_buttons[0].len == -1)
921 progress_buttons[0].len = button_get_len (BUTTON (progress_buttons[0].w));
922
923 progress_buttons[1].w = WIDGET (button_new (y, 0, progress_buttons[1].action,
924 progress_buttons[1].flags, progress_buttons[1].text,
925 progress_button_callback));
926 if (progress_buttons[1].len == -1)
927 progress_buttons[1].len = button_get_len (BUTTON (progress_buttons[1].w));
928
929 if (progress_buttons[2].len == -1)
930 {
931
932 progress_buttons[2].w = WIDGET (button_new (y, 0, progress_buttons[2].action,
933 progress_buttons[2].flags,
934 progress_buttons[2].text,
935 progress_button_callback));
936 progress_buttons[2].len = button_get_len (BUTTON (progress_buttons[2].w));
937 widget_destroy (progress_buttons[2].w);
938 }
939 progress_buttons[2].w = progress_buttons[1].w;
940
941 progress_buttons[3].w = WIDGET (button_new (y, 0, progress_buttons[3].action,
942 progress_buttons[3].flags, progress_buttons[3].text,
943 NULL));
944 if (progress_buttons[3].len == -1)
945 progress_buttons[3].len = button_get_len (BUTTON (progress_buttons[3].w));
946
947 group_add_widget (g, progress_buttons[0].w);
948 group_add_widget (g, progress_buttons[1].w);
949 group_add_widget (g, progress_buttons[3].w);
950
951 buttons_width = 2 +
952 progress_buttons[0].len + MAX (progress_buttons[1].len, progress_buttons[2].len) +
953 progress_buttons[3].len;
954
955
956 r = w->rect;
957 r.lines = y + 3;
958 r.cols = MAX (COLS * 2 / 3, buttons_width + 6);
959 widget_set_size_rect (w, &r);
960
961 place_progress_buttons (ui->op_dlg, FALSE);
962
963 widget_select (progress_buttons[0].w);
964
965
966
967 dlg_init (ui->op_dlg);
968 }
969
970
971
972 void
973 file_op_context_destroy_ui (file_op_context_t * ctx)
974 {
975 if (ctx != NULL && ctx->ui != NULL)
976 {
977 file_op_context_ui_t *ui = (file_op_context_ui_t *) ctx->ui;
978
979 dlg_run_done (ui->op_dlg);
980 widget_destroy (WIDGET (ui->op_dlg));
981 MC_PTR_FREE (ctx->ui);
982 }
983 }
984
985
986
987
988
989
990 void
991 file_progress_show (file_op_context_t * ctx, off_t done, off_t total,
992 const char *stalled_msg, gboolean force_update)
993 {
994 file_op_context_ui_t *ui;
995
996 if (!verbose || ctx == NULL || ctx->ui == NULL)
997 return;
998
999 ui = ctx->ui;
1000
1001 if (total == 0)
1002 {
1003 gauge_show (ui->progress_file_gauge, FALSE);
1004 return;
1005 }
1006
1007 gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total));
1008 gauge_show (ui->progress_file_gauge, TRUE);
1009
1010 if (!force_update)
1011 return;
1012
1013 if (!ui->showing_eta || ctx->eta_secs <= 0.5)
1014 label_set_text (ui->progress_file_label, stalled_msg);
1015 else
1016 {
1017 char buffer2[BUF_TINY];
1018
1019 file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE);
1020 if (ctx->bps == 0)
1021 label_set_textv (ui->progress_file_label, "%s %s", buffer2, stalled_msg);
1022 else
1023 {
1024 char buffer3[BUF_TINY];
1025
1026 file_bps_prepare_for_show (buffer3, ctx->bps);
1027 label_set_textv (ui->progress_file_label, "%s (%s) %s", buffer2, buffer3, stalled_msg);
1028 }
1029
1030 }
1031 }
1032
1033
1034
1035 void
1036 file_progress_show_count (file_op_context_t * ctx, size_t done, size_t total)
1037 {
1038 file_op_context_ui_t *ui;
1039
1040 if (ctx == NULL || ctx->ui == NULL)
1041 return;
1042
1043 ui = ctx->ui;
1044
1045 if (ui->total_files_processed_label == NULL)
1046 return;
1047
1048 if (ctx->progress_totals_computed)
1049 label_set_textv (ui->total_files_processed_label, _("Files processed: %zu / %zu"), done,
1050 total);
1051 else
1052 label_set_textv (ui->total_files_processed_label, _("Files processed: %zu"), done);
1053 }
1054
1055
1056
1057 void
1058 file_progress_show_total (file_op_total_context_t * tctx, file_op_context_t * ctx,
1059 uintmax_t copied_bytes, gboolean show_summary)
1060 {
1061 char buffer2[BUF_TINY];
1062 char buffer3[BUF_TINY];
1063 file_op_context_ui_t *ui;
1064
1065 if (ctx == NULL || ctx->ui == NULL)
1066 return;
1067
1068 ui = ctx->ui;
1069
1070 if (ui->progress_total_gauge != NULL)
1071 {
1072 if (ctx->progress_bytes == 0)
1073 gauge_show (ui->progress_total_gauge, FALSE);
1074 else
1075 {
1076 gauge_set_value (ui->progress_total_gauge, 1024,
1077 (int) (1024 * copied_bytes / ctx->progress_bytes));
1078 gauge_show (ui->progress_total_gauge, TRUE);
1079 }
1080 }
1081
1082 if (!show_summary && tctx->bps == 0)
1083 return;
1084
1085 if (ui->time_label != NULL)
1086 {
1087 gint64 tv_current;
1088 char buffer4[BUF_TINY];
1089
1090 tv_current = g_get_monotonic_time ();
1091 file_frmt_time (buffer2, (tv_current - tctx->transfer_start) / G_USEC_PER_SEC);
1092
1093 if (ctx->progress_totals_computed)
1094 {
1095 file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE);
1096 if (tctx->bps == 0)
1097 label_set_textv (ui->time_label, _("Time: %s %s"), buffer2, buffer3);
1098 else
1099 {
1100 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1101 label_set_textv (ui->time_label, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4);
1102 }
1103 }
1104 else
1105 {
1106 if (tctx->bps == 0)
1107 label_set_textv (ui->time_label, _("Time: %s"), buffer2);
1108 else
1109 {
1110 file_bps_prepare_for_show (buffer4, (long) tctx->bps);
1111 label_set_textv (ui->time_label, _("Time: %s (%s)"), buffer2, buffer4);
1112 }
1113 }
1114 }
1115
1116 if (ui->total_bytes_label != NULL)
1117 {
1118 size_trunc_len (buffer2, 5, tctx->copied_bytes, 0, panels_options.kilobyte_si);
1119
1120 if (!ctx->progress_totals_computed)
1121 hline_set_textv (ui->total_bytes_label, _(" Total: %s "), buffer2);
1122 else
1123 {
1124 size_trunc_len (buffer3, 5, ctx->progress_bytes, 0, panels_options.kilobyte_si);
1125 hline_set_textv (ui->total_bytes_label, _(" Total: %s / %s "), buffer2, buffer3);
1126 }
1127 }
1128 }
1129
1130
1131
1132
1133
1134 void
1135 file_progress_show_source (file_op_context_t * ctx, const vfs_path_t * vpath)
1136 {
1137 file_op_context_ui_t *ui;
1138
1139 if (ctx == NULL || ctx->ui == NULL)
1140 return;
1141
1142 ui = ctx->ui;
1143
1144 if (vpath != NULL)
1145 {
1146 char *s;
1147
1148 s = vfs_path_tokens_get (vpath, -1, 1);
1149 label_set_text (ui->src_file_label, _("Source"));
1150 label_set_text (ui->src_file, truncFileString (ui->op_dlg, s));
1151 g_free (s);
1152 }
1153 else
1154 {
1155 label_set_text (ui->src_file_label, NULL);
1156 label_set_text (ui->src_file, NULL);
1157 }
1158 }
1159
1160
1161
1162 void
1163 file_progress_show_target (file_op_context_t * ctx, const vfs_path_t * vpath)
1164 {
1165 file_op_context_ui_t *ui;
1166
1167 if (ctx == NULL || ctx->ui == NULL)
1168 return;
1169
1170 ui = ctx->ui;
1171
1172 if (vpath != NULL)
1173 {
1174 label_set_text (ui->tgt_file_label, _("Target"));
1175 label_set_text (ui->tgt_file, truncFileStringSecure (ui->op_dlg, vfs_path_as_str (vpath)));
1176 }
1177 else
1178 {
1179 label_set_text (ui->tgt_file_label, NULL);
1180 label_set_text (ui->tgt_file, NULL);
1181 }
1182 }
1183
1184
1185
1186 gboolean
1187 file_progress_show_deleting (file_op_context_t * ctx, const char *s, size_t * count)
1188 {
1189 static gint64 timestamp = 0;
1190
1191 static const gint64 delay = G_USEC_PER_SEC / 25;
1192
1193 gboolean ret;
1194
1195 if (ctx == NULL || ctx->ui == NULL)
1196 return FALSE;
1197
1198 ret = mc_time_elapsed (×tamp, delay);
1199
1200 if (ret)
1201 {
1202 file_op_context_ui_t *ui;
1203
1204 ui = ctx->ui;
1205
1206 if (ui->src_file_label != NULL)
1207 label_set_text (ui->src_file_label, _("Deleting"));
1208
1209 label_set_text (ui->src_file, truncFileStringSecure (ui->op_dlg, s));
1210 }
1211
1212 if (count != NULL)
1213 (*count)++;
1214
1215 return ret;
1216 }
1217
1218
1219
1220 FileProgressStatus
1221 file_progress_real_query_replace (file_op_context_t * ctx, enum OperationMode mode,
1222 const char *src, struct stat * src_stat,
1223 const char *dst, struct stat * dst_stat)
1224 {
1225 file_op_context_ui_t *ui;
1226 FileProgressStatus replace_with_zero;
1227
1228 if (ctx == NULL || ctx->ui == NULL)
1229 return FILE_CONT;
1230
1231 ui = ctx->ui;
1232
1233 if (ui->replace_result == REPLACE_YES || ui->replace_result == REPLACE_NO
1234 || ui->replace_result == REPLACE_APPEND)
1235 {
1236 ui->src_filename = src;
1237 ui->src_stat = src_stat;
1238 ui->tgt_filename = dst;
1239 ui->dst_stat = dst_stat;
1240 ui->replace_result = overwrite_query_dialog (ctx, mode);
1241 }
1242
1243 replace_with_zero = (src_stat->st_size == 0
1244 && ui->dont_overwrite_with_zero) ? FILE_SKIP : FILE_CONT;
1245
1246 switch (ui->replace_result)
1247 {
1248 case REPLACE_OLDER:
1249 do_refresh ();
1250 if (src_stat->st_mtime > dst_stat->st_mtime)
1251 return replace_with_zero;
1252 else
1253 return FILE_SKIP;
1254
1255 case REPLACE_SIZE:
1256 do_refresh ();
1257 if (src_stat->st_size == dst_stat->st_size)
1258 return FILE_SKIP;
1259 else
1260 return replace_with_zero;
1261
1262 case REPLACE_SMALLER:
1263 do_refresh ();
1264 if (src_stat->st_size > dst_stat->st_size)
1265 return FILE_CONT;
1266 else
1267 return FILE_SKIP;
1268
1269 case REPLACE_ALL:
1270 do_refresh ();
1271 return replace_with_zero;
1272
1273 case REPLACE_REGET:
1274
1275 ctx->do_reget = dst_stat->st_size;
1276 MC_FALLTHROUGH;
1277
1278 case REPLACE_APPEND:
1279 ctx->do_append = TRUE;
1280 MC_FALLTHROUGH;
1281
1282 case REPLACE_YES:
1283 do_refresh ();
1284 return FILE_CONT;
1285
1286 case REPLACE_NO:
1287 case REPLACE_NONE:
1288 do_refresh ();
1289 return FILE_SKIP;
1290
1291 case REPLACE_ABORT:
1292 default:
1293 return FILE_ABORT;
1294 }
1295 }
1296
1297
1298
1299 char *
1300 file_mask_dialog (file_op_context_t * ctx, gboolean only_one, const char *format, const void *text,
1301 const char *def_text, gboolean * do_bg)
1302 {
1303 size_t fmd_xlen;
1304 vfs_path_t *vpath;
1305 gboolean source_easy_patterns = easy_patterns;
1306 char fmd_buf[BUF_MEDIUM];
1307 char *dest_dir = NULL;
1308 char *tmp;
1309 char *def_text_secure;
1310
1311 if (ctx == NULL)
1312 return NULL;
1313
1314
1315 ctx->op_preserve = copymove_persistent_attr && filegui__check_attrs_on_fs (def_text);
1316 ctx->stable_symlinks = FALSE;
1317 *do_bg = FALSE;
1318
1319
1320 vpath = vfs_path_from_str_flags (def_text, only_one ? VPF_NO_CANON : VPF_NONE);
1321 tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1322 vfs_path_free (vpath, TRUE);
1323
1324 if (source_easy_patterns)
1325 def_text_secure = strutils_glob_escape (tmp);
1326 else
1327 def_text_secure = strutils_regex_escape (tmp);
1328 g_free (tmp);
1329
1330 if (only_one)
1331 {
1332 int format_len, text_len;
1333 int max_len;
1334
1335 format_len = str_term_width1 (format);
1336 text_len = str_term_width1 (text);
1337 max_len = COLS - 2 - 6;
1338
1339 if (format_len + text_len <= max_len)
1340 {
1341 fmd_xlen = format_len + text_len + 6;
1342 fmd_xlen = MAX (fmd_xlen, 68);
1343 }
1344 else
1345 {
1346 text = str_trunc ((const char *) text, max_len - format_len);
1347 fmd_xlen = max_len + 6;
1348 }
1349
1350 g_snprintf (fmd_buf, sizeof (fmd_buf), format, (const char *) text);
1351 }
1352 else
1353 {
1354 fmd_xlen = COLS * 2 / 3;
1355 fmd_xlen = MAX (fmd_xlen, 68);
1356 g_snprintf (fmd_buf, sizeof (fmd_buf), format, *(const int *) text);
1357 }
1358
1359 {
1360 char *source_mask = NULL;
1361 char *orig_mask;
1362 int val;
1363 struct stat buf;
1364
1365 quick_widget_t quick_widgets[] = {
1366
1367 QUICK_LABELED_INPUT (fmd_buf, input_label_above, easy_patterns ? "*" : "^(.*)$",
1368 "input-def", &source_mask, NULL, FALSE, FALSE,
1369 INPUT_COMPLETE_FILENAMES),
1370 QUICK_START_COLUMNS,
1371 QUICK_SEPARATOR (FALSE),
1372 QUICK_NEXT_COLUMN,
1373 QUICK_CHECKBOX (N_("&Using shell patterns"), &source_easy_patterns, NULL),
1374 QUICK_STOP_COLUMNS,
1375 QUICK_LABELED_INPUT (N_("to:"), input_label_above, def_text_secure, "input2", &dest_dir,
1376 NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
1377 QUICK_SEPARATOR (TRUE),
1378 QUICK_START_COLUMNS,
1379 QUICK_CHECKBOX (N_("Follow &links"), &ctx->follow_links, NULL),
1380 QUICK_CHECKBOX (N_("Preserve &attributes"), &ctx->op_preserve, NULL),
1381 QUICK_NEXT_COLUMN,
1382 QUICK_CHECKBOX (N_("Di&ve into subdir if exists"), &ctx->dive_into_subdirs, NULL),
1383 QUICK_CHECKBOX (N_("&Stable symlinks"), &ctx->stable_symlinks, NULL),
1384 QUICK_STOP_COLUMNS,
1385 QUICK_START_BUTTONS (TRUE, TRUE),
1386 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
1387 #ifdef ENABLE_BACKGROUND
1388 QUICK_BUTTON (N_("&Background"), B_USER, NULL, NULL),
1389 #endif
1390 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1391 QUICK_END
1392
1393 };
1394
1395 WRect r = { -1, -1, 0, fmd_xlen };
1396
1397 quick_dialog_t qdlg = {
1398 r, op_names[ctx->operation], "[Mask Copy/Rename]",
1399 quick_widgets, NULL, NULL
1400 };
1401
1402 while (TRUE)
1403 {
1404 val = quick_dialog_skip (&qdlg, 4);
1405
1406 if (val == B_CANCEL)
1407 {
1408 g_free (def_text_secure);
1409 return NULL;
1410 }
1411
1412 ctx->stat_func = ctx->follow_links ? mc_stat : mc_lstat;
1413
1414 if (ctx->op_preserve)
1415 {
1416 ctx->preserve = TRUE;
1417 ctx->umask_kill = 0777777;
1418 ctx->preserve_uidgid = (geteuid () == 0);
1419 }
1420 else
1421 {
1422 mode_t i2;
1423
1424 ctx->preserve = ctx->preserve_uidgid = FALSE;
1425 i2 = umask (0);
1426 umask (i2);
1427 ctx->umask_kill = i2 ^ 0777777;
1428 }
1429
1430 if (*dest_dir == '\0')
1431 {
1432 g_free (def_text_secure);
1433 g_free (source_mask);
1434 g_free (dest_dir);
1435 return NULL;
1436 }
1437
1438 ctx->search_handle = mc_search_new (source_mask, NULL);
1439 if (ctx->search_handle != NULL)
1440 break;
1441
1442 message (D_ERROR, MSG_ERROR, _("Invalid source pattern '%s'"), source_mask);
1443 MC_PTR_FREE (dest_dir);
1444 MC_PTR_FREE (source_mask);
1445 }
1446
1447 g_free (def_text_secure);
1448 g_free (source_mask);
1449
1450 ctx->search_handle->is_case_sensitive = TRUE;
1451 if (source_easy_patterns)
1452 ctx->search_handle->search_type = MC_SEARCH_T_GLOB;
1453 else
1454 ctx->search_handle->search_type = MC_SEARCH_T_REGEX;
1455
1456 tmp = dest_dir;
1457 dest_dir = tilde_expand (tmp);
1458 g_free (tmp);
1459 vpath = vfs_path_from_str (dest_dir);
1460
1461 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
1462 if (ctx->dest_mask == NULL)
1463 ctx->dest_mask = dest_dir;
1464 else
1465 ctx->dest_mask++;
1466
1467 orig_mask = ctx->dest_mask;
1468
1469 if (*ctx->dest_mask == '\0'
1470 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
1471 && (!only_one
1472 || (mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode))))
1473 || (ctx->dive_into_subdirs
1474 && ((!only_one && !is_wildcarded (ctx->dest_mask))
1475 || (only_one && mc_stat (vpath, &buf) == 0 && S_ISDIR (buf.st_mode)))))
1476 ctx->dest_mask = g_strdup ("\\0");
1477 else
1478 {
1479 ctx->dest_mask = g_strdup (ctx->dest_mask);
1480 *orig_mask = '\0';
1481 }
1482
1483 if (*dest_dir == '\0')
1484 {
1485 g_free (dest_dir);
1486 dest_dir = g_strdup ("./");
1487 }
1488
1489 vfs_path_free (vpath, TRUE);
1490
1491 if (val == B_USER)
1492 *do_bg = TRUE;
1493 }
1494
1495 return dest_dir;
1496 }
1497
1498