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
41 * \brief Source: file management
44 /* {{{ Include files */
53 #include <sys/types.h>
61 #include "../src/tty/tty.h"
62 #include "../src/tty/key.h"
64 #include "../src/search/search.h"
69 #include "main.h" /* cmd_buf */
73 #include "background.h" /* we_are_background */
74 #include "../src/strescape.h"
77 /* Needed for current_panel, other_panel and WTree */
83 #include "../vfs/vfs-impl.h"
87 /* Hack: the vfs code should not rely on this */
88 #define WITH_FULL_PATHS 1
93 * Whether the Midnight Commander tries to provide more
94 * information about copy/move sizes and bytes transfered
95 * at the expense of some speed
97 int file_op_compute_totals
= 1;
99 /* This is a hard link cache */
102 struct vfs_class
*vfs
;
110 /* the hard link cache */
111 static struct link
*linklist
= NULL
;
113 /* the files-to-be-erased list */
114 static struct link
*erase_list
;
117 * In copy_dir_dir we use two additional single linked lists: The first -
118 * variable name `parent_dirs' - holds information about already copied
119 * directories and is used to detect cyclic symbolic links.
120 * The second (`dest_dirs' below) holds information about just created
121 * target directories and is used to detect when an directory is copied
122 * into itself (we don't want to copy infinitly).
123 * Both lists don't use the linkcount and name structure members of struct
126 static struct link
*dest_dirs
= 0;
128 const char *op_names
[3] = {
136 static FileProgressStatus
query_replace (FileOpContext
* ctx
, const char *destname
,
137 struct stat
*_s_stat
, struct stat
*_d_stat
);
138 static FileProgressStatus
query_recursive (FileOpContext
* ctx
, const char *s
);
139 static FileProgressStatus
do_file_error (const char *str
);
140 static FileProgressStatus
erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
);
141 static FileProgressStatus
erase_file (FileOpContext
*ctx
, const char *s
,
142 off_t
*progress_count
, double *progress_bytes
,
143 int is_toplevel_file
);
144 static FileProgressStatus
files_error (const char *format
, const char *file1
,
148 enum CaseConvs
{ NO_CONV
= 0, UP_CHAR
= 1, LOW_CHAR
= 2, UP_SECT
=
151 static FileProgressStatus transform_error
= FILE_CONT
;
154 transform_source (FileOpContext
*ctx
, const char *source
)
156 char *s
= g_strdup (source
);
158 char *fnsource
= (char*)x_basename (s
);
160 /* We remove \n from the filename since regex routines would use \n as an anchor */
161 /* this is just to be allowed to maniupulate file names with \n on it */
162 for (q
= s
; *q
; q
++) {
167 str_fix_string (fnsource
);
169 if ( ! mc_search_run(ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
) ){
170 transform_error
= FILE_SKIP
;
175 return mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
179 free_linklist (struct link
**linklist
)
181 struct link
*lp
, *lp2
;
183 for (lp
= *linklist
; lp
!= NULL
; lp
= lp2
) {
191 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
193 ino_t ino
= sb
->st_ino
;
194 dev_t dev
= sb
->st_dev
;
196 struct vfs_class
*vfs
= vfs_get_class (path
);
203 if (lp
->ino
== ino
&& lp
->dev
== dev
)
211 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
212 * and a hardlink was succesfully made
215 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
218 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
219 ino_t ino
= pstat
->st_ino
;
220 dev_t dev
= pstat
->st_dev
;
221 struct stat link_stat
;
224 if (vfs_file_class_flags (src_name
) & VFSF_NOLINKS
)
227 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
228 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
) {
229 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
230 && link_stat
.st_dev
== dev
231 && vfs_get_class (lp
->name
) == my_vfs
) {
232 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
234 if (vfs_get_class (dst_name
) == vfs_get_class (p
)) {
235 if (!mc_stat (p
, &link_stat
)) {
236 if (!mc_link (p
, dst_name
))
241 message (D_ERROR
, MSG_ERROR
, _(" Cannot make the hardlink "));
244 lp
= (struct link
*) g_malloc (sizeof (struct link
) + strlen (src_name
)
245 + strlen (dst_name
) + 1);
251 strcpy (lp
->name
, src_name
);
252 lpdstname
= lp
->name
+ strlen(lp
->name
) + 1;
253 strcpy (lpdstname
, dst_name
);
261 * Duplicate the contents of the symbolic link src_path in dst_path.
262 * Try to make a stable symlink if the option "stable symlink" was
263 * set in the file mask dialog.
264 * If dst_path is an existing symlink it will be deleted silently
265 * (upper levels take already care of existing files at dst_path).
267 static FileProgressStatus
268 make_symlink (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
)
270 char link_target
[MC_MAXPATHLEN
];
272 FileProgressStatus return_status
;
276 if (mc_lstat (dst_path
, &sb
) == 0 && S_ISLNK (sb
.st_mode
))
282 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
285 file_error (_(" Cannot read source link \"%s\" \n %s "),
287 if (return_status
== FILE_RETRY
)
288 goto retry_src_readlink
;
289 return return_status
;
291 link_target
[len
] = 0;
293 if (ctx
->stable_symlinks
)
294 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
295 message (D_ERROR
, MSG_ERROR
,
296 _(" Cannot make stable symlinks across "
297 "non-local filesystems: \n\n"
298 " Option Stable Symlinks will be disabled "));
299 ctx
->stable_symlinks
= 0;
302 if (ctx
->stable_symlinks
&& *link_target
!= PATH_SEP
) {
305 const char *r
= strrchr (src_path
, PATH_SEP
);
308 p
= g_strndup (src_path
, r
- src_path
+ 1);
309 if (*dst_path
== PATH_SEP
)
310 q
= g_strdup (dst_path
);
312 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
313 s
= strrchr (q
, PATH_SEP
);
316 s
= g_strconcat (p
, link_target
, (char *) NULL
);
318 g_strlcpy (link_target
, s
, sizeof (link_target
));
320 s
= diff_two_paths (q
, link_target
);
322 g_strlcpy (link_target
, s
, sizeof (link_target
));
331 if (mc_symlink (link_target
, dst_path
) == 0)
335 * if dst_exists, it is obvious that this had failed.
336 * We can delete the old symlink and try again...
338 if (dst_is_symlink
) {
339 if (!mc_unlink (dst_path
))
340 if (mc_symlink (link_target
, dst_path
) == 0)
345 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
347 if (return_status
== FILE_RETRY
)
348 goto retry_dst_symlink
;
349 return return_status
;
353 progress_update_one (FileOpContext
*ctx
,
354 off_t
*progress_count
,
355 double *progress_bytes
, off_t add
, int is_toplevel_file
)
359 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
361 (*progress_bytes
) += add
;
364 /* Apply some heuristic here to not call the update stuff very often */
366 file_progress_show_count (ctx
, *progress_count
,
367 ctx
->progress_count
);
368 if (ret
!= FILE_CONT
)
371 file_progress_show_bytes (ctx
, *progress_bytes
,
372 ctx
->progress_bytes
);
377 /* Status of the destination file */
379 DEST_NONE
, /* Not created */
380 DEST_SHORT
, /* Created, not fully copied */
381 DEST_FULL
/* Created, fully copied */
384 static FileProgressStatus
385 warn_same_file (const char *fmt
, const char *a
, const char *b
)
389 msg
= g_strdup_printf (fmt
, a
, b
);
390 result
= query_dialog (MSG_ERROR
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
393 if ( result
) { /* 1 == Abort */
401 copy_file_file (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
,
402 int ask_overwrite
, off_t
*progress_count
,
403 double *progress_bytes
, int is_toplevel_file
)
405 uid_t src_uid
= (uid_t
) - 1;
406 gid_t src_gid
= (gid_t
) - 1;
409 int buf_size
= BUF_8K
;
410 int src_desc
, dest_desc
= -1;
411 int n_read
, n_written
;
412 mode_t src_mode
= 0; /* The mode of the source file */
415 int dst_exists
= 0, appending
= 0;
416 off_t n_read_total
= 0, file_size
= -1;
417 FileProgressStatus return_status
, temp_status
;
418 struct timeval tv_transfer_start
;
419 int dst_status
= DEST_NONE
; /* 1 if the file is not fully copied */
422 /* FIXME: We should not be using global variables! */
424 return_status
= FILE_RETRY
;
426 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
427 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
432 while (mc_stat (dst_path
, &sb2
) == 0) {
433 if (S_ISDIR (sb2
.st_mode
)) {
435 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
437 if (return_status
== FILE_RETRY
)
439 return return_status
;
445 while ((*ctx
->stat_func
) (src_path
, &sb
)) {
447 file_error (_(" Cannot stat source file \"%s\" \n %s "),
449 if (return_status
!= FILE_RETRY
)
450 return return_status
;
454 /* Destination already exists */
455 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
456 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "),
458 /* Should we replace destination? */
461 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
462 if (return_status
!= FILE_CONT
)
463 return return_status
;
467 if (!ctx
->do_append
) {
468 /* Check the hardlinks */
469 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
470 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
471 /* We have made a hardlink - no more processing is necessary */
475 if (S_ISLNK (sb
.st_mode
))
476 return make_symlink (ctx
, src_path
, dst_path
);
478 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
479 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) ||
480 S_ISSOCK (sb
.st_mode
)) {
481 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
483 return_status
= file_error (
484 _(" Cannot create special file \"%s\" \n %s "), dst_path
);
485 if (return_status
== FILE_RETRY
)
487 return return_status
;
491 while (ctx
->preserve_uidgid
492 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
493 temp_status
= file_error (
494 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
495 if (temp_status
== FILE_RETRY
)
499 while (ctx
->preserve
&&
500 mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
)) {
501 temp_status
= file_error (
502 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
503 if (temp_status
== FILE_RETRY
)
511 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
513 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
514 return_status
= file_error (
515 _(" Cannot open source file \"%s\" \n %s "), src_path
);
516 if (return_status
== FILE_RETRY
)
519 return return_status
;
523 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
) {
524 message (D_ERROR
, _("Warning"),
525 _(" Reget failed, about to overwrite file "));
526 ctx
->do_reget
= ctx
->do_append
= 0;
530 while (mc_fstat (src_desc
, &sb
)) {
531 return_status
= file_error (
532 _(" Cannot fstat source file \"%s\" \n %s "), src_path
);
533 if (return_status
== FILE_RETRY
)
538 src_mode
= sb
.st_mode
;
541 utb
.actime
= sb
.st_atime
;
542 utb
.modtime
= sb
.st_mtime
;
543 file_size
= sb
.st_size
;
545 open_flags
= O_WRONLY
;
546 if (dst_exists
!= 0) {
547 if (ctx
->do_append
!= 0)
548 open_flags
|= O_APPEND
;
550 open_flags
|= O_CREAT
| O_TRUNC
;
552 open_flags
|= O_CREAT
| O_EXCL
;
554 while ((dest_desc
= mc_open (dst_path
, open_flags
, src_mode
)) < 0) {
555 if (errno
== EEXIST
) {
558 return_status
= file_error (
559 _(" Cannot create target file \"%s\" \n %s "), dst_path
);
560 if (return_status
== FILE_RETRY
)
565 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
567 appending
= ctx
->do_append
;
570 /* Find out the optimal buffer size. */
571 while (mc_fstat (dest_desc
, &sb
)) {
572 return_status
= file_error (
573 _(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
574 if (return_status
== FILE_RETRY
)
578 buf
= g_malloc (buf_size
);
583 return_status
= file_progress_show (ctx
, 0, file_size
);
587 if (return_status
!= FILE_CONT
)
591 struct timeval tv_current
, tv_last_update
, tv_last_input
;
592 int secs
, update_secs
;
594 const char *stalled_msg
;
596 tv_last_update
= tv_transfer_start
;
600 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
603 while ((n_read
= mc_read (src_desc
, buf
, buf_size
)) < 0) {
604 return_status
= file_error (
605 _(" Cannot read source file \"%s\" \n %s "), src_path
);
606 if (return_status
== FILE_RETRY
)
613 gettimeofday (&tv_current
, NULL
);
617 n_read_total
+= n_read
;
619 /* Windows NT ftp servers report that files have no
620 * permissions: -------, so if we happen to have actually
621 * read something, we should fix the permissions.
623 if (!(src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)))
624 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
625 gettimeofday (&tv_last_input
, NULL
);
629 mc_write (dest_desc
, t
, n_read
)) < n_read
) {
636 file_error (_(" Cannot write target file \"%s\" \n %s "),
638 if (return_status
!= FILE_RETRY
)
643 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
644 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
647 tv_last_update
= tv_current
;
650 /* 2. Check for a stalled condition */
651 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
653 if (update_secs
> 4) {
654 stalled_msg
= _("(stalled)");
659 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
663 ((dt
/ (double) n_read_total
) * file_size
) - dt
;
664 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
669 /* 4. Compute BPS rate */
672 (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
673 if (ctx
->bps_time
< 1)
675 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
678 file_progress_set_stalled_label (ctx
, stalled_msg
);
679 return_status
= file_progress_show_bytes (ctx
, *progress_bytes
+
680 n_read_total
+ ctx
->do_reget
, ctx
->progress_bytes
);
681 if (return_status
== FILE_CONT
) {
683 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
);
686 if (return_status
!= FILE_CONT
)
691 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
696 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
697 temp_status
= file_error (
698 _(" Cannot close source file \"%s\" \n %s "), src_path
);
699 if (temp_status
== FILE_RETRY
)
701 if (temp_status
== FILE_ABORT
)
702 return_status
= temp_status
;
706 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
707 temp_status
= file_error (
708 _(" Cannot close target file \"%s\" \n %s "), dst_path
);
709 if (temp_status
== FILE_RETRY
)
711 return_status
= temp_status
;
715 if (dst_status
== DEST_SHORT
) {
716 /* Remove short file */
718 result
= query_dialog (_("Copy"),
719 _("Incomplete file was retrieved. Keep it?"),
720 D_ERROR
, 2, _("&Delete"), _("&Keep"));
722 mc_unlink (dst_path
);
723 } else if (dst_status
== DEST_FULL
) {
724 /* Copy has succeeded */
725 if (!appending
&& ctx
->preserve_uidgid
) {
726 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
727 temp_status
= file_error (
728 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
729 if (temp_status
== FILE_RETRY
)
731 return_status
= temp_status
;
738 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
))) {
739 temp_status
= file_error (
740 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
741 if (temp_status
!= FILE_RETRY
) {
742 return_status
= temp_status
;
747 src_mode
= umask(-1);
749 src_mode
= 0100666 & ~src_mode
;
750 mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
));
752 mc_utime (dst_path
, &utb
);
756 if (return_status
== FILE_CONT
)
758 progress_update_one (ctx
, progress_count
, progress_bytes
,
759 file_size
, is_toplevel_file
);
761 return return_status
;
765 * I think these copy_*_* functions should have a return type.
766 * anyway, this function *must* have two directories as arguments.
768 /* FIXME: This function needs to check the return values of the
771 copy_dir_dir (FileOpContext
*ctx
, const char *s
, const char *_d
, int toplevel
,
772 int move_over
, int delete, struct link
*parent_dirs
,
773 off_t
*progress_count
, double *progress_bytes
)
776 struct stat buf
, cbuf
;
778 char *dest_dir
= NULL
;
779 FileProgressStatus return_status
= FILE_CONT
;
784 d
= strutils_shell_unescape (_d
);
786 /* First get the mode of the source dir */
788 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
790 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
791 if (return_status
== FILE_RETRY
)
796 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
797 /* Don't copy a directory we created before (we don't want to copy
798 infinitely if a directory is copied into itself) */
799 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
800 return_status
= FILE_CONT
;
804 /* Hmm, hardlink to directory??? - Norbert */
805 /* FIXME: In this step we should do something
806 in case the destination already exist */
807 /* Check the hardlinks */
808 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
809 && check_hardlinks (s
, d
, &cbuf
) == 1) {
810 /* We have made a hardlink - no more processing is necessary */
814 if (!S_ISDIR (cbuf
.st_mode
)) {
816 file_error (_(" Source \"%s\" is not a directory \n %s "), s
);
817 if (return_status
== FILE_RETRY
)
822 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
823 /* we found a cyclic symbolic link */
824 message (D_ERROR
, MSG_ERROR
,
825 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
826 return_status
= FILE_SKIP
;
830 lp
= g_new (struct link
, 1);
831 lp
->vfs
= vfs_get_class (s
);
832 lp
->ino
= cbuf
.st_ino
;
833 lp
->dev
= cbuf
.st_dev
;
834 lp
->next
= parent_dirs
;
838 /* Now, check if the dest dir exists, if not, create it. */
839 if (mc_stat (d
, &buf
)) {
840 /* Here the dir doesn't exist : make it ! */
842 if (mc_rename (s
, d
) == 0) {
843 return_status
= FILE_CONT
;
851 * If the destination directory exists, we want to copy the whole
852 * directory, but we only want this to happen once.
854 * Escape sequences added to the * to compiler warnings.
855 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
856 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
858 if (!S_ISDIR (buf
.st_mode
)) {
859 return_status
= file_error(
860 _(" Destination \"%s\" must be a directory \n %s "), d
);
861 if (return_status
== FILE_RETRY
)
865 /* Dive into subdir if exists */
866 if (toplevel
&& ctx
->dive_into_subdirs
) {
867 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
874 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
875 return_status
= file_error (
876 _(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
877 if (return_status
!= FILE_RETRY
)
881 lp
= g_new (struct link
, 1);
882 mc_stat (dest_dir
, &buf
);
883 lp
->vfs
= vfs_get_class (dest_dir
);
884 lp
->ino
= buf
.st_ino
;
885 lp
->dev
= buf
.st_dev
;
886 lp
->next
= dest_dirs
;
889 if (ctx
->preserve_uidgid
) {
890 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
891 return_status
= file_error (
892 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
893 if (return_status
!= FILE_RETRY
)
899 /* open the source dir for reading */
900 reading
= mc_opendir (s
);
904 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
907 * Now, we don't want '.' and '..' to be created / copied at any time
909 if (!strcmp (next
->d_name
, "."))
911 if (!strcmp (next
->d_name
, ".."))
914 /* get the filename and add it to the src directory */
915 path
= concat_dir_and_file (s
, next
->d_name
);
917 (*ctx
->stat_func
) (path
, &buf
);
918 if (S_ISDIR (buf
.st_mode
)) {
921 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
923 * From here, we just intend to recursively copy subdirs, not
924 * the double functionality of copying different when the target
925 * dir already exists. So, we give the recursive call the flag 0
926 * meaning no toplevel.
928 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0, delete,
929 parent_dirs
, progress_count
, progress_bytes
);
934 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
935 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
936 progress_count
, progress_bytes
, 0);
939 if (delete && return_status
== FILE_CONT
) {
940 if (ctx
->erase_at_end
) {
941 static struct link
*tail
;
942 size_t len
= strlen (path
);
943 lp
= g_malloc (sizeof (struct link
) + len
);
944 strncpy (lp
->name
, path
, len
+ 1);
945 lp
->st_mode
= buf
.st_mode
;
947 if (erase_list
!= NULL
) {
951 erase_list
= tail
= lp
;
953 if (S_ISDIR (buf
.st_mode
)) {
954 return_status
= erase_dir_iff_empty (ctx
, path
);
956 return_status
= erase_file (ctx
, path
, 0, 0, 0);
961 mc_closedir (reading
);
964 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
965 utb
.actime
= cbuf
.st_atime
;
966 utb
.modtime
= cbuf
.st_mtime
;
967 mc_utime (dest_dir
, &utb
);
969 cbuf
.st_mode
= umask(-1);
971 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
972 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
977 g_free (parent_dirs
);
980 return return_status
;
985 /* {{{ Move routines */
987 static FileProgressStatus
988 move_file_file (FileOpContext
*ctx
, const char *s
, const char *d
,
989 off_t
*progress_count
, double *progress_bytes
)
991 struct stat src_stats
, dst_stats
;
992 FileProgressStatus return_status
= FILE_CONT
;
993 gboolean copy_done
= FALSE
;
995 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
996 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1001 while (mc_lstat (s
, &src_stats
) != 0) {
1002 /* Source doesn't exist */
1004 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1005 if (return_status
!= FILE_RETRY
)
1006 return return_status
;
1009 if (mc_lstat (d
, &dst_stats
) == 0) {
1010 if (src_stats
.st_dev
== dst_stats
.st_dev
1011 && src_stats
.st_ino
== dst_stats
.st_ino
)
1012 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "), s
, d
);
1014 if (S_ISDIR (dst_stats
.st_mode
)) {
1015 message (D_ERROR
, MSG_ERROR
,
1016 _(" Cannot overwrite directory `%s' "), d
);
1021 if (confirm_overwrite
) {
1022 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1023 if (return_status
!= FILE_CONT
)
1024 return return_status
;
1026 /* Ok to overwrite */
1029 if (!ctx
->do_append
) {
1030 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1031 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1032 goto retry_src_remove
;
1034 return return_status
;
1037 if (mc_rename (s
, d
) == 0) {
1038 return progress_update_one (ctx
, progress_count
,
1040 src_stats
.st_size
, 1);
1044 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1045 one nfs to the same, but on the server it is on two different
1046 filesystems. Then nfs returns EIO instead of EXDEV.
1047 Hope it will not hurt if we always in case of error try to copy/delete. */
1049 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1051 if (errno
!= EXDEV
) {
1053 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1055 if (return_status
== FILE_RETRY
)
1057 return return_status
;
1061 /* Failed because filesystem boundary -> copy the file instead */
1063 copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1064 if (return_status
!= FILE_CONT
)
1065 return return_status
;
1069 if ((return_status
=
1070 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1071 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1072 return return_status
;
1077 if (mc_unlink (s
)) {
1079 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1080 if (return_status
== FILE_RETRY
)
1081 goto retry_src_remove
;
1082 return return_status
;
1086 return_status
= progress_update_one (ctx
,
1089 src_stats
.st_size
, 1);
1092 return return_status
;
1096 move_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
,
1097 off_t
*progress_count
, double *progress_bytes
)
1099 struct stat sbuf
, dbuf
, destbuf
;
1102 FileProgressStatus return_status
;
1103 gboolean move_over
= FALSE
;
1106 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1107 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1113 dstat_ok
= (mc_stat (d
, &dbuf
) == 0);
1115 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
1116 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same directory "), s
, d
);
1119 destdir
= g_strdup (d
); /* destination doesn't exist */
1120 else if (!ctx
->dive_into_subdirs
) {
1121 destdir
= g_strdup (d
);
1124 destdir
= concat_dir_and_file (d
, x_basename (s
));
1126 /* Check if the user inputted an existing dir */
1128 if (!mc_stat (destdir
, &destbuf
)) {
1130 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1131 progress_count
, progress_bytes
);
1133 if (return_status
!= FILE_CONT
)
1137 if (S_ISDIR (destbuf
.st_mode
))
1140 (" Cannot overwrite directory \"%s\" %s "),
1144 file_error (_(" Cannot overwrite file \"%s\" %s "),
1146 if (return_status
== FILE_RETRY
)
1147 goto retry_dst_stat
;
1150 return return_status
;
1154 if (mc_rename (s
, destdir
) == 0) {
1155 return_status
= FILE_CONT
;
1159 if (errno
!= EXDEV
) {
1162 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1164 if (return_status
== FILE_RETRY
)
1168 /* Failed because of filesystem boundary -> copy dir instead */
1170 copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
,
1173 if (return_status
!= FILE_CONT
)
1176 if ((return_status
=
1177 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1178 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1182 if (ctx
->erase_at_end
) {
1183 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1184 if (S_ISDIR (erase_list
->st_mode
)) {
1186 erase_dir_iff_empty (ctx
, erase_list
->name
);
1189 erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1191 erase_list
= erase_list
->next
;
1195 erase_dir_iff_empty (ctx
, s
);
1199 while (erase_list
) {
1201 erase_list
= erase_list
->next
;
1204 return return_status
;
1209 /* {{{ Erase routines */
1210 /* Don't update progress status if progress_count==NULL */
1211 static FileProgressStatus
1212 erase_file (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1213 double *progress_bytes
, int is_toplevel_file
)
1218 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1222 if (progress_count
&& mc_lstat (s
, &buf
)) {
1223 /* ignore, most likely the mc_unlink fails, too */
1227 while (mc_unlink (s
)) {
1229 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1230 if (return_status
!= FILE_RETRY
)
1231 return return_status
;
1235 return progress_update_one (ctx
, progress_count
, progress_bytes
,
1236 buf
.st_size
, is_toplevel_file
);
1241 static FileProgressStatus
1242 recursive_erase (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1243 double *progress_bytes
)
1245 struct dirent
*next
;
1249 FileProgressStatus return_status
= FILE_CONT
;
1251 if (!strcmp (s
, ".."))
1254 reading
= mc_opendir (s
);
1259 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1260 if (!strcmp (next
->d_name
, "."))
1262 if (!strcmp (next
->d_name
, ".."))
1264 path
= concat_dir_and_file (s
, next
->d_name
);
1265 if (mc_lstat (path
, &buf
)) {
1267 mc_closedir (reading
);
1270 if (S_ISDIR (buf
.st_mode
))
1273 (ctx
, path
, progress_count
, progress_bytes
)
1274 != FILE_CONT
) ? FILE_RETRY
: FILE_CONT
;
1277 erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1280 mc_closedir (reading
);
1281 if (return_status
!= FILE_CONT
)
1282 return return_status
;
1283 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1287 while (my_rmdir (s
)) {
1289 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1290 if (return_status
!= FILE_RETRY
)
1291 return return_status
;
1297 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1298 in the directory path points to, 0 else. */
1300 check_dir_is_empty (const char *path
)
1306 dir
= mc_opendir (path
);
1310 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1311 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1312 (d
->d_name
[1] == '.'
1313 && d
->d_name
[2] == '\0')))
1314 continue; /* "." or ".." */
1324 erase_dir (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1325 double *progress_bytes
)
1327 FileProgressStatus error
;
1329 if (strcmp (s
, "..") == 0)
1332 if (strcmp (s
, ".") == 0)
1335 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1339 /* The old way to detect a non empty directory was:
1340 error = my_rmdir (s);
1341 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1342 For the linux user space nfs server (nfs-server-2.2beta29-2)
1343 we would have to check also for EIO. I hope the new way is
1344 fool proof. (Norbert)
1346 error
= check_dir_is_empty (s
);
1347 if (error
== 0) { /* not empty */
1348 error
= query_recursive (ctx
, s
);
1349 if (error
== FILE_CONT
)
1350 return recursive_erase (ctx
, s
, progress_count
,
1356 while (my_rmdir (s
) == -1) {
1358 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1359 if (error
!= FILE_RETRY
)
1366 static FileProgressStatus
1367 erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
)
1369 FileProgressStatus error
;
1371 if (strcmp (s
, "..") == 0)
1374 if (strcmp (s
, ".") == 0)
1377 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1381 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1384 while (my_rmdir (s
)) {
1386 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1387 if (error
!= FILE_RETRY
)
1396 /* {{{ Panel operate routines */
1399 * Return currently selected entry name or the name of the first marked
1400 * entry if there is one.
1403 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1407 if (get_current_type () == view_tree
) {
1408 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1409 char *tree_name
= tree_selected_name (tree
);
1411 mc_stat (tree_name
, stat_buf
);
1415 if (panel
->marked
) {
1416 for (i
= 0; i
< panel
->count
; i
++)
1417 if (panel
->dir
.list
[i
].f
.marked
) {
1418 *stat_buf
= panel
->dir
.list
[i
].st
;
1419 return panel
->dir
.list
[i
].fname
;
1422 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1423 return panel
->dir
.list
[panel
->selected
].fname
;
1425 g_assert_not_reached ();
1431 compute_dir_size_create_ui (void)
1433 ComputeDirSizeUI
*ui
;
1435 const char *b_name
= N_("&Abort");
1441 ui
= g_new (ComputeDirSizeUI
, 1);
1443 ui
->dlg
= create_dlg (0, 0, 8, COLS
/2, dialog_colors
, NULL
,
1444 NULL
, _("Directory scanning"), DLG_CENTER
);
1445 ui
->dirname
= label_new (3, 3, "");
1446 add_widget (ui
->dlg
, ui
->dirname
);
1448 add_widget (ui
->dlg
,
1449 button_new (5, (ui
->dlg
->cols
- strlen (b_name
))/2,
1450 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
1452 /* We will manage the dialog without any help,
1453 that's why we have to call init_dlg */
1460 compute_dir_size_destroy_ui (ComputeDirSizeUI
*ui
)
1463 /* schedule to update passive panel */
1464 other_panel
->dirty
= 1;
1466 /* close and destroy dialog */
1467 dlg_run_done (ui
->dlg
);
1468 destroy_dlg (ui
->dlg
);
1474 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
1476 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
1483 label_set_text (this->dirname
, name_trunc (dirname
, this->dlg
->cols
- 6));
1485 event
.x
= -1; /* Don't show the GPM cursor */
1486 c
= tty_get_event (&event
, FALSE
, FALSE
);
1490 /* Reinitialize to avoid old values after events other than
1491 selecting a button */
1492 this->dlg
->ret_value
= FILE_CONT
;
1494 dlg_process_event (this->dlg
, c
, &event
);
1496 switch (this->dlg
->ret_value
) {
1508 * Computes the number of bytes used by the files in a directory
1511 compute_dir_size (const char *dirname
, const void *ui
,
1512 compute_dir_size_callback cback
,
1513 off_t
*ret_marked
, double *ret_total
)
1516 struct dirent
*dirent
;
1517 FileProgressStatus ret
= FILE_CONT
;
1519 dir
= mc_opendir (dirname
);
1524 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1529 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
1531 if (ret
!= FILE_CONT
)
1534 if (strcmp (dirent
->d_name
, ".") == 0)
1536 if (strcmp (dirent
->d_name
, "..") == 0)
1539 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1540 res
= mc_lstat (fullname
, &s
);
1547 if (S_ISDIR (s
.st_mode
)) {
1548 off_t subdir_count
= 0;
1549 double subdir_bytes
= 0;
1551 ret
= compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
);
1553 if (ret
!= FILE_CONT
) {
1558 *ret_marked
+= subdir_count
;
1559 *ret_total
+= subdir_bytes
;
1562 *ret_total
+= s
.st_size
;
1574 * panel_compute_totals:
1576 * compute the number of files and the number of bytes
1577 * used up by the whole selection, recursing directories
1578 * as required. In addition, it checks to see if it will
1579 * overwrite any files by doing the copy.
1581 static FileProgressStatus
1582 panel_compute_totals (WPanel
*panel
, const void *ui
,
1583 compute_dir_size_callback cback
,
1584 off_t
*ret_marked
, double *ret_total
)
1591 for (i
= 0; i
< panel
->count
; i
++) {
1594 if (!panel
->dir
.list
[i
].f
.marked
)
1597 s
= &panel
->dir
.list
[i
].st
;
1599 if (S_ISDIR (s
->st_mode
)) {
1601 off_t subdir_count
= 0;
1602 double subdir_bytes
= 0;
1603 FileProgressStatus status
;
1606 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1608 status
= compute_dir_size (dir_name
, ui
, cback
,
1609 &subdir_count
, &subdir_bytes
);
1612 if (status
!= FILE_CONT
)
1615 *ret_marked
+= subdir_count
;
1616 *ret_total
+= subdir_bytes
;
1619 *ret_total
+= s
->st_size
;
1627 * This array introduced to avoid translation problems. The former (op_names)
1628 * is assumed to be nouns, suitable in dialog box titles; this one should
1629 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1630 * Notice first symbol - it is to fool gettext and force these strings to
1631 * be different for it. First symbol is skipped while building a prompt.
1632 * (I don't use spaces around the words, because someday they could be
1633 * dropped, when widgets get smarter)
1635 static const char *op_names1
[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1638 int fmd_xlen
= FMD_XLEN
;
1641 * These are formats for building a prompt. Parts encoded as follows:
1642 * %o - operation from op_names1
1643 * %f - file/files or files/directories, as appropriate
1644 * %m - "with source mask" or question mark for delete
1645 * %s - source name (truncated)
1646 * %d - number of marked files
1647 * %e - "to:" or question mark for delete
1649 * xgettext:no-c-format */
1650 static const char *one_format
= N_("%o %f \"%s\"%m");
1651 /* xgettext:no-c-format */
1652 static const char *many_format
= N_("%o %d %f%m");
1653 static const char *prompt_parts
[] = {
1654 N_("file"), N_("files"), N_("directory"), N_("directories"),
1655 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1659 * Generate user prompt for panel operation.
1660 * single_source is the name if the source entry or NULL for multiple
1662 * src_stat is only used when single_source is not NULL.
1665 panel_operate_generate_prompt (const WPanel
*panel
, const int operation
,
1666 const char *single_source
,
1667 const struct stat
*src_stat
)
1669 register const char *sp
, *cp
;
1671 char format_string
[BUF_MEDIUM
];
1672 char *dp
= format_string
;
1675 static int i18n_flag
= 0;
1677 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1678 op_names1
[i
] = _(op_names1
[i
]);
1680 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1681 prompt_parts
[i
] = _(prompt_parts
[i
]);
1683 one_format
= _(one_format
);
1684 many_format
= _(many_format
);
1687 #endif /* ENABLE_NLS */
1689 sp
= single_source
? one_format
: many_format
;
1697 cp
= op_names1
[operation
] + 1;
1700 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1703 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[6];
1706 if (single_source
) {
1707 cp
= S_ISDIR (src_stat
->
1708 st_mode
) ? prompt_parts
[2] :
1711 cp
= (panel
->marked
== panel
->dirs_marked
)
1713 : (panel
->dirs_marked
? prompt_parts
[4]
1732 if (single_source
) {
1733 i
= fmd_xlen
- str_term_width1 (format_string
) - 4;
1734 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1735 str_trunc (single_source
, i
));
1737 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1739 i
= str_term_width1 (cmd_buf
) + 6 - fmd_xlen
;
1749 * Performs one of the operations on the selection on the source_panel
1750 * (copy, delete, move).
1752 * Returns 1 if did change the directory
1753 * structure, Returns 0 if user aborted
1755 * force_single forces operation on the current entry and affects
1756 * default destination. Current filename is used as default.
1759 panel_operate (void *source_panel
, FileOperation operation
,
1762 WPanel
*panel
= source_panel
;
1763 #ifdef WITH_FULL_PATHS
1764 char *source_with_path
= NULL
;
1766 # define source_with_path source
1767 #endif /* !WITH_FULL_PATHS */
1768 char *source
= NULL
;
1771 char *save_cwd
= NULL
, *save_dest
= NULL
;
1772 int single_entry
= (get_current_type () == view_tree
)
1773 || (panel
->marked
<= 1) || force_single
;
1774 struct stat src_stat
, dst_stat
;
1776 FileProgressStatus value
;
1783 int do_bg
= 0; /* do background operation? */
1785 free_linklist (&linklist
);
1786 free_linklist (&dest_dirs
);
1788 /* Update panel contents to avoid actions on deleted files */
1789 if (!panel
->is_panelized
) {
1790 update_panels (UP_RELOAD
, UP_KEEPSEL
);
1796 source
= selection (panel
)->fname
;
1797 src_stat
= selection (panel
)->st
;
1799 source
= panel_get_file (panel
, &src_stat
);
1802 if (!strcmp (source
, "..")) {
1803 message (D_ERROR
, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1808 /* Generate confirmation prompt */
1809 panel_operate_generate_prompt (panel
, operation
, source
, &src_stat
);
1811 ctx
= file_op_context_new (operation
);
1813 /* Show confirmation dialog */
1814 if (operation
== OP_DELETE
&& confirm_delete
) {
1818 i
= query_dialog (_(op_names
[operation
]), cmd_buf
, D_ERROR
, 2,
1819 _("&Yes"), _("&No"));
1822 file_op_context_destroy (ctx
);
1825 } else if (operation
!= OP_DELETE
) {
1829 /* Forced single operations default to the original name */
1832 else if (get_other_type () == view_listing
)
1833 dest_dir
= other_panel
->cwd
;
1835 dest_dir
= panel
->cwd
;
1837 * Add trailing backslash only when do non-local ops.
1838 * It saves user from occasional file renames (when destination
1843 && dest_dir
[strlen(dest_dir
)-1] != PATH_SEP
) {
1844 /* add trailing separator */
1845 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char*)0);
1848 dest_dir_
= g_strdup (dest_dir
);
1851 file_op_context_destroy (ctx
);
1856 file_mask_dialog (ctx
, operation
, cmd_buf
, dest_dir_
,
1857 single_entry
, &do_bg
);
1860 if (!dest
|| !dest
[0]) {
1861 file_op_context_destroy (ctx
);
1866 #ifdef WITH_BACKGROUND
1867 /* Did the user select to do a background operation? */
1871 v
= do_background (ctx
,
1872 g_strconcat (op_names
[operation
], ": ",
1875 message (D_ERROR
, MSG_ERROR
,
1876 _(" Sorry, I could not put the job in background "));
1879 /* If we are the parent */
1881 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
1882 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
1883 /* file_op_context_destroy (ctx); */
1887 #endif /* WITH_BACKGROUND */
1889 /* Initialize things */
1890 /* We do not want to trash cache every time file is
1891 created/touched. However, this will make our cache contain
1894 if (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1895 save_dest
= g_strdup (dest
);
1898 if (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1899 save_cwd
= g_strdup (panel
->cwd
);
1902 /* Now, let's do the job */
1907 file_op_context_create_ui (ctx
, 1);
1909 /* This code is only called by the tree and panel code */
1911 /* We now have ETA in all cases */
1913 /* One file: FIXME mc_chdir will take user out of any vfs */
1914 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1915 mc_chdir (PATH_SEP_STR
);
1917 /* The source and src_stat variables have been initialized before */
1918 #ifdef WITH_FULL_PATHS
1919 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1920 #endif /* WITH_FULL_PATHS */
1922 if (operation
== OP_DELETE
) {
1923 if (S_ISDIR (src_stat
.st_mode
))
1924 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1927 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1929 temp
= transform_source (ctx
, source_with_path
);
1931 value
= transform_error
;
1933 char *repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
1934 char *temp2
= concat_dir_and_file (repl_dest
, temp
);
1940 switch (operation
) {
1943 * we use file_mask_op_follow_links only with OP_COPY,
1945 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1947 if (S_ISDIR (src_stat
.st_mode
)) {
1949 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
1950 0, 0, 0, &count
, &bytes
);
1953 copy_file_file (ctx
, source_with_path
, dest
, 1,
1959 if (S_ISDIR (src_stat
.st_mode
))
1961 move_dir_dir (ctx
, source_with_path
, dest
,
1965 move_file_file (ctx
, source_with_path
, dest
,
1970 /* Unknown file operation */
1974 } /* Copy or move operation */
1976 if ((value
== FILE_CONT
) && !force_single
)
1977 unmark_files (panel
);
1980 /* Check destination for copy or move operation */
1981 if (operation
!= OP_DELETE
) {
1982 retry_many_dst_stat
:
1983 dst_result
= mc_stat (dest
, &dst_stat
);
1984 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
1986 (_(" Destination \"%s\" must be a directory \n %s "),
1987 dest
) == FILE_RETRY
)
1988 goto retry_many_dst_stat
;
1993 /* Initialize variables for progress bars */
1994 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
1995 ComputeDirSizeUI
*ui
;
1996 FileProgressStatus status
;
1998 ui
= compute_dir_size_create_ui ();
1999 status
= panel_compute_totals (panel
,
2000 ui
, compute_dir_size_update_ui
,
2001 &ctx
->progress_count
, &ctx
->progress_bytes
);
2002 compute_dir_size_destroy_ui (ui
);
2004 if (status
!= FILE_CONT
)
2007 ctx
->progress_totals_computed
= 1;
2009 ctx
->progress_totals_computed
= 0;
2010 ctx
->progress_count
= panel
->marked
;
2011 ctx
->progress_bytes
= panel
->total
;
2014 /* Loop for every file, perform the actual copy operation */
2015 for (i
= 0; i
< panel
->count
; i
++) {
2016 if (!panel
->dir
.list
[i
].f
.marked
)
2017 continue; /* Skip the unmarked ones */
2019 source
= panel
->dir
.list
[i
].fname
;
2020 src_stat
= panel
->dir
.list
[i
].st
;
2022 #ifdef WITH_FULL_PATHS
2023 g_free (source_with_path
);
2024 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2025 #endif /* WITH_FULL_PATHS */
2027 if (operation
== OP_DELETE
) {
2028 if (S_ISDIR (src_stat
.st_mode
))
2030 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
2033 erase_file (ctx
, source_with_path
, &count
, &bytes
,
2036 temp
= transform_source (ctx
, source_with_path
);
2038 value
= transform_error
;
2041 char *repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2042 char *temp2
= concat_dir_and_file (repl_dest
, temp
);
2046 temp3
= source_with_path
;
2047 source_with_path
= strutils_shell_unescape(source_with_path
);
2050 temp2
= strutils_shell_unescape(temp2
);
2053 switch (operation
) {
2056 * we use file_mask_op_follow_links only with OP_COPY
2058 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2059 if (S_ISDIR (src_stat
.st_mode
))
2061 copy_dir_dir (ctx
, source_with_path
, temp2
,
2062 1, 0, 0, 0, &count
, &bytes
);
2065 copy_file_file (ctx
, source_with_path
,
2066 temp2
, 1, &count
, &bytes
,
2068 free_linklist (&dest_dirs
);
2072 if (S_ISDIR (src_stat
.st_mode
))
2074 move_dir_dir (ctx
, source_with_path
, temp2
,
2078 move_file_file (ctx
, source_with_path
,
2079 temp2
, &count
, &bytes
);
2083 /* Unknown file operation */
2088 } /* Copy or move operation */
2090 if (value
== FILE_ABORT
)
2093 if (value
== FILE_CONT
)
2094 do_file_mark (panel
, i
, 0);
2096 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2101 && file_progress_show_bytes (ctx
, bytes
,
2102 ctx
->progress_bytes
) ==
2106 if (operation
!= OP_DELETE
&& verbose
2107 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2111 } /* Loop for every file */
2112 } /* Many entries */
2117 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2121 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2125 free_linklist (&linklist
);
2126 free_linklist (&dest_dirs
);
2127 #ifdef WITH_FULL_PATHS
2128 g_free (source_with_path
);
2129 #endif /* WITH_FULL_PATHS */
2131 g_free (ctx
->dest_mask
);
2132 ctx
->dest_mask
= NULL
;
2133 #ifdef WITH_BACKGROUND
2134 /* Let our parent know we are saying bye bye */
2135 if (we_are_background
) {
2139 #endif /* WITH_BACKGROUND */
2141 file_op_context_destroy (ctx
);
2147 /* {{{ Query/status report routines */
2149 static FileProgressStatus
2150 real_do_file_error (enum OperationMode mode
, const char *error
)
2155 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2157 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2175 /* Report error with one file */
2177 file_error (const char *format
, const char *file
)
2179 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
,
2180 path_trunc (file
, 30), unix_error_string (errno
));
2182 return do_file_error (cmd_buf
);
2185 /* Report error with two files */
2186 static FileProgressStatus
2187 files_error (const char *format
, const char *file1
, const char *file2
)
2189 char *nfile1
= g_strdup (path_trunc (file1
, 15));
2190 char *nfile2
= g_strdup (path_trunc (file2
, 15));
2192 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
, nfile1
, nfile2
,
2193 unix_error_string (errno
));
2198 return do_file_error (cmd_buf
);
2201 static FileProgressStatus
2202 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, const char *s
)
2206 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2210 _("\n Directory not empty. \n"
2211 " Delete it recursively? ")
2212 : _("\n Background process: Directory not empty \n"
2213 " Delete it recursively? ");
2214 text
= g_strconcat (_(" Delete: "), path_trunc (s
, 30), " ", (char *) NULL
);
2218 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2219 _("&Yes"), _("&No"),
2220 _("A&ll"), _("Non&e"),
2223 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2228 switch (ctx
->recursive_result
) {
2230 case RECURSIVE_ALWAYS
:
2234 case RECURSIVE_NEVER
:
2237 case RECURSIVE_ABORT
:
2244 #ifdef WITH_BACKGROUND
2245 static FileProgressStatus
2246 do_file_error (const char *str
)
2250 FileProgressStatus (*f
) (enum OperationMode
, const char *);
2252 pntr
.f
= real_do_file_error
;
2254 if (we_are_background
)
2255 return parent_call (pntr
.p
, NULL
, 1, strlen (str
),
2258 return real_do_file_error (Foreground
, str
);
2261 static FileProgressStatus
2262 query_recursive (FileOpContext
*ctx
, const char *s
)
2266 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
2268 pntr
.f
= real_query_recursive
;
2270 if (we_are_background
)
2271 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
2273 return real_query_recursive (ctx
, Foreground
, s
);
2276 static FileProgressStatus
2277 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2278 struct stat
*_d_stat
)
2282 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
2283 struct stat
*, struct stat
*);
2285 pntr
.f
= file_progress_real_query_replace
;
2287 if (we_are_background
)
2288 return parent_call (pntr
.p
,
2291 strlen (destname
), destname
,
2292 sizeof (struct stat
), _s_stat
,
2293 sizeof (struct stat
), _d_stat
);
2295 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2300 static FileProgressStatus
2301 do_file_error (const char *str
)
2303 return real_do_file_error (Foreground
, str
);
2306 static FileProgressStatus
2307 query_recursive (FileOpContext
*ctx
, const char *s
)
2309 return real_query_recursive (ctx
, Foreground
, s
);
2312 static FileProgressStatus
2313 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2314 struct stat
*_d_stat
)
2316 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2320 #endif /* !WITH_BACKGROUND */
2323 Cause emacs to enter folding mode for this file: