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