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