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>
61 #include "main.h" /* cmd_buf */
65 #include "background.h" /* we_are_background */
68 /* Needed for current_panel, other_panel and WTree */
75 #include "../vfs/vfs-impl.h"
79 /* Hack: the vfs code should not rely on this */
80 #define WITH_FULL_PATHS 1
85 * Whether the Midnight Commander tries to provide more
86 * information about copy/move sizes and bytes transfered
87 * at the expense of some speed
89 int file_op_compute_totals
= 1;
91 /* This is a hard link cache */
94 struct vfs_class
*vfs
;
102 /* the hard link cache */
103 static struct link
*linklist
= NULL
;
105 /* the files-to-be-erased list */
106 static struct link
*erase_list
;
109 * In copy_dir_dir we use two additional single linked lists: The first -
110 * variable name `parent_dirs' - holds information about already copied
111 * directories and is used to detect cyclic symbolic links.
112 * The second (`dest_dirs' below) holds information about just created
113 * target directories and is used to detect when an directory is copied
114 * into itself (we don't want to copy infinitly).
115 * Both lists don't use the linkcount and name structure members of struct
118 static struct link
*dest_dirs
= 0;
120 const char *op_names
[3] = {
128 static int query_replace (FileOpContext
* ctx
, const char *destname
,
129 struct stat
*_s_stat
, struct stat
*_d_stat
);
130 static int query_recursive (FileOpContext
* ctx
, const char *s
);
131 static int do_file_error (const char *str
);
132 static int erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
);
133 static int erase_file (FileOpContext
*ctx
, const char *s
,
134 off_t
*progress_count
, double *progress_bytes
,
135 int is_toplevel_file
);
136 static int files_error (const char *format
, const char *file1
,
140 enum CaseConvs
{ NO_CONV
= 0, UP_CHAR
= 1, LOW_CHAR
= 2, UP_SECT
=
144 convert_case (char c
, enum CaseConvs
*conversion
)
146 if (*conversion
& UP_CHAR
) {
147 *conversion
&= ~UP_CHAR
;
148 return toupper ((unsigned char) c
);
149 } else if (*conversion
& LOW_CHAR
) {
150 *conversion
&= ~LOW_CHAR
;
151 return tolower ((unsigned char) c
);
152 } else if (*conversion
& UP_SECT
) {
153 return toupper ((unsigned char) c
);
154 } else if (*conversion
& LOW_SECT
) {
155 return tolower ((unsigned char) c
);
160 static int transform_error
= 0;
163 do_transform_source (FileOpContext
*ctx
, const char *source
)
166 const char *fnsource
= x_basename (source
);
168 enum CaseConvs case_conv
= NO_CONV
;
169 static char fntarget
[MC_MAXPATHLEN
];
171 len
= strlen (fnsource
);
172 j
= re_match (&ctx
->rx
, fnsource
, len
, 0, &ctx
->regs
);
174 transform_error
= FILE_SKIP
;
177 for (next_reg
= 1, j
= 0, k
= 0; j
< strlen (ctx
->dest_mask
); j
++) {
178 switch (ctx
->dest_mask
[j
]) {
181 if (!isdigit ((unsigned char) ctx
->dest_mask
[j
])) {
182 /* Backslash followed by non-digit */
183 switch (ctx
->dest_mask
[j
]) {
185 case_conv
|= UP_SECT
;
186 case_conv
&= ~LOW_SECT
;
189 case_conv
|= UP_CHAR
;
192 case_conv
|= LOW_SECT
;
193 case_conv
&= ~UP_SECT
;
196 case_conv
|= LOW_CHAR
;
202 /* Backslash as quote mark */
204 convert_case (ctx
->dest_mask
[j
], &case_conv
);
208 /* Backslash followed by digit */
209 next_reg
= ctx
->dest_mask
[j
] - '0';
214 if (next_reg
< 0 || next_reg
>= RE_NREGS
215 || ctx
->regs
.start
[next_reg
] < 0) {
216 message (1, MSG_ERROR
, _(" Invalid target mask "));
217 transform_error
= FILE_ABORT
;
220 for (l
= (size_t) ctx
->regs
.start
[next_reg
];
221 l
< (size_t) ctx
->regs
.end
[next_reg
]; l
++)
222 fntarget
[k
++] = convert_case (fnsource
[l
], &case_conv
);
227 fntarget
[k
++] = convert_case (ctx
->dest_mask
[j
], &case_conv
);
236 transform_source (FileOpContext
*ctx
, const char *source
)
238 char *s
= g_strdup (source
);
242 /* We remove \n from the filename since regex routines would use \n as an anchor */
243 /* this is just to be allowed to maniupulate file names with \n on it */
244 for (q
= s
; *q
; q
++) {
248 p
= do_transform_source (ctx
, s
);
254 free_linklist (struct link
**linklist
)
256 struct link
*lp
, *lp2
;
258 for (lp
= *linklist
; lp
!= NULL
; lp
= lp2
) {
266 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
268 ino_t ino
= sb
->st_ino
;
269 dev_t dev
= sb
->st_dev
;
271 struct vfs_class
*vfs
= vfs_get_class (path
);
278 if (lp
->ino
== ino
&& lp
->dev
== dev
)
286 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
287 * and a hardlink was succesfully made
290 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
293 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
294 ino_t ino
= pstat
->st_ino
;
295 dev_t dev
= pstat
->st_dev
;
296 struct stat link_stat
;
299 if (vfs_file_class_flags (src_name
) & VFSF_NOLINKS
)
302 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
303 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
) {
304 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
305 && link_stat
.st_dev
== dev
306 && vfs_get_class (lp
->name
) == my_vfs
) {
307 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
309 if (vfs_get_class (dst_name
) == vfs_get_class (p
)) {
310 if (!mc_stat (p
, &link_stat
)) {
311 if (!mc_link (p
, dst_name
))
316 message (1, MSG_ERROR
, _(" Cannot make the hardlink "));
319 lp
= (struct link
*) g_malloc (sizeof (struct link
) + strlen (src_name
)
320 + strlen (dst_name
) + 1);
326 strcpy (lp
->name
, src_name
);
327 lpdstname
= lp
->name
+ strlen(lp
->name
) + 1;
328 strcpy (lpdstname
, dst_name
);
336 * Duplicate the contents of the symbolic link src_path in dst_path.
337 * Try to make a stable symlink if the option "stable symlink" was
338 * set in the file mask dialog.
339 * If dst_path is an existing symlink it will be deleted silently
340 * (upper levels take already care of existing files at dst_path).
343 make_symlink (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
)
345 char link_target
[MC_MAXPATHLEN
];
351 if (mc_lstat (dst_path
, &sb
) == 0 && S_ISLNK (sb
.st_mode
))
357 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
360 file_error (_(" Cannot read source link \"%s\" \n %s "),
362 if (return_status
== FILE_RETRY
)
363 goto retry_src_readlink
;
364 return return_status
;
366 link_target
[len
] = 0;
368 if (ctx
->stable_symlinks
)
369 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
370 message (1, MSG_ERROR
,
371 _(" Cannot make stable symlinks across "
372 "non-local filesystems: \n\n"
373 " Option Stable Symlinks will be disabled "));
374 ctx
->stable_symlinks
= 0;
377 if (ctx
->stable_symlinks
&& *link_target
!= PATH_SEP
) {
380 const char *r
= strrchr (src_path
, PATH_SEP
);
383 p
= g_strndup (src_path
, r
- src_path
+ 1);
384 if (*dst_path
== PATH_SEP
)
385 q
= g_strdup (dst_path
);
387 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
388 s
= strrchr (q
, PATH_SEP
);
391 s
= g_strconcat (p
, link_target
, (char *) NULL
);
393 g_strlcpy (link_target
, s
, sizeof (link_target
));
395 s
= diff_two_paths (q
, link_target
);
397 g_strlcpy (link_target
, s
, sizeof (link_target
));
406 if (mc_symlink (link_target
, dst_path
) == 0)
410 * if dst_exists, it is obvious that this had failed.
411 * We can delete the old symlink and try again...
413 if (dst_is_symlink
) {
414 if (!mc_unlink (dst_path
))
415 if (mc_symlink (link_target
, dst_path
) == 0)
420 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
422 if (return_status
== FILE_RETRY
)
423 goto retry_dst_symlink
;
424 return return_status
;
428 progress_update_one (FileOpContext
*ctx
,
429 off_t
*progress_count
,
430 double *progress_bytes
, off_t add
, int is_toplevel_file
)
434 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
436 (*progress_bytes
) += add
;
439 /* Apply some heuristic here to not call the update stuff very often */
441 file_progress_show_count (ctx
, *progress_count
,
442 ctx
->progress_count
);
443 if (ret
!= FILE_CONT
)
446 file_progress_show_bytes (ctx
, *progress_bytes
,
447 ctx
->progress_bytes
);
452 /* Status of the destination file */
454 DEST_NONE
, /* Not created */
455 DEST_SHORT
, /* Created, not fully copied */
456 DEST_FULL
/* Created, fully copied */
460 copy_file_file (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
,
461 int ask_overwrite
, off_t
*progress_count
,
462 double *progress_bytes
, int is_toplevel_file
)
464 uid_t src_uid
= (uid_t
) - 1;
465 gid_t src_gid
= (gid_t
) - 1;
468 int buf_size
= BUF_8K
;
469 int src_desc
, dest_desc
= -1;
470 int n_read
, n_written
;
471 mode_t src_mode
= 0; /* The mode of the source file */
474 int dst_exists
= 0, appending
= 0;
475 off_t n_read_total
= 0, file_size
= -1;
476 int return_status
, temp_status
;
477 struct timeval tv_transfer_start
;
478 int dst_status
= DEST_NONE
; /* 1 if the file is not fully copied */
480 /* FIXME: We should not be using global variables! */
482 return_status
= FILE_RETRY
;
484 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
485 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
490 while (mc_stat (dst_path
, &sb2
) == 0) {
491 if (S_ISDIR (sb2
.st_mode
)) {
493 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
495 if (return_status
== FILE_RETRY
)
497 return return_status
;
503 while ((*ctx
->stat_func
) (src_path
, &sb
)) {
505 file_error (_(" Cannot stat source file \"%s\" \n %s "),
507 if (return_status
!= FILE_RETRY
)
508 return return_status
;
512 /* Destination already exists */
513 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
) {
514 message (1, MSG_ERROR
,
515 _(" `%s' and `%s' are the same file "), src_path
, dst_path
);
520 /* Should we replace destination? */
523 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
524 if (return_status
!= FILE_CONT
)
525 return return_status
;
529 if (!ctx
->do_append
) {
530 /* Check the hardlinks */
531 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
532 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
533 /* We have made a hardlink - no more processing is necessary */
537 if (S_ISLNK (sb
.st_mode
)) {
540 retval
= make_symlink (ctx
, src_path
, dst_path
);
544 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
545 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) ||
546 S_ISSOCK (sb
.st_mode
)) {
547 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
549 return_status
= file_error (
550 _(" Cannot create special file \"%s\" \n %s "), dst_path
);
551 if (return_status
== FILE_RETRY
)
553 return return_status
;
557 while (ctx
->preserve_uidgid
558 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
559 temp_status
= file_error (
560 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
561 if (temp_status
== FILE_RETRY
)
565 while (ctx
->preserve
&&
566 mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
)) {
567 temp_status
= file_error (
568 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
569 if (temp_status
== FILE_RETRY
)
577 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
579 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
580 return_status
= file_error (
581 _(" Cannot open source file \"%s\" \n %s "), src_path
);
582 if (return_status
== FILE_RETRY
)
585 return return_status
;
589 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
) {
590 message (1, _("Warning"),
591 _(" Reget failed, about to overwrite file "));
592 ctx
->do_reget
= ctx
->do_append
= 0;
596 while (mc_fstat (src_desc
, &sb
)) {
597 return_status
= file_error (
598 _(" Cannot fstat source file \"%s\" \n %s "), src_path
);
599 if (return_status
== FILE_RETRY
)
604 src_mode
= sb
.st_mode
;
607 utb
.actime
= sb
.st_atime
;
608 utb
.modtime
= sb
.st_mtime
;
609 file_size
= sb
.st_size
;
611 /* Create the new regular file with small permissions initially,
612 do not create a security hole. FIXME: You have security hole
613 here, btw. Imagine copying to /tmp and symlink attack :-( */
615 while ((dest_desc
= mc_open (dst_path
, O_WRONLY
| (ctx
->do_append
?
616 O_APPEND
: (O_CREAT
| O_TRUNC
)), 0600)) < 0) {
617 return_status
= file_error (
618 _(" Cannot create target file \"%s\" \n %s "), dst_path
);
619 if (return_status
== FILE_RETRY
)
624 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
626 appending
= ctx
->do_append
;
629 /* Find out the optimal buffer size. */
630 while (mc_fstat (dest_desc
, &sb
)) {
631 return_status
= file_error (
632 _(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
633 if (return_status
== FILE_RETRY
)
637 buf
= g_malloc (buf_size
);
642 return_status
= file_progress_show (ctx
, 0, file_size
);
646 if (return_status
!= FILE_CONT
)
650 struct timeval tv_current
, tv_last_update
, tv_last_input
;
651 int secs
, update_secs
;
653 const char *stalled_msg
;
655 tv_last_update
= tv_transfer_start
;
659 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
662 while ((n_read
= mc_read (src_desc
, buf
, buf_size
)) < 0) {
663 return_status
= file_error (
664 _(" Cannot read source file \"%s\" \n %s "), src_path
);
665 if (return_status
== FILE_RETRY
)
672 gettimeofday (&tv_current
, NULL
);
676 n_read_total
+= n_read
;
678 /* Windows NT ftp servers report that files have no
679 * permissions: -------, so if we happen to have actually
680 * read something, we should fix the permissions.
682 if (!(src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)))
683 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
684 gettimeofday (&tv_last_input
, NULL
);
688 mc_write (dest_desc
, t
, n_read
)) < n_read
) {
695 file_error (_(" Cannot write target file \"%s\" \n %s "),
697 if (return_status
!= FILE_RETRY
)
702 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
703 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
706 tv_last_update
= tv_current
;
709 /* 2. Check for a stalled condition */
710 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
712 if (update_secs
> 4) {
713 stalled_msg
= _("(stalled)");
718 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
722 ((dt
/ (double) n_read_total
) * file_size
) - dt
;
723 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
728 /* 4. Compute BPS rate */
731 (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
732 if (ctx
->bps_time
< 1)
734 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
737 file_progress_set_stalled_label (ctx
, stalled_msg
);
738 return_status
= file_progress_show_bytes (ctx
, *progress_bytes
+
739 n_read_total
+ ctx
->do_reget
, ctx
->progress_bytes
);
740 if (return_status
== FILE_CONT
) {
742 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
);
745 if (return_status
!= FILE_CONT
)
750 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
755 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
756 temp_status
= file_error (
757 _(" Cannot close source file \"%s\" \n %s "), src_path
);
758 if (temp_status
== FILE_RETRY
)
760 if (temp_status
== FILE_ABORT
)
761 return_status
= temp_status
;
765 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
766 temp_status
= file_error (
767 _(" Cannot close target file \"%s\" \n %s "), dst_path
);
768 if (temp_status
== FILE_RETRY
)
770 return_status
= temp_status
;
774 if (dst_status
== DEST_SHORT
) {
775 /* Remove short file */
777 result
= query_dialog (_("Copy"),
778 _("Incomplete file was retrieved. Keep it?"),
779 D_ERROR
, 2, _("&Delete"), _("&Keep"));
781 mc_unlink (dst_path
);
782 } else if (dst_status
== DEST_FULL
) {
783 /* Copy has succeeded */
784 if (!appending
&& ctx
->preserve_uidgid
) {
785 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
786 temp_status
= file_error (
787 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
788 if (temp_status
== FILE_RETRY
)
790 return_status
= temp_status
;
795 if (!appending
&& ctx
->preserve
) {
796 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
))) {
797 temp_status
= file_error (
798 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
799 if (temp_status
!= FILE_RETRY
) {
800 return_status
= temp_status
;
804 mc_utime (dst_path
, &utb
);
808 if (return_status
== FILE_CONT
)
810 progress_update_one (ctx
, progress_count
, progress_bytes
,
811 file_size
, is_toplevel_file
);
813 return return_status
;
817 * I think these copy_*_* functions should have a return type.
818 * anyway, this function *must* have two directories as arguments.
820 /* FIXME: This function needs to check the return values of the
823 copy_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
, int toplevel
,
824 int move_over
, int delete, struct link
*parent_dirs
,
825 off_t
*progress_count
, double *progress_bytes
)
828 struct stat buf
, cbuf
;
830 char *path
, *mdpath
, *dest_file
, *dest_dir
;
831 int return_status
= FILE_CONT
;
835 /* First get the mode of the source dir */
837 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
839 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
840 if (return_status
== FILE_RETRY
)
842 return return_status
;
845 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
846 /* Don't copy a directory we created before (we don't want to copy
847 infinitely if a directory is copied into itself) */
848 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
852 /* Hmm, hardlink to directory??? - Norbert */
853 /* FIXME: In this step we should do something
854 in case the destination already exist */
855 /* Check the hardlinks */
856 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
857 && check_hardlinks (s
, d
, &cbuf
) == 1) {
858 /* We have made a hardlink - no more processing is necessary */
859 return return_status
;
862 if (!S_ISDIR (cbuf
.st_mode
)) {
864 file_error (_(" Source \"%s\" is not a directory \n %s "), s
);
865 if (return_status
== FILE_RETRY
)
867 return return_status
;
870 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
871 /* we found a cyclic symbolic link */
872 message (1, MSG_ERROR
,
873 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
877 lp
= g_new (struct link
, 1);
878 lp
->vfs
= vfs_get_class (s
);
879 lp
->ino
= cbuf
.st_ino
;
880 lp
->dev
= cbuf
.st_dev
;
881 lp
->next
= parent_dirs
;
885 /* Now, check if the dest dir exists, if not, create it. */
886 if (mc_stat (d
, &buf
)) {
887 /* Here the dir doesn't exist : make it ! */
890 if (mc_rename (s
, d
) == 0) {
891 g_free (parent_dirs
);
895 dest_dir
= g_strdup (d
);
898 * If the destination directory exists, we want to copy the whole
899 * directory, but we only want this to happen once.
901 * Escape sequences added to the * to compiler warnings.
902 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
903 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
905 if (!S_ISDIR (buf
.st_mode
)) {
906 return_status
= file_error(
907 _(" Destination \"%s\" must be a directory \n %s "), d
);
908 if (return_status
== FILE_RETRY
)
910 g_free (parent_dirs
);
911 return return_status
;
913 /* Dive into subdir if exists */
914 if (toplevel
&& ctx
->dive_into_subdirs
) {
915 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
917 dest_dir
= g_strdup (d
);
921 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
922 return_status
= file_error (
923 _(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
924 if (return_status
!= FILE_RETRY
)
928 lp
= g_new (struct link
, 1);
929 mc_stat (dest_dir
, &buf
);
930 lp
->vfs
= vfs_get_class (dest_dir
);
931 lp
->ino
= buf
.st_ino
;
932 lp
->dev
= buf
.st_dev
;
933 lp
->next
= dest_dirs
;
936 if (ctx
->preserve_uidgid
) {
937 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
938 return_status
= file_error (
939 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
940 if (return_status
!= FILE_RETRY
)
946 /* open the source dir for reading */
947 if ((reading
= mc_opendir (s
)) == 0) {
951 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
953 * Now, we don't want '.' and '..' to be created / copied at any time
955 if (!strcmp (next
->d_name
, "."))
957 if (!strcmp (next
->d_name
, ".."))
960 /* get the filename and add it to the src directory */
961 path
= concat_dir_and_file (s
, next
->d_name
);
963 (*ctx
->stat_func
) (path
, &buf
);
964 if (S_ISDIR (buf
.st_mode
)) {
965 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
967 * From here, we just intend to recursively copy subdirs, not
968 * the double functionality of copying different when the target
969 * dir already exists. So, we give the recursive call the flag 0
970 * meaning no toplevel.
972 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0, delete,
973 parent_dirs
, progress_count
, progress_bytes
);
976 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
977 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
978 progress_count
, progress_bytes
, 0);
981 if (delete && return_status
== FILE_CONT
) {
982 if (ctx
->erase_at_end
) {
983 static struct link
*tail
;
984 lp
= g_malloc (sizeof (struct link
) + strlen (path
));
985 strcpy (lp
->name
, path
);
986 lp
->st_mode
= buf
.st_mode
;
992 erase_list
= tail
= lp
;
994 if (S_ISDIR (buf
.st_mode
)) {
995 return_status
= erase_dir_iff_empty (ctx
, path
);
997 return_status
= erase_file (ctx
, path
, 0, 0, 0);
1002 mc_closedir (reading
);
1004 if (ctx
->preserve
) {
1005 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1006 utb
.actime
= cbuf
.st_atime
;
1007 utb
.modtime
= cbuf
.st_mtime
;
1008 mc_utime (dest_dir
, &utb
);
1013 g_free (parent_dirs
);
1014 return return_status
;
1019 /* {{{ Move routines */
1022 move_file_file (FileOpContext
*ctx
, const char *s
, const char *d
,
1023 off_t
*progress_count
, double *progress_bytes
)
1025 struct stat src_stats
, dst_stats
;
1026 int return_status
= FILE_CONT
;
1027 gboolean copy_done
= FALSE
;
1029 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
1030 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1035 while (mc_lstat (s
, &src_stats
) != 0) {
1036 /* Source doesn't exist */
1038 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1039 if (return_status
!= FILE_RETRY
)
1040 return return_status
;
1043 if (mc_lstat (d
, &dst_stats
) == 0) {
1044 if (src_stats
.st_dev
== dst_stats
.st_dev
1045 && src_stats
.st_ino
== dst_stats
.st_ino
) {
1046 int msize
= COLS
- 36;
1047 char st
[MC_MAXPATHLEN
];
1048 char dt
[MC_MAXPATHLEN
];
1054 strcpy (st
, path_trunc (s
, msize
));
1055 strcpy (dt
, path_trunc (d
, msize
));
1056 message (1, MSG_ERROR
,
1057 _(" `%s' and `%s' are the same file "), st
, dt
);
1062 if (S_ISDIR (dst_stats
.st_mode
)) {
1063 message (1, MSG_ERROR
,
1064 _(" Cannot overwrite directory `%s' "), d
);
1069 if (confirm_overwrite
) {
1070 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1071 if (return_status
!= FILE_CONT
)
1072 return return_status
;
1074 /* Ok to overwrite */
1077 if (!ctx
->do_append
) {
1078 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1079 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1080 goto retry_src_remove
;
1082 return return_status
;
1085 if (mc_rename (s
, d
) == 0) {
1086 return progress_update_one (ctx
, progress_count
,
1088 src_stats
.st_size
, 1);
1092 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1093 one nfs to the same, but on the server it is on two different
1094 filesystems. Then nfs returns EIO instead of EXDEV.
1095 Hope it will not hurt if we always in case of error try to copy/delete. */
1097 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1099 if (errno
!= EXDEV
) {
1101 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1103 if (return_status
== FILE_RETRY
)
1105 return return_status
;
1109 /* Failed because filesystem boundary -> copy the file instead */
1111 copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1112 if (return_status
!= FILE_CONT
)
1113 return return_status
;
1117 if ((return_status
=
1118 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1119 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1120 return return_status
;
1125 if (mc_unlink (s
)) {
1127 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1128 if (return_status
== FILE_RETRY
)
1129 goto retry_src_remove
;
1130 return return_status
;
1134 return_status
= progress_update_one (ctx
,
1137 src_stats
.st_size
, 1);
1140 return return_status
;
1144 move_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
,
1145 off_t
*progress_count
, double *progress_bytes
)
1147 struct stat sbuf
, dbuf
, destbuf
;
1153 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1154 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1160 if (mc_stat (d
, &dbuf
))
1161 destdir
= g_strdup (d
); /* destination doesn't exist */
1162 else if (!ctx
->dive_into_subdirs
) {
1163 destdir
= g_strdup (d
);
1166 destdir
= concat_dir_and_file (d
, x_basename (s
));
1168 if (sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
) {
1169 int msize
= COLS
- 36;
1170 char st
[MC_MAXPATHLEN
];
1171 char dt
[MC_MAXPATHLEN
];
1177 strcpy (st
, path_trunc (s
, msize
));
1178 strcpy (dt
, path_trunc (d
, msize
));
1179 message (1, MSG_ERROR
,
1180 _(" `%s' and `%s' are the same directory "), st
, dt
);
1185 /* Check if the user inputted an existing dir */
1187 if (!mc_stat (destdir
, &destbuf
)) {
1189 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1190 progress_count
, progress_bytes
);
1192 if (return_status
!= FILE_CONT
)
1196 if (S_ISDIR (destbuf
.st_mode
))
1199 (" Cannot overwrite directory \"%s\" %s "),
1203 file_error (_(" Cannot overwrite file \"%s\" %s "),
1205 if (return_status
== FILE_RETRY
)
1206 goto retry_dst_stat
;
1209 return return_status
;
1213 if (mc_rename (s
, destdir
) == 0) {
1214 return_status
= FILE_CONT
;
1218 if (errno
!= EXDEV
) {
1221 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1223 if (return_status
== FILE_RETRY
)
1227 /* Failed because of filesystem boundary -> copy dir instead */
1229 copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
,
1232 if (return_status
!= FILE_CONT
)
1235 if ((return_status
=
1236 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1237 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1241 if (ctx
->erase_at_end
) {
1242 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1243 if (S_ISDIR (erase_list
->st_mode
)) {
1245 erase_dir_iff_empty (ctx
, erase_list
->name
);
1248 erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1250 erase_list
= erase_list
->next
;
1254 erase_dir_iff_empty (ctx
, s
);
1258 while (erase_list
) {
1260 erase_list
= erase_list
->next
;
1263 return return_status
;
1268 /* {{{ Erase routines */
1269 /* Don't update progress status if progress_count==NULL */
1271 erase_file (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1272 double *progress_bytes
, int is_toplevel_file
)
1277 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1281 if (progress_count
&& mc_lstat (s
, &buf
)) {
1282 /* ignore, most likely the mc_unlink fails, too */
1286 while (mc_unlink (s
)) {
1288 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1289 if (return_status
!= FILE_RETRY
)
1290 return return_status
;
1294 return progress_update_one (ctx
, progress_count
, progress_bytes
,
1295 buf
.st_size
, is_toplevel_file
);
1301 recursive_erase (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1302 double *progress_bytes
)
1304 struct dirent
*next
;
1308 int return_status
= FILE_CONT
;
1310 if (!strcmp (s
, ".."))
1313 reading
= mc_opendir (s
);
1318 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1319 if (!strcmp (next
->d_name
, "."))
1321 if (!strcmp (next
->d_name
, ".."))
1323 path
= concat_dir_and_file (s
, next
->d_name
);
1324 if (mc_lstat (path
, &buf
)) {
1326 mc_closedir (reading
);
1329 if (S_ISDIR (buf
.st_mode
))
1332 (ctx
, path
, progress_count
, progress_bytes
)
1336 erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1339 mc_closedir (reading
);
1340 if (return_status
!= FILE_CONT
)
1341 return return_status
;
1342 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1346 while (my_rmdir (s
)) {
1348 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1349 if (return_status
!= FILE_RETRY
)
1350 return return_status
;
1356 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1357 in the directory path points to, 0 else. */
1359 check_dir_is_empty (const char *path
)
1365 dir
= mc_opendir (path
);
1369 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1370 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1371 (d
->d_name
[1] == '.'
1372 && d
->d_name
[2] == '\0')))
1373 continue; /* "." or ".." */
1383 erase_dir (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1384 double *progress_bytes
)
1388 if (strcmp (s
, "..") == 0)
1391 if (strcmp (s
, ".") == 0)
1394 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1398 /* The old way to detect a non empty directory was:
1399 error = my_rmdir (s);
1400 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1401 For the linux user space nfs server (nfs-server-2.2beta29-2)
1402 we would have to check also for EIO. I hope the new way is
1403 fool proof. (Norbert)
1405 error
= check_dir_is_empty (s
);
1406 if (error
== 0) { /* not empty */
1407 error
= query_recursive (ctx
, s
);
1408 if (error
== FILE_CONT
)
1409 return recursive_erase (ctx
, s
, progress_count
,
1415 while (my_rmdir (s
) == -1) {
1417 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1418 if (error
!= FILE_RETRY
)
1426 erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
)
1430 if (strcmp (s
, "..") == 0)
1433 if (strcmp (s
, ".") == 0)
1436 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1440 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1443 while (my_rmdir (s
)) {
1445 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1446 if (error
!= FILE_RETRY
)
1455 /* {{{ Panel operate routines */
1458 * Return currently selected entry name or the name of the first marked
1459 * entry if there is one.
1462 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1466 if (get_current_type () == view_tree
) {
1467 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1468 char *tree_name
= tree_selected_name (tree
);
1470 mc_stat (tree_name
, stat_buf
);
1474 if (panel
->marked
) {
1475 for (i
= 0; i
< panel
->count
; i
++)
1476 if (panel
->dir
.list
[i
].f
.marked
) {
1477 *stat_buf
= panel
->dir
.list
[i
].st
;
1478 return panel
->dir
.list
[i
].fname
;
1481 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1482 return panel
->dir
.list
[panel
->selected
].fname
;
1484 g_assert_not_reached ();
1491 * Computes the number of bytes used by the files in a directory
1494 compute_dir_size (const char *dirname
, off_t
*ret_marked
, double *ret_total
)
1497 struct dirent
*dirent
;
1499 dir
= mc_opendir (dirname
);
1504 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1509 if (strcmp (dirent
->d_name
, ".") == 0)
1511 if (strcmp (dirent
->d_name
, "..") == 0)
1514 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1516 res
= mc_lstat (fullname
, &s
);
1523 if (S_ISDIR (s
.st_mode
)) {
1524 off_t subdir_count
= 0;
1525 double subdir_bytes
= 0;
1527 compute_dir_size (fullname
, &subdir_count
, &subdir_bytes
);
1529 *ret_marked
+= subdir_count
;
1530 *ret_total
+= subdir_bytes
;
1533 *ret_total
+= s
.st_size
;
1542 * panel_compute_totals:
1544 * compute the number of files and the number of bytes
1545 * used up by the whole selection, recursing directories
1546 * as required. In addition, it checks to see if it will
1547 * overwrite any files by doing the copy.
1550 panel_compute_totals (WPanel
*panel
, off_t
*ret_marked
, double *ret_total
)
1557 for (i
= 0; i
< panel
->count
; i
++) {
1560 if (!panel
->dir
.list
[i
].f
.marked
)
1563 s
= &panel
->dir
.list
[i
].st
;
1565 if (S_ISDIR (s
->st_mode
)) {
1567 off_t subdir_count
= 0;
1568 double subdir_bytes
= 0;
1571 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1572 compute_dir_size (dir_name
, &subdir_count
, &subdir_bytes
);
1574 *ret_marked
+= subdir_count
;
1575 *ret_total
+= subdir_bytes
;
1579 *ret_total
+= s
->st_size
;
1585 * This array introduced to avoid translation problems. The former (op_names)
1586 * is assumed to be nouns, suitable in dialog box titles; this one should
1587 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1588 * Notice first symbol - it is to fool gettext and force these strings to
1589 * be different for it. First symbol is skipped while building a prompt.
1590 * (I don't use spaces around the words, because someday they could be
1591 * dropped, when widgets get smarter)
1593 static const char *op_names1
[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1596 int fmd_xlen
= FMD_XLEN
;
1599 * These are formats for building a prompt. Parts encoded as follows:
1600 * %o - operation from op_names1
1601 * %f - file/files or files/directories, as appropriate
1602 * %m - "with source mask" or question mark for delete
1603 * %s - source name (truncated)
1604 * %d - number of marked files
1605 * %e - "to:" or question mark for delete
1607 * xgettext:no-c-format */
1608 static const char *one_format
= N_("%o %f \"%s\"%m");
1609 /* xgettext:no-c-format */
1610 static const char *many_format
= N_("%o %d %f%m");
1611 static const char *prompt_parts
[] = {
1612 N_("file"), N_("files"), N_("directory"), N_("directories"),
1613 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1617 * Generate user prompt for panel operation.
1618 * single_source is the name if the source entry or NULL for multiple
1620 * src_stat is only used when single_source is not NULL.
1623 panel_operate_generate_prompt (const WPanel
*panel
, const int operation
,
1624 const char *single_source
,
1625 const struct stat
*src_stat
)
1627 register const char *sp
, *cp
;
1629 char format_string
[BUF_MEDIUM
];
1630 char *dp
= format_string
;
1633 static int i18n_flag
= 0;
1635 fmd_init_i18n (FALSE
); /* to get proper fmd_xlen */
1637 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1638 op_names1
[i
] = _(op_names1
[i
]);
1640 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1641 prompt_parts
[i
] = _(prompt_parts
[i
]);
1643 one_format
= _(one_format
);
1644 many_format
= _(many_format
);
1647 #endif /* ENABLE_NLS */
1649 sp
= single_source
? one_format
: many_format
;
1657 cp
= op_names1
[operation
] + 1;
1660 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1663 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[6];
1666 if (single_source
) {
1667 cp
= S_ISDIR (src_stat
->
1668 st_mode
) ? prompt_parts
[2] :
1671 cp
= (panel
->marked
== panel
->dirs_marked
)
1673 : (panel
->dirs_marked
? prompt_parts
[4]
1692 if (single_source
) {
1693 i
= fmd_xlen
- strlen (format_string
) - 4;
1694 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1695 name_trunc (single_source
, i
));
1697 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1699 i
= strlen (cmd_buf
) + 6 - fmd_xlen
;
1702 fmd_init_i18n (TRUE
); /* to recalculate positions of child widgets */
1710 * Performs one of the operations on the selection on the source_panel
1711 * (copy, delete, move).
1713 * Returns 1 if did change the directory
1714 * structure, Returns 0 if user aborted
1716 * force_single forces operation on the current entry and affects
1717 * default destination. Current filename is used as default.
1720 panel_operate (void *source_panel
, FileOperation operation
,
1723 WPanel
*panel
= source_panel
;
1724 #ifdef WITH_FULL_PATHS
1725 char *source_with_path
= NULL
;
1727 # define source_with_path source
1728 #endif /* !WITH_FULL_PATHS */
1729 char *source
= NULL
;
1731 const char *temp
= NULL
;
1732 char *save_cwd
= NULL
, *save_dest
= NULL
;
1733 int single_entry
= (get_current_type () == view_tree
)
1734 || (panel
->marked
<= 1) || force_single
;
1735 struct stat src_stat
, dst_stat
;
1743 int do_bg
= 0; /* do background operation? */
1745 free_linklist (&linklist
);
1746 free_linklist (&dest_dirs
);
1750 source
= selection (panel
)->fname
;
1751 src_stat
= selection (panel
)->st
;
1753 source
= panel_get_file (panel
, &src_stat
);
1756 if (!strcmp (source
, "..")) {
1757 message (1, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1762 /* Generate confirmation prompt */
1763 panel_operate_generate_prompt (panel
, operation
, source
, &src_stat
);
1765 ctx
= file_op_context_new (operation
);
1767 /* Show confirmation dialog */
1768 if (operation
== OP_DELETE
&& confirm_delete
) {
1772 i
= query_dialog (_(op_names
[operation
]), cmd_buf
, D_ERROR
, 2,
1773 _("&Yes"), _("&No"));
1776 file_op_context_destroy (ctx
);
1779 } else if (operation
!= OP_DELETE
) {
1782 /* Forced single operations default to the original name */
1785 else if (get_other_type () == view_listing
)
1786 dest_dir
= other_panel
->cwd
;
1788 dest_dir
= panel
->cwd
;
1791 file_mask_dialog (ctx
, operation
, cmd_buf
, dest_dir
,
1792 single_entry
, &do_bg
);
1794 file_op_context_destroy (ctx
);
1798 file_op_context_destroy (ctx
);
1803 #ifdef WITH_BACKGROUND
1804 /* Did the user select to do a background operation? */
1808 v
= do_background (ctx
,
1809 g_strconcat (op_names
[operation
], ": ",
1812 message (1, MSG_ERROR
,
1813 _(" Sorry, I could not put the job in background "));
1816 /* If we are the parent */
1818 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
1819 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
1820 /* file_op_context_destroy (ctx); */
1824 #endif /* WITH_BACKGROUND */
1826 /* Initialize things */
1827 /* We do not want to trash cache every time file is
1828 created/touched. However, this will make our cache contain
1831 if (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1832 save_dest
= g_strdup (dest
);
1835 if (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1836 save_cwd
= g_strdup (panel
->cwd
);
1839 /* Now, let's do the job */
1844 file_op_context_create_ui (ctx
, 1);
1846 /* This code is only called by the tree and panel code */
1848 /* We now have ETA in all cases */
1850 /* One file: FIXME mc_chdir will take user out of any vfs */
1851 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1852 mc_chdir (PATH_SEP_STR
);
1854 /* The source and src_stat variables have been initialized before */
1855 #ifdef WITH_FULL_PATHS
1856 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1857 #endif /* WITH_FULL_PATHS */
1859 if (operation
== OP_DELETE
) {
1860 if (S_ISDIR (src_stat
.st_mode
))
1861 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1864 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1866 temp
= transform_source (ctx
, source_with_path
);
1869 value
= transform_error
;
1871 char *temp2
= concat_dir_and_file (dest
, temp
);
1876 source_with_path
= unescape_string(source_with_path
);
1877 dest
= unescape_string(dest
);
1878 switch (operation
) {
1881 * we use file_mask_op_follow_links only with OP_COPY,
1883 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1885 if (S_ISDIR (src_stat
.st_mode
))
1887 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
1888 0, 0, 0, &count
, &bytes
);
1891 copy_file_file (ctx
, source_with_path
, dest
, 1,
1896 if (S_ISDIR (src_stat
.st_mode
))
1898 move_dir_dir (ctx
, source_with_path
, dest
,
1902 move_file_file (ctx
, source_with_path
, dest
,
1907 /* Unknown file operation */
1911 } /* Copy or move operation */
1913 if ((value
== FILE_CONT
) && !force_single
)
1914 unmark_files (panel
);
1917 /* Check destination for copy or move operation */
1918 if (operation
!= OP_DELETE
) {
1919 retry_many_dst_stat
:
1920 dst_result
= mc_stat (dest
, &dst_stat
);
1921 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
1923 (_(" Destination \"%s\" must be a directory \n %s "),
1924 dest
) == FILE_RETRY
)
1925 goto retry_many_dst_stat
;
1930 /* Initialize variables for progress bars */
1931 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
1932 panel_compute_totals (panel
, &ctx
->progress_count
,
1933 &ctx
->progress_bytes
);
1934 ctx
->progress_totals_computed
= 1;
1936 ctx
->progress_totals_computed
= 0;
1937 ctx
->progress_count
= panel
->marked
;
1938 ctx
->progress_bytes
= panel
->total
;
1941 /* Loop for every file, perform the actual copy operation */
1942 for (i
= 0; i
< panel
->count
; i
++) {
1943 if (!panel
->dir
.list
[i
].f
.marked
)
1944 continue; /* Skip the unmarked ones */
1946 source
= panel
->dir
.list
[i
].fname
;
1947 src_stat
= panel
->dir
.list
[i
].st
;
1949 #ifdef WITH_FULL_PATHS
1950 g_free (source_with_path
);
1951 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1952 #endif /* WITH_FULL_PATHS */
1954 if (operation
== OP_DELETE
) {
1955 if (S_ISDIR (src_stat
.st_mode
))
1957 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1960 erase_file (ctx
, source_with_path
, &count
, &bytes
,
1963 temp
= transform_source (ctx
, source_with_path
);
1965 value
= transform_error
;
1967 char *temp2
= concat_dir_and_file (dest
, temp
);
1969 source_with_path
= unescape_string(source_with_path
);
1970 temp2
= unescape_string(temp2
);
1972 switch (operation
) {
1975 * we use file_mask_op_follow_links only with OP_COPY
1977 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1978 if (S_ISDIR (src_stat
.st_mode
))
1980 copy_dir_dir (ctx
, source_with_path
, temp2
,
1981 1, 0, 0, 0, &count
, &bytes
);
1984 copy_file_file (ctx
, source_with_path
,
1985 temp2
, 1, &count
, &bytes
,
1987 free_linklist (&dest_dirs
);
1991 if (S_ISDIR (src_stat
.st_mode
))
1993 move_dir_dir (ctx
, source_with_path
, temp2
,
1997 move_file_file (ctx
, source_with_path
,
1998 temp2
, &count
, &bytes
);
2002 /* Unknown file operation */
2007 } /* Copy or move operation */
2009 if (value
== FILE_ABORT
)
2012 if (value
== FILE_CONT
)
2013 do_file_mark (panel
, i
, 0);
2015 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2020 && file_progress_show_bytes (ctx
, bytes
,
2021 ctx
->progress_bytes
) ==
2025 if (operation
!= OP_DELETE
&& verbose
2026 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2030 } /* Loop for every file */
2031 } /* Many entries */
2036 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2040 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2044 free_linklist (&linklist
);
2045 free_linklist (&dest_dirs
);
2046 #ifdef WITH_FULL_PATHS
2047 g_free (source_with_path
);
2048 #endif /* WITH_FULL_PATHS */
2050 g_free (ctx
->dest_mask
);
2051 ctx
->dest_mask
= NULL
;
2052 #ifdef WITH_BACKGROUND
2053 /* Let our parent know we are saying bye bye */
2054 if (we_are_background
) {
2058 #endif /* WITH_BACKGROUND */
2060 file_op_context_destroy (ctx
);
2066 /* {{{ Query/status report routines */
2069 real_do_file_error (enum OperationMode mode
, const char *error
)
2074 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2076 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2094 /* Report error with one file */
2096 file_error (const char *format
, const char *file
)
2098 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
,
2099 path_trunc (file
, 30), unix_error_string (errno
));
2101 return do_file_error (cmd_buf
);
2104 /* Report error with two files */
2106 files_error (const char *format
, const char *file1
, const char *file2
)
2111 strcpy (nfile1
, path_trunc (file1
, 15));
2112 strcpy (nfile2
, path_trunc (file2
, 15));
2114 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
, nfile1
, nfile2
,
2115 unix_error_string (errno
));
2117 return do_file_error (cmd_buf
);
2121 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, const char *s
)
2125 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2129 _("\n Directory not empty. \n"
2130 " Delete it recursively? ")
2131 : _("\n Background process: Directory not empty \n"
2132 " Delete it recursively? ");
2133 text
= g_strconcat (_(" Delete: "), path_trunc (s
, 30), " ", (char *) NULL
);
2137 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2138 _("&Yes"), _("&No"),
2139 _("A&ll"), _("Non&e"),
2142 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2147 switch (ctx
->recursive_result
) {
2149 case RECURSIVE_ALWAYS
:
2153 case RECURSIVE_NEVER
:
2156 case RECURSIVE_ABORT
:
2163 #ifdef WITH_BACKGROUND
2165 do_file_error (const char *str
)
2167 if (we_are_background
)
2168 return parent_call (real_do_file_error
, NULL
, 1, strlen (str
),
2171 return real_do_file_error (Foreground
, str
);
2175 query_recursive (FileOpContext
*ctx
, const char *s
)
2177 if (we_are_background
)
2178 return parent_call (real_query_recursive
, ctx
, 1, strlen (s
), s
);
2180 return real_query_recursive (ctx
, Foreground
, s
);
2184 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2185 struct stat
*_d_stat
)
2187 if (we_are_background
)
2188 return parent_call ((void *) file_progress_real_query_replace
,
2191 strlen (destname
), destname
,
2192 sizeof (struct stat
), _s_stat
,
2193 sizeof (struct stat
), _d_stat
);
2195 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2201 do_file_error (const char *str
)
2203 return real_do_file_error (Foreground
, str
);
2207 query_recursive (FileOpContext
*ctx
, const char *s
)
2209 return real_query_recursive (ctx
, Foreground
, s
);
2213 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2214 struct stat
*_d_stat
)
2216 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2220 #endif /* !WITH_BACKGROUND */
2223 Cause emacs to enter folding mode for this file: