2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1994, 1995 Janne Kukonlehto
6 1994, 1995 Fred Leeflang
7 1994, 1995, 1996 Miguel de Icaza
8 1995, 1996 Jakub Jelinek
12 The copy code was based in GNU's cp, and was written by:
13 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
15 The move code was based in GNU's mv, and was written by:
16 Mike Parker and David MacKenzie.
18 Janne Kukonlehto added much error recovery to them for being used
19 in an interactive program.
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
36 * Please note that all dialogs used here must be safe for background
40 /* {{{ Include files */
49 #include <sys/types.h>
53 #include <mhl/types.h>
54 #include <mhl/memory.h>
55 #include <mhl/escape.h>
56 #include <mhl/string.h>
66 #include "main.h" /* cmd_buf */
70 #include "background.h" /* we_are_background */
73 /* Needed for current_panel, other_panel and WTree */
80 #include "../vfs/vfs-impl.h"
84 /* Hack: the vfs code should not rely on this */
85 #define WITH_FULL_PATHS 1
90 * Whether the Midnight Commander tries to provide more
91 * information about copy/move sizes and bytes transfered
92 * at the expense of some speed
94 int file_op_compute_totals
= 1;
96 /* This is a hard link cache */
99 struct vfs_class
*vfs
;
107 /* the hard link cache */
108 static struct link
*linklist
= NULL
;
110 /* the files-to-be-erased list */
111 static struct link
*erase_list
;
114 * In copy_dir_dir we use two additional single linked lists: The first -
115 * variable name `parent_dirs' - holds information about already copied
116 * directories and is used to detect cyclic symbolic links.
117 * The second (`dest_dirs' below) holds information about just created
118 * target directories and is used to detect when an directory is copied
119 * into itself (we don't want to copy infinitly).
120 * Both lists don't use the linkcount and name structure members of struct
123 static struct link
*dest_dirs
= 0;
125 const char *op_names
[3] = {
133 static int query_replace (FileOpContext
* ctx
, const char *destname
,
134 struct stat
*_s_stat
, struct stat
*_d_stat
);
135 static int query_recursive (FileOpContext
* ctx
, const char *s
);
136 static int do_file_error (const char *str
);
137 static int erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
);
138 static int erase_file (FileOpContext
*ctx
, const char *s
,
139 off_t
*progress_count
, double *progress_bytes
,
140 int is_toplevel_file
);
141 static int files_error (const char *format
, const char *file1
,
145 enum CaseConvs
{ NO_CONV
= 0, UP_CHAR
= 1, LOW_CHAR
= 2, UP_SECT
=
149 convert_case (char c
, enum CaseConvs
*conversion
)
151 if (*conversion
& UP_CHAR
) {
152 *conversion
&= ~UP_CHAR
;
153 return toupper ((unsigned char) c
);
154 } else if (*conversion
& LOW_CHAR
) {
155 *conversion
&= ~LOW_CHAR
;
156 return tolower ((unsigned char) c
);
157 } else if (*conversion
& UP_SECT
) {
158 return toupper ((unsigned char) c
);
159 } else if (*conversion
& LOW_SECT
) {
160 return tolower ((unsigned char) c
);
165 static int transform_error
= 0;
168 do_transform_source (FileOpContext
*ctx
, const char *source
)
171 const char *fnsource
= x_basename (source
);
173 enum CaseConvs case_conv
= NO_CONV
;
174 static char fntarget
[MC_MAXPATHLEN
];
176 len
= strlen (fnsource
);
177 j
= re_match (&ctx
->rx
, fnsource
, len
, 0, &ctx
->regs
);
179 transform_error
= FILE_SKIP
;
182 for (next_reg
= 1, j
= 0, k
= 0; j
< strlen (ctx
->dest_mask
); j
++) {
183 switch (ctx
->dest_mask
[j
]) {
185 if (mhl_shell_is_char_escaped (&ctx
->dest_mask
[j
])){
186 fntarget
[k
++] = ctx
->dest_mask
[j
++];
187 fntarget
[k
++] = ctx
->dest_mask
[j
];
191 if (!isdigit ((unsigned char) ctx
->dest_mask
[j
])) {
192 /* Backslash followed by non-digit */
193 switch (ctx
->dest_mask
[j
]) {
195 case_conv
|= UP_SECT
;
196 case_conv
&= ~LOW_SECT
;
199 case_conv
|= UP_CHAR
;
202 case_conv
|= LOW_SECT
;
203 case_conv
&= ~UP_SECT
;
206 case_conv
|= LOW_CHAR
;
212 /* Backslash as quote mark */
214 convert_case (ctx
->dest_mask
[j
], &case_conv
);
218 /* Backslash followed by digit */
219 next_reg
= ctx
->dest_mask
[j
] - '0';
225 if (next_reg
< 0 || next_reg
>= RE_NREGS
226 || ctx
->regs
.start
[next_reg
] < 0) {
227 message (D_ERROR
, MSG_ERROR
, _(" Invalid target mask "));
228 transform_error
= FILE_ABORT
;
231 for (l
= (size_t) ctx
->regs
.start
[next_reg
];
232 l
< (size_t) ctx
->regs
.end
[next_reg
]; l
++)
233 fntarget
[k
++] = convert_case (fnsource
[l
], &case_conv
);
238 fntarget
[k
++] = convert_case (ctx
->dest_mask
[j
], &case_conv
);
247 transform_source (FileOpContext
*ctx
, const char *source
)
249 char *s
= g_strdup (source
);
253 /* We remove \n from the filename since regex routines would use \n as an anchor */
254 /* this is just to be allowed to maniupulate file names with \n on it */
255 for (q
= s
; *q
; q
++) {
259 p
= do_transform_source (ctx
, s
);
265 free_linklist (struct link
**linklist
)
267 struct link
*lp
, *lp2
;
269 for (lp
= *linklist
; lp
!= NULL
; lp
= lp2
) {
277 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
279 ino_t ino
= sb
->st_ino
;
280 dev_t dev
= sb
->st_dev
;
282 struct vfs_class
*vfs
= vfs_get_class (path
);
289 if (lp
->ino
== ino
&& lp
->dev
== dev
)
297 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
298 * and a hardlink was succesfully made
301 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
304 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
305 ino_t ino
= pstat
->st_ino
;
306 dev_t dev
= pstat
->st_dev
;
307 struct stat link_stat
;
310 if (vfs_file_class_flags (src_name
) & VFSF_NOLINKS
)
313 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
314 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
) {
315 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
316 && link_stat
.st_dev
== dev
317 && vfs_get_class (lp
->name
) == my_vfs
) {
318 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
320 if (vfs_get_class (dst_name
) == vfs_get_class (p
)) {
321 if (!mc_stat (p
, &link_stat
)) {
322 if (!mc_link (p
, dst_name
))
327 message (D_ERROR
, MSG_ERROR
, _(" Cannot make the hardlink "));
330 lp
= (struct link
*) g_malloc (sizeof (struct link
) + strlen (src_name
)
331 + strlen (dst_name
) + 1);
337 strcpy (lp
->name
, src_name
);
338 lpdstname
= lp
->name
+ strlen(lp
->name
) + 1;
339 strcpy (lpdstname
, dst_name
);
347 * Duplicate the contents of the symbolic link src_path in dst_path.
348 * Try to make a stable symlink if the option "stable symlink" was
349 * set in the file mask dialog.
350 * If dst_path is an existing symlink it will be deleted silently
351 * (upper levels take already care of existing files at dst_path).
354 make_symlink (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
)
356 char link_target
[MC_MAXPATHLEN
];
362 if (mc_lstat (dst_path
, &sb
) == 0 && S_ISLNK (sb
.st_mode
))
368 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
371 file_error (_(" Cannot read source link \"%s\" \n %s "),
373 if (return_status
== FILE_RETRY
)
374 goto retry_src_readlink
;
375 return return_status
;
377 link_target
[len
] = 0;
379 if (ctx
->stable_symlinks
)
380 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
381 message (D_ERROR
, MSG_ERROR
,
382 _(" Cannot make stable symlinks across "
383 "non-local filesystems: \n\n"
384 " Option Stable Symlinks will be disabled "));
385 ctx
->stable_symlinks
= 0;
388 if (ctx
->stable_symlinks
&& *link_target
!= PATH_SEP
) {
391 const char *r
= strrchr (src_path
, PATH_SEP
);
394 p
= g_strndup (src_path
, r
- src_path
+ 1);
395 if (*dst_path
== PATH_SEP
)
396 q
= g_strdup (dst_path
);
398 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
399 s
= strrchr (q
, PATH_SEP
);
402 s
= g_strconcat (p
, link_target
, (char *) NULL
);
404 g_strlcpy (link_target
, s
, sizeof (link_target
));
406 s
= diff_two_paths (q
, link_target
);
408 g_strlcpy (link_target
, s
, sizeof (link_target
));
417 if (mc_symlink (link_target
, dst_path
) == 0)
421 * if dst_exists, it is obvious that this had failed.
422 * We can delete the old symlink and try again...
424 if (dst_is_symlink
) {
425 if (!mc_unlink (dst_path
))
426 if (mc_symlink (link_target
, dst_path
) == 0)
431 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
433 if (return_status
== FILE_RETRY
)
434 goto retry_dst_symlink
;
435 return return_status
;
439 progress_update_one (FileOpContext
*ctx
,
440 off_t
*progress_count
,
441 double *progress_bytes
, off_t add
, int is_toplevel_file
)
445 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
447 (*progress_bytes
) += add
;
450 /* Apply some heuristic here to not call the update stuff very often */
452 file_progress_show_count (ctx
, *progress_count
,
453 ctx
->progress_count
);
454 if (ret
!= FILE_CONT
)
457 file_progress_show_bytes (ctx
, *progress_bytes
,
458 ctx
->progress_bytes
);
463 /* Status of the destination file */
465 DEST_NONE
, /* Not created */
466 DEST_SHORT
, /* Created, not fully copied */
467 DEST_FULL
/* Created, fully copied */
471 copy_file_file (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
,
472 int ask_overwrite
, off_t
*progress_count
,
473 double *progress_bytes
, int is_toplevel_file
)
475 uid_t src_uid
= (uid_t
) - 1;
476 gid_t src_gid
= (gid_t
) - 1;
479 int buf_size
= BUF_8K
;
480 int src_desc
, dest_desc
= -1;
481 int n_read
, n_written
;
482 mode_t src_mode
= 0; /* The mode of the source file */
485 int dst_exists
= 0, appending
= 0;
486 off_t n_read_total
= 0, file_size
= -1;
487 int return_status
, temp_status
;
488 struct timeval tv_transfer_start
;
489 int dst_status
= DEST_NONE
; /* 1 if the file is not fully copied */
491 /* FIXME: We should not be using global variables! */
493 return_status
= FILE_RETRY
;
495 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
496 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
501 while (mc_stat (dst_path
, &sb2
) == 0) {
502 if (S_ISDIR (sb2
.st_mode
)) {
504 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
506 if (return_status
== FILE_RETRY
)
508 return return_status
;
514 while ((*ctx
->stat_func
) (src_path
, &sb
)) {
516 file_error (_(" Cannot stat source file \"%s\" \n %s "),
518 if (return_status
!= FILE_RETRY
)
519 return return_status
;
523 /* Destination already exists */
524 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
) {
525 message (D_ERROR
, MSG_ERROR
,
526 _(" `%s' and `%s' are the same file "), src_path
, dst_path
);
531 /* Should we replace destination? */
534 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
535 if (return_status
!= FILE_CONT
)
536 return return_status
;
540 if (!ctx
->do_append
) {
541 /* Check the hardlinks */
542 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
543 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
544 /* We have made a hardlink - no more processing is necessary */
548 if (S_ISLNK (sb
.st_mode
)) {
551 retval
= make_symlink (ctx
, src_path
, dst_path
);
555 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
556 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) ||
557 S_ISSOCK (sb
.st_mode
)) {
558 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
560 return_status
= file_error (
561 _(" Cannot create special file \"%s\" \n %s "), dst_path
);
562 if (return_status
== FILE_RETRY
)
564 return return_status
;
568 while (ctx
->preserve_uidgid
569 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
570 temp_status
= file_error (
571 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
572 if (temp_status
== FILE_RETRY
)
576 while (ctx
->preserve
&&
577 mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
)) {
578 temp_status
= file_error (
579 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
580 if (temp_status
== FILE_RETRY
)
588 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
590 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
591 return_status
= file_error (
592 _(" Cannot open source file \"%s\" \n %s "), src_path
);
593 if (return_status
== FILE_RETRY
)
596 return return_status
;
600 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
) {
601 message (D_ERROR
, _("Warning"),
602 _(" Reget failed, about to overwrite file "));
603 ctx
->do_reget
= ctx
->do_append
= 0;
607 while (mc_fstat (src_desc
, &sb
)) {
608 return_status
= file_error (
609 _(" Cannot fstat source file \"%s\" \n %s "), src_path
);
610 if (return_status
== FILE_RETRY
)
615 src_mode
= sb
.st_mode
;
618 utb
.actime
= sb
.st_atime
;
619 utb
.modtime
= sb
.st_mtime
;
620 file_size
= sb
.st_size
;
622 /* Create the new regular file with small permissions initially,
623 do not create a security hole. FIXME: You have security hole
624 here, btw. Imagine copying to /tmp and symlink attack :-( */
626 while ((dest_desc
= mc_open (dst_path
, O_WRONLY
| (ctx
->do_append
?
627 O_APPEND
: (O_CREAT
| O_TRUNC
)), 0600)) < 0) {
628 return_status
= file_error (
629 _(" Cannot create target file \"%s\" \n %s "), dst_path
);
630 if (return_status
== FILE_RETRY
)
635 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
637 appending
= ctx
->do_append
;
640 /* Find out the optimal buffer size. */
641 while (mc_fstat (dest_desc
, &sb
)) {
642 return_status
= file_error (
643 _(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
644 if (return_status
== FILE_RETRY
)
648 buf
= g_malloc (buf_size
);
653 return_status
= file_progress_show (ctx
, 0, file_size
);
657 if (return_status
!= FILE_CONT
)
661 struct timeval tv_current
, tv_last_update
, tv_last_input
;
662 int secs
, update_secs
;
664 const char *stalled_msg
;
666 tv_last_update
= tv_transfer_start
;
670 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
673 while ((n_read
= mc_read (src_desc
, buf
, buf_size
)) < 0) {
674 return_status
= file_error (
675 _(" Cannot read source file \"%s\" \n %s "), src_path
);
676 if (return_status
== FILE_RETRY
)
683 gettimeofday (&tv_current
, NULL
);
687 n_read_total
+= n_read
;
689 /* Windows NT ftp servers report that files have no
690 * permissions: -------, so if we happen to have actually
691 * read something, we should fix the permissions.
693 if (!(src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)))
694 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
695 gettimeofday (&tv_last_input
, NULL
);
699 mc_write (dest_desc
, t
, n_read
)) < n_read
) {
706 file_error (_(" Cannot write target file \"%s\" \n %s "),
708 if (return_status
!= FILE_RETRY
)
713 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
714 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
717 tv_last_update
= tv_current
;
720 /* 2. Check for a stalled condition */
721 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
723 if (update_secs
> 4) {
724 stalled_msg
= _("(stalled)");
729 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
733 ((dt
/ (double) n_read_total
) * file_size
) - dt
;
734 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
739 /* 4. Compute BPS rate */
742 (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
743 if (ctx
->bps_time
< 1)
745 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
748 file_progress_set_stalled_label (ctx
, stalled_msg
);
749 return_status
= file_progress_show_bytes (ctx
, *progress_bytes
+
750 n_read_total
+ ctx
->do_reget
, ctx
->progress_bytes
);
751 if (return_status
== FILE_CONT
) {
753 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
);
756 if (return_status
!= FILE_CONT
)
761 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
766 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
767 temp_status
= file_error (
768 _(" Cannot close source file \"%s\" \n %s "), src_path
);
769 if (temp_status
== FILE_RETRY
)
771 if (temp_status
== FILE_ABORT
)
772 return_status
= temp_status
;
776 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
777 temp_status
= file_error (
778 _(" Cannot close target file \"%s\" \n %s "), dst_path
);
779 if (temp_status
== FILE_RETRY
)
781 return_status
= temp_status
;
785 if (dst_status
== DEST_SHORT
) {
786 /* Remove short file */
788 result
= query_dialog (_("Copy"),
789 _("Incomplete file was retrieved. Keep it?"),
790 D_ERROR
, 2, _("&Delete"), _("&Keep"));
792 mc_unlink (dst_path
);
793 } else if (dst_status
== DEST_FULL
) {
794 /* Copy has succeeded */
795 if (!appending
&& ctx
->preserve_uidgid
) {
796 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
797 temp_status
= file_error (
798 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
799 if (temp_status
== FILE_RETRY
)
801 return_status
= temp_status
;
806 if (!appending
&& ctx
->preserve
) {
807 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
))) {
808 temp_status
= file_error (
809 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
810 if (temp_status
!= FILE_RETRY
) {
811 return_status
= temp_status
;
815 mc_utime (dst_path
, &utb
);
819 if (return_status
== FILE_CONT
)
821 progress_update_one (ctx
, progress_count
, progress_bytes
,
822 file_size
, is_toplevel_file
);
824 return return_status
;
828 * I think these copy_*_* functions should have a return type.
829 * anyway, this function *must* have two directories as arguments.
831 /* FIXME: This function needs to check the return values of the
834 copy_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
, int toplevel
,
835 int move_over
, int delete, struct link
*parent_dirs
,
836 off_t
*progress_count
, double *progress_bytes
)
839 struct stat buf
, cbuf
;
841 char *path
, *mdpath
, *dest_file
, *dest_dir
;
842 int return_status
= FILE_CONT
;
846 /* First get the mode of the source dir */
848 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
850 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
851 if (return_status
== FILE_RETRY
)
853 return return_status
;
856 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
857 /* Don't copy a directory we created before (we don't want to copy
858 infinitely if a directory is copied into itself) */
859 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
863 /* Hmm, hardlink to directory??? - Norbert */
864 /* FIXME: In this step we should do something
865 in case the destination already exist */
866 /* Check the hardlinks */
867 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
868 && check_hardlinks (s
, d
, &cbuf
) == 1) {
869 /* We have made a hardlink - no more processing is necessary */
870 return return_status
;
873 if (!S_ISDIR (cbuf
.st_mode
)) {
875 file_error (_(" Source \"%s\" is not a directory \n %s "), s
);
876 if (return_status
== FILE_RETRY
)
878 return return_status
;
881 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
882 /* we found a cyclic symbolic link */
883 message (D_ERROR
, MSG_ERROR
,
884 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
888 lp
= g_new (struct link
, 1);
889 lp
->vfs
= vfs_get_class (s
);
890 lp
->ino
= cbuf
.st_ino
;
891 lp
->dev
= cbuf
.st_dev
;
892 lp
->next
= parent_dirs
;
896 /* Now, check if the dest dir exists, if not, create it. */
897 if (mc_stat (d
, &buf
)) {
898 /* Here the dir doesn't exist : make it ! */
901 if (mc_rename (s
, d
) == 0) {
902 g_free (parent_dirs
);
906 dest_dir
= g_strdup (d
);
909 * If the destination directory exists, we want to copy the whole
910 * directory, but we only want this to happen once.
912 * Escape sequences added to the * to compiler warnings.
913 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
914 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
916 if (!S_ISDIR (buf
.st_mode
)) {
917 return_status
= file_error(
918 _(" Destination \"%s\" must be a directory \n %s "), d
);
919 if (return_status
== FILE_RETRY
)
921 g_free (parent_dirs
);
922 return return_status
;
924 /* Dive into subdir if exists */
925 if (toplevel
&& ctx
->dive_into_subdirs
) {
926 dest_dir
= mhl_str_dir_plus_file (d
, x_basename (s
));
928 dest_dir
= g_strdup (d
);
932 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
933 return_status
= file_error (
934 _(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
935 if (return_status
!= FILE_RETRY
)
939 lp
= g_new (struct link
, 1);
940 mc_stat (dest_dir
, &buf
);
941 lp
->vfs
= vfs_get_class (dest_dir
);
942 lp
->ino
= buf
.st_ino
;
943 lp
->dev
= buf
.st_dev
;
944 lp
->next
= dest_dirs
;
947 if (ctx
->preserve_uidgid
) {
948 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
949 return_status
= file_error (
950 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
951 if (return_status
!= FILE_RETRY
)
957 /* open the source dir for reading */
958 if ((reading
= mc_opendir (s
)) == 0) {
962 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
964 * Now, we don't want '.' and '..' to be created / copied at any time
966 if (!strcmp (next
->d_name
, "."))
968 if (!strcmp (next
->d_name
, ".."))
971 /* get the filename and add it to the src directory */
972 path
= mhl_str_dir_plus_file (s
, next
->d_name
);
974 (*ctx
->stat_func
) (path
, &buf
);
975 if (S_ISDIR (buf
.st_mode
)) {
976 mdpath
= mhl_str_dir_plus_file (dest_dir
, next
->d_name
);
978 * From here, we just intend to recursively copy subdirs, not
979 * the double functionality of copying different when the target
980 * dir already exists. So, we give the recursive call the flag 0
981 * meaning no toplevel.
983 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0, delete,
984 parent_dirs
, progress_count
, progress_bytes
);
987 dest_file
= mhl_str_dir_plus_file (dest_dir
, x_basename (path
));
988 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
989 progress_count
, progress_bytes
, 0);
992 if (delete && return_status
== FILE_CONT
) {
993 if (ctx
->erase_at_end
) {
994 static struct link
*tail
;
995 lp
= g_malloc (sizeof (struct link
) + strlen (path
));
996 strcpy (lp
->name
, path
);
997 lp
->st_mode
= buf
.st_mode
;
1003 erase_list
= tail
= lp
;
1005 if (S_ISDIR (buf
.st_mode
)) {
1006 return_status
= erase_dir_iff_empty (ctx
, path
);
1008 return_status
= erase_file (ctx
, path
, 0, 0, 0);
1013 mc_closedir (reading
);
1015 if (ctx
->preserve
) {
1016 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1017 utb
.actime
= cbuf
.st_atime
;
1018 utb
.modtime
= cbuf
.st_mtime
;
1019 mc_utime (dest_dir
, &utb
);
1024 g_free (parent_dirs
);
1025 return return_status
;
1030 /* {{{ Move routines */
1033 move_file_file (FileOpContext
*ctx
, const char *s
, const char *d
,
1034 off_t
*progress_count
, double *progress_bytes
)
1036 struct stat src_stats
, dst_stats
;
1037 int return_status
= FILE_CONT
;
1038 bool copy_done
= FALSE
;
1040 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
1041 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1046 while (mc_lstat (s
, &src_stats
) != 0) {
1047 /* Source doesn't exist */
1049 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1050 if (return_status
!= FILE_RETRY
)
1051 return return_status
;
1054 if (mc_lstat (d
, &dst_stats
) == 0) {
1055 if (src_stats
.st_dev
== dst_stats
.st_dev
1056 && src_stats
.st_ino
== dst_stats
.st_ino
) {
1057 int msize
= COLS
- 36;
1058 char st
[MC_MAXPATHLEN
];
1059 char dt
[MC_MAXPATHLEN
];
1065 strcpy (st
, path_trunc (s
, msize
));
1066 strcpy (dt
, path_trunc (d
, msize
));
1067 message (D_ERROR
, MSG_ERROR
,
1068 _(" `%s' and `%s' are the same file "), st
, dt
);
1073 if (S_ISDIR (dst_stats
.st_mode
)) {
1074 message (D_ERROR
, MSG_ERROR
,
1075 _(" Cannot overwrite directory `%s' "), d
);
1080 if (confirm_overwrite
) {
1081 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1082 if (return_status
!= FILE_CONT
)
1083 return return_status
;
1085 /* Ok to overwrite */
1088 if (!ctx
->do_append
) {
1089 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1090 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1091 goto retry_src_remove
;
1093 return return_status
;
1096 if (mc_rename (s
, d
) == 0) {
1097 return progress_update_one (ctx
, progress_count
,
1099 src_stats
.st_size
, 1);
1103 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1104 one nfs to the same, but on the server it is on two different
1105 filesystems. Then nfs returns EIO instead of EXDEV.
1106 Hope it will not hurt if we always in case of error try to copy/delete. */
1108 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1110 if (errno
!= EXDEV
) {
1112 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1114 if (return_status
== FILE_RETRY
)
1116 return return_status
;
1120 /* Failed because filesystem boundary -> copy the file instead */
1122 copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1123 if (return_status
!= FILE_CONT
)
1124 return return_status
;
1128 if ((return_status
=
1129 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1130 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1131 return return_status
;
1136 if (mc_unlink (s
)) {
1138 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1139 if (return_status
== FILE_RETRY
)
1140 goto retry_src_remove
;
1141 return return_status
;
1145 return_status
= progress_update_one (ctx
,
1148 src_stats
.st_size
, 1);
1151 return return_status
;
1155 move_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
,
1156 off_t
*progress_count
, double *progress_bytes
)
1158 struct stat sbuf
, dbuf
, destbuf
;
1164 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1165 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1171 if (mc_stat (d
, &dbuf
))
1172 destdir
= g_strdup (d
); /* destination doesn't exist */
1173 else if (!ctx
->dive_into_subdirs
) {
1174 destdir
= g_strdup (d
);
1177 destdir
= mhl_str_dir_plus_file (d
, x_basename (s
));
1179 if (sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
) {
1180 int msize
= COLS
- 36;
1181 char st
[MC_MAXPATHLEN
];
1182 char dt
[MC_MAXPATHLEN
];
1188 strcpy (st
, path_trunc (s
, msize
));
1189 strcpy (dt
, path_trunc (d
, msize
));
1190 message (D_ERROR
, MSG_ERROR
,
1191 _(" `%s' and `%s' are the same directory "), st
, dt
);
1196 /* Check if the user inputted an existing dir */
1198 if (!mc_stat (destdir
, &destbuf
)) {
1200 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1201 progress_count
, progress_bytes
);
1203 if (return_status
!= FILE_CONT
)
1207 if (S_ISDIR (destbuf
.st_mode
))
1210 (" Cannot overwrite directory \"%s\" %s "),
1214 file_error (_(" Cannot overwrite file \"%s\" %s "),
1216 if (return_status
== FILE_RETRY
)
1217 goto retry_dst_stat
;
1220 return return_status
;
1224 if (mc_rename (s
, destdir
) == 0) {
1225 return_status
= FILE_CONT
;
1229 if (errno
!= EXDEV
) {
1232 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1234 if (return_status
== FILE_RETRY
)
1238 /* Failed because of filesystem boundary -> copy dir instead */
1240 copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
,
1243 if (return_status
!= FILE_CONT
)
1246 if ((return_status
=
1247 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1248 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1252 if (ctx
->erase_at_end
) {
1253 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1254 if (S_ISDIR (erase_list
->st_mode
)) {
1256 erase_dir_iff_empty (ctx
, erase_list
->name
);
1259 erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1261 erase_list
= erase_list
->next
;
1265 erase_dir_iff_empty (ctx
, s
);
1269 while (erase_list
) {
1271 erase_list
= erase_list
->next
;
1274 return return_status
;
1279 /* {{{ Erase routines */
1280 /* Don't update progress status if progress_count==NULL */
1282 erase_file (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1283 double *progress_bytes
, int is_toplevel_file
)
1288 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1292 if (progress_count
&& mc_lstat (s
, &buf
)) {
1293 /* ignore, most likely the mc_unlink fails, too */
1297 while (mc_unlink (s
)) {
1299 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1300 if (return_status
!= FILE_RETRY
)
1301 return return_status
;
1305 return progress_update_one (ctx
, progress_count
, progress_bytes
,
1306 buf
.st_size
, is_toplevel_file
);
1312 recursive_erase (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1313 double *progress_bytes
)
1315 struct dirent
*next
;
1319 int return_status
= FILE_CONT
;
1321 if (!strcmp (s
, ".."))
1324 reading
= mc_opendir (s
);
1329 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1330 if (!strcmp (next
->d_name
, "."))
1332 if (!strcmp (next
->d_name
, ".."))
1334 path
= mhl_str_dir_plus_file (s
, next
->d_name
);
1335 if (mc_lstat (path
, &buf
)) {
1337 mc_closedir (reading
);
1340 if (S_ISDIR (buf
.st_mode
))
1343 (ctx
, path
, progress_count
, progress_bytes
)
1347 erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1350 mc_closedir (reading
);
1351 if (return_status
!= FILE_CONT
)
1352 return return_status
;
1353 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1357 while (my_rmdir (s
)) {
1359 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1360 if (return_status
!= FILE_RETRY
)
1361 return return_status
;
1367 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1368 in the directory path points to, 0 else. */
1370 check_dir_is_empty (const char *path
)
1376 dir
= mc_opendir (path
);
1380 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1381 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1382 (d
->d_name
[1] == '.'
1383 && d
->d_name
[2] == '\0')))
1384 continue; /* "." or ".." */
1394 erase_dir (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1395 double *progress_bytes
)
1399 if (strcmp (s
, "..") == 0)
1402 if (strcmp (s
, ".") == 0)
1405 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1409 /* The old way to detect a non empty directory was:
1410 error = my_rmdir (s);
1411 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1412 For the linux user space nfs server (nfs-server-2.2beta29-2)
1413 we would have to check also for EIO. I hope the new way is
1414 fool proof. (Norbert)
1416 error
= check_dir_is_empty (s
);
1417 if (error
== 0) { /* not empty */
1418 error
= query_recursive (ctx
, s
);
1419 if (error
== FILE_CONT
)
1420 return recursive_erase (ctx
, s
, progress_count
,
1426 while (my_rmdir (s
) == -1) {
1428 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1429 if (error
!= FILE_RETRY
)
1437 erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
)
1441 if (strcmp (s
, "..") == 0)
1444 if (strcmp (s
, ".") == 0)
1447 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1451 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1454 while (my_rmdir (s
)) {
1456 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1457 if (error
!= FILE_RETRY
)
1466 /* {{{ Panel operate routines */
1469 * Return currently selected entry name or the name of the first marked
1470 * entry if there is one.
1473 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1477 if (get_current_type () == view_tree
) {
1478 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1479 char *tree_name
= tree_selected_name (tree
);
1481 mc_stat (tree_name
, stat_buf
);
1485 if (panel
->marked
) {
1486 for (i
= 0; i
< panel
->count
; i
++)
1487 if (panel
->dir
.list
[i
].f
.marked
) {
1488 *stat_buf
= panel
->dir
.list
[i
].st
;
1489 return panel
->dir
.list
[i
].fname
;
1492 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1493 return panel
->dir
.list
[panel
->selected
].fname
;
1495 g_assert_not_reached ();
1502 * Computes the number of bytes used by the files in a directory
1505 compute_dir_size (const char *dirname
, off_t
*ret_marked
, double *ret_total
)
1508 struct dirent
*dirent
;
1510 dir
= mc_opendir (dirname
);
1515 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1520 if (strcmp (dirent
->d_name
, ".") == 0)
1522 if (strcmp (dirent
->d_name
, "..") == 0)
1525 fullname
= mhl_str_dir_plus_file (dirname
, dirent
->d_name
);
1527 res
= mc_lstat (fullname
, &s
);
1534 if (S_ISDIR (s
.st_mode
)) {
1535 off_t subdir_count
= 0;
1536 double subdir_bytes
= 0;
1538 compute_dir_size (fullname
, &subdir_count
, &subdir_bytes
);
1540 *ret_marked
+= subdir_count
;
1541 *ret_total
+= subdir_bytes
;
1544 *ret_total
+= s
.st_size
;
1553 * panel_compute_totals:
1555 * compute the number of files and the number of bytes
1556 * used up by the whole selection, recursing directories
1557 * as required. In addition, it checks to see if it will
1558 * overwrite any files by doing the copy.
1561 panel_compute_totals (WPanel
*panel
, off_t
*ret_marked
, double *ret_total
)
1568 for (i
= 0; i
< panel
->count
; i
++) {
1571 if (!panel
->dir
.list
[i
].f
.marked
)
1574 s
= &panel
->dir
.list
[i
].st
;
1576 if (S_ISDIR (s
->st_mode
)) {
1578 off_t subdir_count
= 0;
1579 double subdir_bytes
= 0;
1582 mhl_str_dir_plus_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1583 compute_dir_size (dir_name
, &subdir_count
, &subdir_bytes
);
1585 *ret_marked
+= subdir_count
;
1586 *ret_total
+= subdir_bytes
;
1590 *ret_total
+= s
->st_size
;
1596 * This array introduced to avoid translation problems. The former (op_names)
1597 * is assumed to be nouns, suitable in dialog box titles; this one should
1598 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1599 * Notice first symbol - it is to fool gettext and force these strings to
1600 * be different for it. First symbol is skipped while building a prompt.
1601 * (I don't use spaces around the words, because someday they could be
1602 * dropped, when widgets get smarter)
1604 static const char *op_names1
[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1607 int fmd_xlen
= FMD_XLEN
;
1610 * These are formats for building a prompt. Parts encoded as follows:
1611 * %o - operation from op_names1
1612 * %f - file/files or files/directories, as appropriate
1613 * %m - "with source mask" or question mark for delete
1614 * %s - source name (truncated)
1615 * %d - number of marked files
1616 * %e - "to:" or question mark for delete
1618 * xgettext:no-c-format */
1619 static const char *one_format
= N_("%o %f \"%s\"%m");
1620 /* xgettext:no-c-format */
1621 static const char *many_format
= N_("%o %d %f%m");
1622 static const char *prompt_parts
[] = {
1623 N_("file"), N_("files"), N_("directory"), N_("directories"),
1624 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1628 * Generate user prompt for panel operation.
1629 * single_source is the name if the source entry or NULL for multiple
1631 * src_stat is only used when single_source is not NULL.
1634 panel_operate_generate_prompt (const WPanel
*panel
, const int operation
,
1635 const char *single_source
,
1636 const struct stat
*src_stat
)
1638 register const char *sp
, *cp
;
1640 char format_string
[BUF_MEDIUM
];
1641 char *dp
= format_string
;
1644 static int i18n_flag
= 0;
1646 fmd_init_i18n (FALSE
); /* to get proper fmd_xlen */
1648 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1649 op_names1
[i
] = _(op_names1
[i
]);
1651 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1652 prompt_parts
[i
] = _(prompt_parts
[i
]);
1654 one_format
= _(one_format
);
1655 many_format
= _(many_format
);
1658 #endif /* ENABLE_NLS */
1660 sp
= single_source
? one_format
: many_format
;
1668 cp
= op_names1
[operation
] + 1;
1671 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1674 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[6];
1677 if (single_source
) {
1678 cp
= S_ISDIR (src_stat
->
1679 st_mode
) ? prompt_parts
[2] :
1682 cp
= (panel
->marked
== panel
->dirs_marked
)
1684 : (panel
->dirs_marked
? prompt_parts
[4]
1703 if (single_source
) {
1704 i
= fmd_xlen
- strlen (format_string
) - 4;
1705 snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1706 name_trunc (single_source
, i
));
1708 snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1710 i
= strlen (cmd_buf
) + 6 - fmd_xlen
;
1713 fmd_init_i18n (TRUE
); /* to recalculate positions of child widgets */
1721 * Performs one of the operations on the selection on the source_panel
1722 * (copy, delete, move).
1724 * Returns 1 if did change the directory
1725 * structure, Returns 0 if user aborted
1727 * force_single forces operation on the current entry and affects
1728 * default destination. Current filename is used as default.
1731 panel_operate (void *source_panel
, FileOperation operation
,
1734 WPanel
*panel
= source_panel
;
1735 #ifdef WITH_FULL_PATHS
1736 char *source_with_path
= NULL
;
1738 # define source_with_path source
1739 #endif /* !WITH_FULL_PATHS */
1740 char *source
= NULL
;
1742 const char *temp
= NULL
;
1743 char *save_cwd
= NULL
, *save_dest
= NULL
;
1744 int single_entry
= (get_current_type () == view_tree
)
1745 || (panel
->marked
<= 1) || force_single
;
1746 struct stat src_stat
, dst_stat
;
1754 int do_bg
= 0; /* do background operation? */
1756 free_linklist (&linklist
);
1757 free_linklist (&dest_dirs
);
1761 source
= selection (panel
)->fname
;
1762 src_stat
= selection (panel
)->st
;
1764 source
= panel_get_file (panel
, &src_stat
);
1767 if (!strcmp (source
, "..")) {
1768 message (D_ERROR
, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1773 /* Generate confirmation prompt */
1774 panel_operate_generate_prompt (panel
, operation
, source
, &src_stat
);
1776 ctx
= file_op_context_new (operation
);
1778 /* Show confirmation dialog */
1779 if (operation
== OP_DELETE
&& confirm_delete
) {
1783 i
= query_dialog (_(op_names
[operation
]), cmd_buf
, D_ERROR
, 2,
1784 _("&Yes"), _("&No"));
1787 file_op_context_destroy (ctx
);
1790 } else if (operation
!= OP_DELETE
) {
1794 /* Forced single operations default to the original name */
1797 else if (get_other_type () == view_listing
)
1798 dest_dir
= other_panel
->cwd
;
1800 dest_dir
= panel
->cwd
;
1803 * Add trailing backslash only when do non-locally ops.
1804 * It saves user from occasional file renames (when destination
1809 dest_dir_
= mhl_str_dup (dest_dir
);
1811 /* add trailing separator */
1812 if (*dest_dir
&& strcmp(&dest_dir
[strlen(dest_dir
)-1], PATH_SEP_STR
)) {
1813 dest_dir_
= mhl_str_concat (dest_dir
, PATH_SEP_STR
);
1815 dest_dir_
= mhl_str_dup (dest_dir
);
1818 file_op_context_destroy (ctx
);
1823 file_mask_dialog (ctx
, operation
, cmd_buf
, dest_dir_
,
1824 single_entry
, &do_bg
);
1828 file_op_context_destroy (ctx
);
1832 file_op_context_destroy (ctx
);
1837 #ifdef WITH_BACKGROUND
1838 /* Did the user select to do a background operation? */
1842 v
= do_background (ctx
,
1843 g_strconcat (op_names
[operation
], ": ",
1846 message (D_ERROR
, MSG_ERROR
,
1847 _(" Sorry, I could not put the job in background "));
1850 /* If we are the parent */
1852 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
1853 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
1854 /* file_op_context_destroy (ctx); */
1858 #endif /* WITH_BACKGROUND */
1860 /* Initialize things */
1861 /* We do not want to trash cache every time file is
1862 created/touched. However, this will make our cache contain
1865 if (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1866 save_dest
= g_strdup (dest
);
1869 if (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1870 save_cwd
= g_strdup (panel
->cwd
);
1873 /* Now, let's do the job */
1878 file_op_context_create_ui (ctx
, 1);
1880 /* This code is only called by the tree and panel code */
1882 /* We now have ETA in all cases */
1884 /* One file: FIXME mc_chdir will take user out of any vfs */
1885 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1886 mc_chdir (PATH_SEP_STR
);
1888 /* The source and src_stat variables have been initialized before */
1889 #ifdef WITH_FULL_PATHS
1890 source_with_path
= mhl_str_dir_plus_file (panel
->cwd
, source
);
1891 #endif /* WITH_FULL_PATHS */
1893 if (operation
== OP_DELETE
) {
1894 if (S_ISDIR (src_stat
.st_mode
))
1895 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1898 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1900 temp
= transform_source (ctx
, source_with_path
);
1903 value
= transform_error
;
1905 char *temp2
= mhl_str_dir_plus_file (dest
, temp
);
1910 switch (operation
) {
1913 * we use file_mask_op_follow_links only with OP_COPY,
1915 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1917 if (S_ISDIR (src_stat
.st_mode
))
1919 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
1920 0, 0, 0, &count
, &bytes
);
1923 copy_file_file (ctx
, source_with_path
, dest
, 1,
1928 if (S_ISDIR (src_stat
.st_mode
))
1930 move_dir_dir (ctx
, source_with_path
, dest
,
1934 move_file_file (ctx
, source_with_path
, dest
,
1939 /* Unknown file operation */
1943 } /* Copy or move operation */
1945 if ((value
== FILE_CONT
) && !force_single
)
1946 unmark_files (panel
);
1949 /* Check destination for copy or move operation */
1950 if (operation
!= OP_DELETE
) {
1951 retry_many_dst_stat
:
1952 dst_result
= mc_stat (dest
, &dst_stat
);
1953 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
1955 (_(" Destination \"%s\" must be a directory \n %s "),
1956 dest
) == FILE_RETRY
)
1957 goto retry_many_dst_stat
;
1962 /* Initialize variables for progress bars */
1963 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
1964 panel_compute_totals (panel
, &ctx
->progress_count
,
1965 &ctx
->progress_bytes
);
1966 ctx
->progress_totals_computed
= 1;
1968 ctx
->progress_totals_computed
= 0;
1969 ctx
->progress_count
= panel
->marked
;
1970 ctx
->progress_bytes
= panel
->total
;
1973 /* Loop for every file, perform the actual copy operation */
1974 for (i
= 0; i
< panel
->count
; i
++) {
1975 if (!panel
->dir
.list
[i
].f
.marked
)
1976 continue; /* Skip the unmarked ones */
1978 source
= panel
->dir
.list
[i
].fname
;
1979 src_stat
= panel
->dir
.list
[i
].st
;
1981 #ifdef WITH_FULL_PATHS
1982 g_free (source_with_path
);
1983 source_with_path
= mhl_str_dir_plus_file (panel
->cwd
, source
);
1984 #endif /* WITH_FULL_PATHS */
1986 if (operation
== OP_DELETE
) {
1987 if (S_ISDIR (src_stat
.st_mode
))
1989 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1992 erase_file (ctx
, source_with_path
, &count
, &bytes
,
1995 temp
= transform_source (ctx
, source_with_path
);
1997 value
= transform_error
;
1999 char *temp2
= mhl_str_dir_plus_file (dest
, temp
);
2001 source_with_path
= mhl_shell_unescape_buf(source_with_path
);
2002 temp2
= mhl_shell_unescape_buf(temp2
);
2004 switch (operation
) {
2007 * we use file_mask_op_follow_links only with OP_COPY
2009 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2010 if (S_ISDIR (src_stat
.st_mode
))
2012 copy_dir_dir (ctx
, source_with_path
, temp2
,
2013 1, 0, 0, 0, &count
, &bytes
);
2016 copy_file_file (ctx
, source_with_path
,
2017 temp2
, 1, &count
, &bytes
,
2019 free_linklist (&dest_dirs
);
2023 if (S_ISDIR (src_stat
.st_mode
))
2025 move_dir_dir (ctx
, source_with_path
, temp2
,
2029 move_file_file (ctx
, source_with_path
,
2030 temp2
, &count
, &bytes
);
2034 /* Unknown file operation */
2039 } /* Copy or move operation */
2041 if (value
== FILE_ABORT
)
2044 if (value
== FILE_CONT
)
2045 do_file_mark (panel
, i
, 0);
2047 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2052 && file_progress_show_bytes (ctx
, bytes
,
2053 ctx
->progress_bytes
) ==
2057 if (operation
!= OP_DELETE
&& verbose
2058 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2062 } /* Loop for every file */
2063 } /* Many entries */
2068 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2072 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2076 free_linklist (&linklist
);
2077 free_linklist (&dest_dirs
);
2078 #ifdef WITH_FULL_PATHS
2079 g_free (source_with_path
);
2080 #endif /* WITH_FULL_PATHS */
2082 g_free (ctx
->dest_mask
);
2083 ctx
->dest_mask
= NULL
;
2084 #ifdef WITH_BACKGROUND
2085 /* Let our parent know we are saying bye bye */
2086 if (we_are_background
) {
2090 #endif /* WITH_BACKGROUND */
2092 file_op_context_destroy (ctx
);
2098 /* {{{ Query/status report routines */
2101 real_do_file_error (enum OperationMode mode
, const char *error
)
2106 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2108 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2126 /* Report error with one file */
2128 file_error (const char *format
, const char *file
)
2130 snprintf (cmd_buf
, sizeof (cmd_buf
), format
,
2131 path_trunc (file
, 30), unix_error_string (errno
));
2133 return do_file_error (cmd_buf
);
2136 /* Report error with two files */
2138 files_error (const char *format
, const char *file1
, const char *file2
)
2143 strcpy (nfile1
, path_trunc (file1
, 15));
2144 strcpy (nfile2
, path_trunc (file2
, 15));
2146 snprintf (cmd_buf
, sizeof (cmd_buf
), format
, nfile1
, nfile2
,
2147 unix_error_string (errno
));
2149 return do_file_error (cmd_buf
);
2153 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, const char *s
)
2157 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2161 _("\n Directory not empty. \n"
2162 " Delete it recursively? ")
2163 : _("\n Background process: Directory not empty \n"
2164 " Delete it recursively? ");
2165 text
= g_strconcat (_(" Delete: "), path_trunc (s
, 30), " ", (char *) NULL
);
2169 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2170 _("&Yes"), _("&No"),
2171 _("A&ll"), _("Non&e"),
2174 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2179 switch (ctx
->recursive_result
) {
2181 case RECURSIVE_ALWAYS
:
2185 case RECURSIVE_NEVER
:
2188 case RECURSIVE_ABORT
:
2195 #ifdef WITH_BACKGROUND
2197 do_file_error (const char *str
)
2199 if (we_are_background
)
2200 return parent_call (real_do_file_error
, NULL
, 1, strlen (str
),
2203 return real_do_file_error (Foreground
, str
);
2207 query_recursive (FileOpContext
*ctx
, const char *s
)
2209 if (we_are_background
)
2210 return parent_call (real_query_recursive
, ctx
, 1, strlen (s
), s
);
2212 return real_query_recursive (ctx
, Foreground
, s
);
2216 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2217 struct stat
*_d_stat
)
2219 if (we_are_background
)
2220 return parent_call ((void *) file_progress_real_query_replace
,
2223 strlen (destname
), destname
,
2224 sizeof (struct stat
), _s_stat
,
2225 sizeof (struct stat
), _d_stat
);
2227 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2233 do_file_error (const char *str
)
2235 return real_do_file_error (Foreground
, str
);
2239 query_recursive (FileOpContext
*ctx
, const char *s
)
2241 return real_query_recursive (ctx
, Foreground
, s
);
2245 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2246 struct stat
*_d_stat
)
2248 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2252 #endif /* !WITH_BACKGROUND */
2255 Cause emacs to enter folding mode for this file: