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"
73 #include "background.h" /* we_are_background */
74 #include "../src/strescape.h"
77 /* Needed for current_panel, other_panel and WTree */
83 #include "../../lib/vfs/mc-vfs/vfs-impl.h"
84 #include "../../lib/vfs/mc-vfs/vfs.h"
88 /* Hack: the vfs code should not rely on this */
89 #define WITH_FULL_PATHS 1
94 * Whether the Midnight Commander tries to provide more
95 * information about copy/move sizes and bytes transfered
96 * at the expense of some speed
98 int file_op_compute_totals
= 1;
100 /* This is a hard link cache */
103 struct vfs_class
*vfs
;
111 /* the hard link cache */
112 static struct link
*linklist
= NULL
;
114 /* the files-to-be-erased list */
115 static struct link
*erase_list
;
118 * In copy_dir_dir we use two additional single linked lists: The first -
119 * variable name `parent_dirs' - holds information about already copied
120 * directories and is used to detect cyclic symbolic links.
121 * The second (`dest_dirs' below) holds information about just created
122 * target directories and is used to detect when an directory is copied
123 * into itself (we don't want to copy infinitly).
124 * Both lists don't use the linkcount and name structure members of struct
127 static struct link
*dest_dirs
= NULL
;
129 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
130 const char *op_names
[3] = {
131 N_("DialogTitle|Copy"),
132 N_("DialogTitle|Move"),
133 N_("DialogTitle|Delete")
138 static FileProgressStatus
query_replace (FileOpContext
* ctx
, const char *destname
,
139 struct stat
*_s_stat
, struct stat
*_d_stat
);
140 static FileProgressStatus
query_recursive (FileOpContext
* ctx
, const char *s
);
141 static FileProgressStatus
do_file_error (const char *str
);
142 static FileProgressStatus
erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
);
143 static FileProgressStatus
erase_file (FileOpContext
*ctx
, const char *s
,
144 off_t
*progress_count
, double *progress_bytes
,
145 int is_toplevel_file
);
146 static FileProgressStatus
files_error (const char *format
, const char *file1
,
149 static FileProgressStatus transform_error
= FILE_CONT
;
152 transform_source (FileOpContext
*ctx
, const char *source
)
157 s
= g_strdup (source
);
159 /* We remove \n from the filename since regex routines would use \n as an anchor */
160 /* this is just to be allowed to maniupulate file names with \n on it */
161 for (q
= s
; *q
!= '\0'; q
++)
165 fnsource
= (char *) x_basename (s
);
167 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
168 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
171 transform_error
= FILE_SKIP
;
179 free_linklist (struct link
**lc_linklist
)
181 struct link
*lp
, *lp2
;
183 for (lp
= *lc_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
);
197 #endif /* ENABLE_VFS */
204 #endif /* ENABLE_VFS */
205 if (lp
->ino
== ino
&& lp
->dev
== dev
)
213 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
214 * and a hardlink was succesfully made
217 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
220 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
221 ino_t ino
= pstat
->st_ino
;
222 dev_t dev
= pstat
->st_dev
;
223 struct stat link_stat
;
226 if (vfs_file_class_flags (src_name
) & VFSF_NOLINKS
)
229 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
230 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
) {
231 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
232 && link_stat
.st_dev
== dev
233 && vfs_get_class (lp
->name
) == my_vfs
) {
234 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
236 if (vfs_get_class (dst_name
) == vfs_get_class (p
)) {
237 if (!mc_stat (p
, &link_stat
)) {
238 if (!mc_link (p
, dst_name
))
243 message (D_ERROR
, MSG_ERROR
, _(" Cannot make the hardlink "));
246 lp
= (struct link
*) g_try_malloc (sizeof (struct link
) + strlen (src_name
)
247 + strlen (dst_name
) + 1);
253 strcpy (lp
->name
, src_name
);
254 lpdstname
= lp
->name
+ strlen(lp
->name
) + 1;
255 strcpy (lpdstname
, dst_name
);
263 * Duplicate the contents of the symbolic link src_path in dst_path.
264 * Try to make a stable symlink if the option "stable symlink" was
265 * set in the file mask dialog.
266 * If dst_path is an existing symlink it will be deleted silently
267 * (upper levels take already care of existing files at dst_path).
269 static FileProgressStatus
270 make_symlink (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
)
272 char link_target
[MC_MAXPATHLEN
];
274 FileProgressStatus return_status
;
278 if (mc_lstat (dst_path
, &sb
) == 0 && S_ISLNK (sb
.st_mode
))
284 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
287 file_error (_(" Cannot read source link \"%s\" \n %s "),
289 if (return_status
== FILE_RETRY
)
290 goto retry_src_readlink
;
291 return return_status
;
293 link_target
[len
] = 0;
295 if (ctx
->stable_symlinks
)
296 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
297 message (D_ERROR
, MSG_ERROR
,
298 _(" Cannot make stable symlinks across "
299 "non-local filesystems: \n\n"
300 " Option Stable Symlinks will be disabled "));
301 ctx
->stable_symlinks
= 0;
304 if (ctx
->stable_symlinks
&& *link_target
!= PATH_SEP
) {
307 const char *r
= strrchr (src_path
, PATH_SEP
);
310 p
= g_strndup (src_path
, r
- src_path
+ 1);
311 if (*dst_path
== PATH_SEP
)
312 q
= g_strdup (dst_path
);
314 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
315 s
= strrchr (q
, PATH_SEP
);
318 s
= g_strconcat (p
, link_target
, (char *) NULL
);
320 g_strlcpy (link_target
, s
, sizeof (link_target
));
322 s
= diff_two_paths (q
, link_target
);
324 g_strlcpy (link_target
, s
, sizeof (link_target
));
333 if (mc_symlink (link_target
, dst_path
) == 0)
337 * if dst_exists, it is obvious that this had failed.
338 * We can delete the old symlink and try again...
340 if (dst_is_symlink
) {
341 if (!mc_unlink (dst_path
))
342 if (mc_symlink (link_target
, dst_path
) == 0)
347 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
349 if (return_status
== FILE_RETRY
)
350 goto retry_dst_symlink
;
351 return return_status
;
355 progress_update_one (FileOpContext
*ctx
,
356 off_t
*progress_count
,
357 double *progress_bytes
, off_t add
, int is_toplevel_file
)
361 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
363 (*progress_bytes
) += add
;
366 /* Apply some heuristic here to not call the update stuff very often */
368 file_progress_show_count (ctx
, *progress_count
,
369 ctx
->progress_count
);
370 if (ret
!= FILE_CONT
)
373 file_progress_show_bytes (ctx
, *progress_bytes
,
374 ctx
->progress_bytes
);
379 /* Status of the destination file */
381 DEST_NONE
, /* Not created */
382 DEST_SHORT
, /* Created, not fully copied */
383 DEST_FULL
/* Created, fully copied */
386 static FileProgressStatus
387 real_warn_same_file (enum OperationMode mode
, const char *fmt
,
388 const char *a
, const char *b
)
392 const char *head_msg
;
394 head_msg
= mode
== Foreground
? MSG_ERROR
:
395 _(" Background process error ");
397 msg
= g_strdup_printf (fmt
, a
, b
);
398 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
401 if ( result
) { /* 1 == Abort */
408 #ifdef WITH_BACKGROUND
409 static FileProgressStatus
410 warn_same_file (const char *fmt
, const char *a
, const char *b
)
414 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
415 const char *a
, const char *b
);
417 pntr
.f
= real_warn_same_file
;
419 if (we_are_background
)
420 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
),
421 fmt
, strlen(a
), a
, strlen(b
), b
);
423 return real_warn_same_file (Foreground
, fmt
, a
, b
);
426 static FileProgressStatus
427 warn_same_file (const char *fmt
, const char *a
, const char *b
)
429 return real_warn_same_file (Foreground
, fmt
, a
, b
);
434 copy_file_file (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
,
435 int ask_overwrite
, off_t
*progress_count
,
436 double *progress_bytes
, int is_toplevel_file
)
438 uid_t src_uid
= (uid_t
) - 1;
439 gid_t src_gid
= (gid_t
) - 1;
442 int buf_size
= BUF_8K
;
443 int src_desc
, dest_desc
= -1;
444 int n_read
, n_written
;
445 mode_t src_mode
= 0; /* The mode of the source file */
448 int dst_exists
= 0, appending
= 0;
449 off_t n_read_total
= 0, file_size
= -1;
450 FileProgressStatus return_status
, temp_status
;
451 struct timeval tv_transfer_start
;
452 int dst_status
= DEST_NONE
; /* 1 if the file is not fully copied */
455 /* FIXME: We should not be using global variables! */
457 return_status
= FILE_RETRY
;
459 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
460 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
465 while (mc_stat (dst_path
, &sb2
) == 0) {
466 if (S_ISDIR (sb2
.st_mode
)) {
468 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
470 if (return_status
== FILE_RETRY
)
472 return return_status
;
478 while ((*ctx
->stat_func
) (src_path
, &sb
)) {
480 file_error (_(" Cannot stat source file \"%s\" \n %s "),
482 if (return_status
!= FILE_RETRY
)
483 return return_status
;
487 /* Destination already exists */
488 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
489 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "),
491 /* Should we replace destination? */
494 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
495 if (return_status
!= FILE_CONT
)
496 return return_status
;
500 if (!ctx
->do_append
) {
501 /* Check the hardlinks */
502 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
503 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
504 /* We have made a hardlink - no more processing is necessary */
508 if (S_ISLNK (sb
.st_mode
))
509 return make_symlink (ctx
, src_path
, dst_path
);
511 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
512 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) ||
513 S_ISSOCK (sb
.st_mode
)) {
514 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
516 return_status
= file_error (
517 _(" Cannot create special file \"%s\" \n %s "), dst_path
);
518 if (return_status
== FILE_RETRY
)
520 return return_status
;
524 while (ctx
->preserve_uidgid
525 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
526 temp_status
= file_error (
527 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
528 if (temp_status
== FILE_RETRY
)
532 while (ctx
->preserve
&&
533 mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
)) {
534 temp_status
= file_error (
535 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
536 if (temp_status
== FILE_RETRY
)
544 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
546 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
547 return_status
= file_error (
548 _(" Cannot open source file \"%s\" \n %s "), src_path
);
549 if (return_status
== FILE_RETRY
)
552 return return_status
;
556 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
) {
557 message (D_ERROR
, _("Warning"),
558 _(" Reget failed, about to overwrite file "));
559 ctx
->do_reget
= ctx
->do_append
= 0;
563 while (mc_fstat (src_desc
, &sb
)) {
564 return_status
= file_error (
565 _(" Cannot fstat source file \"%s\" \n %s "), src_path
);
566 if (return_status
== FILE_RETRY
)
571 src_mode
= sb
.st_mode
;
574 utb
.actime
= sb
.st_atime
;
575 utb
.modtime
= sb
.st_mtime
;
576 file_size
= sb
.st_size
;
578 open_flags
= O_WRONLY
;
579 if (dst_exists
!= 0) {
580 if (ctx
->do_append
!= 0)
581 open_flags
|= O_APPEND
;
583 open_flags
|= O_CREAT
| O_TRUNC
;
585 open_flags
|= O_CREAT
| O_EXCL
;
587 while ((dest_desc
= mc_open (dst_path
, open_flags
, src_mode
)) < 0) {
588 if (errno
== EEXIST
) {
591 return_status
= file_error (
592 _(" Cannot create target file \"%s\" \n %s "), dst_path
);
593 if (return_status
== FILE_RETRY
)
598 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
600 appending
= ctx
->do_append
;
603 /* Find out the optimal buffer size. */
604 while (mc_fstat (dest_desc
, &sb
)) {
605 return_status
= file_error (
606 _(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
607 if (return_status
== FILE_RETRY
)
611 buf
= g_malloc (buf_size
);
616 return_status
= file_progress_show (ctx
, 0, file_size
);
620 if (return_status
!= FILE_CONT
)
624 struct timeval tv_current
, tv_last_update
, tv_last_input
;
625 int secs
, update_secs
;
627 const char *stalled_msg
;
629 tv_last_update
= tv_transfer_start
;
633 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
636 while ((n_read
= mc_read (src_desc
, buf
, buf_size
)) < 0) {
637 return_status
= file_error (
638 _(" Cannot read source file \"%s\" \n %s "), src_path
);
639 if (return_status
== FILE_RETRY
)
646 gettimeofday (&tv_current
, NULL
);
650 n_read_total
+= n_read
;
652 /* Windows NT ftp servers report that files have no
653 * permissions: -------, so if we happen to have actually
654 * read something, we should fix the permissions.
656 if (!(src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)))
657 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
658 gettimeofday (&tv_last_input
, NULL
);
662 mc_write (dest_desc
, t
, n_read
)) < n_read
) {
669 file_error (_(" Cannot write target file \"%s\" \n %s "),
671 if (return_status
!= FILE_RETRY
)
676 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
677 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
680 tv_last_update
= tv_current
;
683 /* 2. Check for a stalled condition */
684 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
686 if (update_secs
> 4) {
687 stalled_msg
= _("(stalled)");
692 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
696 ((dt
/ (double) n_read_total
) * file_size
) - dt
;
697 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
702 /* 4. Compute BPS rate */
705 (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
706 if (ctx
->bps_time
< 1)
708 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
711 file_progress_set_stalled_label (ctx
, stalled_msg
);
712 return_status
= file_progress_show_bytes (ctx
, *progress_bytes
+
713 n_read_total
+ ctx
->do_reget
, ctx
->progress_bytes
);
714 if (return_status
== FILE_CONT
) {
716 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
);
719 if (return_status
!= FILE_CONT
)
724 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
729 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
730 temp_status
= file_error (
731 _(" Cannot close source file \"%s\" \n %s "), src_path
);
732 if (temp_status
== FILE_RETRY
)
734 if (temp_status
== FILE_ABORT
)
735 return_status
= temp_status
;
739 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
740 temp_status
= file_error (
741 _(" Cannot close target file \"%s\" \n %s "), dst_path
);
742 if (temp_status
== FILE_RETRY
)
744 return_status
= temp_status
;
748 if (dst_status
== DEST_SHORT
) {
749 /* Remove short file */
751 result
= query_dialog (Q_("DialogTitle|Copy"),
752 _("Incomplete file was retrieved. Keep it?"),
753 D_ERROR
, 2, _("&Delete"), _("&Keep"));
755 mc_unlink (dst_path
);
756 } else if (dst_status
== DEST_FULL
) {
757 /* Copy has succeeded */
758 if (!appending
&& ctx
->preserve_uidgid
) {
759 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
760 temp_status
= file_error (
761 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
762 if (temp_status
== FILE_RETRY
)
764 return_status
= temp_status
;
771 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
))) {
772 temp_status
= file_error (
773 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
774 if (temp_status
!= FILE_RETRY
) {
775 return_status
= temp_status
;
780 src_mode
= umask(-1);
782 src_mode
= 0100666 & ~src_mode
;
783 mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
));
785 mc_utime (dst_path
, &utb
);
789 if (return_status
== FILE_CONT
)
791 progress_update_one (ctx
, progress_count
, progress_bytes
,
792 file_size
, is_toplevel_file
);
794 return return_status
;
798 * I think these copy_*_* functions should have a return type.
799 * anyway, this function *must* have two directories as arguments.
801 /* FIXME: This function needs to check the return values of the
804 copy_dir_dir (FileOpContext
*ctx
, const char *s
, const char *_d
, int toplevel
,
805 int move_over
, int delete, struct link
*parent_dirs
,
806 off_t
*progress_count
, double *progress_bytes
)
809 struct stat buf
, cbuf
;
811 char *dest_dir
= NULL
;
812 FileProgressStatus return_status
= FILE_CONT
;
817 d
= strutils_shell_unescape (_d
);
819 /* First get the mode of the source dir */
821 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
823 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
824 if (return_status
== FILE_RETRY
)
829 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
830 /* Don't copy a directory we created before (we don't want to copy
831 infinitely if a directory is copied into itself) */
832 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
833 return_status
= FILE_CONT
;
837 /* Hmm, hardlink to directory??? - Norbert */
838 /* FIXME: In this step we should do something
839 in case the destination already exist */
840 /* Check the hardlinks */
841 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
842 && check_hardlinks (s
, d
, &cbuf
) == 1) {
843 /* We have made a hardlink - no more processing is necessary */
847 if (!S_ISDIR (cbuf
.st_mode
)) {
849 file_error (_(" Source \"%s\" is not a directory \n %s "), s
);
850 if (return_status
== FILE_RETRY
)
855 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
856 /* we found a cyclic symbolic link */
857 message (D_ERROR
, MSG_ERROR
,
858 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
859 return_status
= FILE_SKIP
;
863 lp
= g_new (struct link
, 1);
864 lp
->vfs
= vfs_get_class (s
);
865 lp
->ino
= cbuf
.st_ino
;
866 lp
->dev
= cbuf
.st_dev
;
867 lp
->next
= parent_dirs
;
871 /* Now, check if the dest dir exists, if not, create it. */
872 if (mc_stat (d
, &buf
)) {
873 /* Here the dir doesn't exist : make it ! */
875 if (mc_rename (s
, d
) == 0) {
876 return_status
= FILE_CONT
;
884 * If the destination directory exists, we want to copy the whole
885 * directory, but we only want this to happen once.
887 * Escape sequences added to the * to compiler warnings.
888 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
889 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
891 if (!S_ISDIR (buf
.st_mode
)) {
892 return_status
= file_error(
893 _(" Destination \"%s\" must be a directory \n %s "), d
);
894 if (return_status
== FILE_RETRY
)
898 /* Dive into subdir if exists */
899 if (toplevel
&& ctx
->dive_into_subdirs
) {
900 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
907 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
908 return_status
= file_error (
909 _(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
910 if (return_status
!= FILE_RETRY
)
914 lp
= g_new (struct link
, 1);
915 mc_stat (dest_dir
, &buf
);
916 lp
->vfs
= vfs_get_class (dest_dir
);
917 lp
->ino
= buf
.st_ino
;
918 lp
->dev
= buf
.st_dev
;
919 lp
->next
= dest_dirs
;
922 if (ctx
->preserve_uidgid
) {
923 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
924 return_status
= file_error (
925 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
926 if (return_status
!= FILE_RETRY
)
932 /* open the source dir for reading */
933 reading
= mc_opendir (s
);
937 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
940 * Now, we don't want '.' and '..' to be created / copied at any time
942 if (!strcmp (next
->d_name
, "."))
944 if (!strcmp (next
->d_name
, ".."))
947 /* get the filename and add it to the src directory */
948 path
= concat_dir_and_file (s
, next
->d_name
);
950 (*ctx
->stat_func
) (path
, &buf
);
951 if (S_ISDIR (buf
.st_mode
)) {
954 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
956 * From here, we just intend to recursively copy subdirs, not
957 * the double functionality of copying different when the target
958 * dir already exists. So, we give the recursive call the flag 0
959 * meaning no toplevel.
961 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0, delete,
962 parent_dirs
, progress_count
, progress_bytes
);
967 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
968 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
969 progress_count
, progress_bytes
, 0);
972 if (delete && return_status
== FILE_CONT
) {
973 if (ctx
->erase_at_end
) {
974 static struct link
*tail
;
975 size_t len
= strlen (path
);
976 lp
= g_malloc (sizeof (struct link
) + len
);
977 strncpy (lp
->name
, path
, len
+ 1);
978 lp
->st_mode
= buf
.st_mode
;
980 if (erase_list
!= NULL
) {
984 erase_list
= tail
= lp
;
986 if (S_ISDIR (buf
.st_mode
)) {
987 return_status
= erase_dir_iff_empty (ctx
, path
);
989 return_status
= erase_file (ctx
, path
, 0, 0, 0);
994 mc_closedir (reading
);
997 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
998 utb
.actime
= cbuf
.st_atime
;
999 utb
.modtime
= cbuf
.st_mtime
;
1000 mc_utime (dest_dir
, &utb
);
1002 cbuf
.st_mode
= umask(-1);
1003 umask(cbuf
.st_mode
);
1004 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
1005 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1010 g_free (parent_dirs
);
1013 return return_status
;
1018 /* {{{ Move routines */
1020 static FileProgressStatus
1021 move_file_file (FileOpContext
*ctx
, const char *s
, const char *d
,
1022 off_t
*progress_count
, double *progress_bytes
)
1024 struct stat src_stats
, dst_stats
;
1025 FileProgressStatus return_status
= FILE_CONT
;
1026 gboolean copy_done
= FALSE
;
1028 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
1029 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1034 while (mc_lstat (s
, &src_stats
) != 0) {
1035 /* Source doesn't exist */
1037 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1038 if (return_status
!= FILE_RETRY
)
1039 return return_status
;
1042 if (mc_lstat (d
, &dst_stats
) == 0) {
1043 if (src_stats
.st_dev
== dst_stats
.st_dev
1044 && src_stats
.st_ino
== dst_stats
.st_ino
)
1045 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "), s
, d
);
1047 if (S_ISDIR (dst_stats
.st_mode
)) {
1048 message (D_ERROR
, MSG_ERROR
,
1049 _(" Cannot overwrite directory `%s' "), d
);
1054 if (confirm_overwrite
) {
1055 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1056 if (return_status
!= FILE_CONT
)
1057 return return_status
;
1059 /* Ok to overwrite */
1062 if (!ctx
->do_append
) {
1063 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1064 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1065 goto retry_src_remove
;
1067 return return_status
;
1070 if (mc_rename (s
, d
) == 0) {
1071 return progress_update_one (ctx
, progress_count
,
1073 src_stats
.st_size
, 1);
1077 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1078 one nfs to the same, but on the server it is on two different
1079 filesystems. Then nfs returns EIO instead of EXDEV.
1080 Hope it will not hurt if we always in case of error try to copy/delete. */
1082 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1084 if (errno
!= EXDEV
) {
1086 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1088 if (return_status
== FILE_RETRY
)
1090 return return_status
;
1094 /* Failed because filesystem boundary -> copy the file instead */
1096 copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1097 if (return_status
!= FILE_CONT
)
1098 return return_status
;
1102 if ((return_status
=
1103 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1104 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1105 return return_status
;
1110 if (mc_unlink (s
)) {
1112 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1113 if (return_status
== FILE_RETRY
)
1114 goto retry_src_remove
;
1115 return return_status
;
1119 return_status
= progress_update_one (ctx
,
1122 src_stats
.st_size
, 1);
1125 return return_status
;
1129 move_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
,
1130 off_t
*progress_count
, double *progress_bytes
)
1132 struct stat sbuf
, dbuf
, destbuf
;
1135 FileProgressStatus return_status
;
1136 gboolean move_over
= FALSE
;
1139 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1140 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1146 dstat_ok
= (mc_stat (d
, &dbuf
) == 0);
1148 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
1149 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same directory "), s
, d
);
1152 destdir
= g_strdup (d
); /* destination doesn't exist */
1153 else if (!ctx
->dive_into_subdirs
) {
1154 destdir
= g_strdup (d
);
1157 destdir
= concat_dir_and_file (d
, x_basename (s
));
1159 /* Check if the user inputted an existing dir */
1161 if (!mc_stat (destdir
, &destbuf
)) {
1163 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1164 progress_count
, progress_bytes
);
1166 if (return_status
!= FILE_CONT
)
1170 if (S_ISDIR (destbuf
.st_mode
))
1173 (" Cannot overwrite directory \"%s\" %s "),
1177 file_error (_(" Cannot overwrite file \"%s\" %s "),
1179 if (return_status
== FILE_RETRY
)
1180 goto retry_dst_stat
;
1183 return return_status
;
1187 if (mc_rename (s
, destdir
) == 0) {
1188 return_status
= FILE_CONT
;
1192 if (errno
!= EXDEV
) {
1195 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1197 if (return_status
== FILE_RETRY
)
1201 /* Failed because of filesystem boundary -> copy dir instead */
1203 copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
,
1206 if (return_status
!= FILE_CONT
)
1209 if ((return_status
=
1210 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1211 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1215 if (ctx
->erase_at_end
) {
1216 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1217 if (S_ISDIR (erase_list
->st_mode
)) {
1219 erase_dir_iff_empty (ctx
, erase_list
->name
);
1222 erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1224 erase_list
= erase_list
->next
;
1228 erase_dir_iff_empty (ctx
, s
);
1232 while (erase_list
) {
1234 erase_list
= erase_list
->next
;
1237 return return_status
;
1242 /* {{{ Erase routines */
1243 /* Don't update progress status if progress_count==NULL */
1244 static FileProgressStatus
1245 erase_file (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1246 double *progress_bytes
, int is_toplevel_file
)
1251 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1255 if (progress_count
&& mc_lstat (s
, &buf
)) {
1256 /* ignore, most likely the mc_unlink fails, too */
1260 while (mc_unlink (s
)) {
1262 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1263 if (return_status
!= FILE_RETRY
)
1264 return return_status
;
1268 return progress_update_one (ctx
, progress_count
, progress_bytes
,
1269 buf
.st_size
, is_toplevel_file
);
1274 static FileProgressStatus
1275 recursive_erase (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1276 double *progress_bytes
)
1278 struct dirent
*next
;
1282 FileProgressStatus return_status
= FILE_CONT
;
1284 if (!strcmp (s
, ".."))
1287 reading
= mc_opendir (s
);
1292 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1293 if (!strcmp (next
->d_name
, "."))
1295 if (!strcmp (next
->d_name
, ".."))
1297 path
= concat_dir_and_file (s
, next
->d_name
);
1298 if (mc_lstat (path
, &buf
)) {
1300 mc_closedir (reading
);
1303 if (S_ISDIR (buf
.st_mode
))
1306 (ctx
, path
, progress_count
, progress_bytes
)
1307 != FILE_CONT
) ? FILE_RETRY
: FILE_CONT
;
1310 erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1313 mc_closedir (reading
);
1314 if (return_status
!= FILE_CONT
)
1315 return return_status
;
1316 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1320 while (my_rmdir (s
)) {
1322 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1323 if (return_status
!= FILE_RETRY
)
1324 return return_status
;
1330 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1331 in the directory path points to, 0 else. */
1333 check_dir_is_empty (const char *path
)
1339 dir
= mc_opendir (path
);
1343 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1344 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1345 (d
->d_name
[1] == '.'
1346 && d
->d_name
[2] == '\0')))
1347 continue; /* "." or ".." */
1357 erase_dir (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1358 double *progress_bytes
)
1360 FileProgressStatus error
;
1362 if (strcmp (s
, "..") == 0)
1365 if (strcmp (s
, ".") == 0)
1368 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1372 /* The old way to detect a non empty directory was:
1373 error = my_rmdir (s);
1374 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1375 For the linux user space nfs server (nfs-server-2.2beta29-2)
1376 we would have to check also for EIO. I hope the new way is
1377 fool proof. (Norbert)
1379 error
= check_dir_is_empty (s
);
1380 if (error
== 0) { /* not empty */
1381 error
= query_recursive (ctx
, s
);
1382 if (error
== FILE_CONT
)
1383 return recursive_erase (ctx
, s
, progress_count
,
1389 while (my_rmdir (s
) == -1) {
1391 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1392 if (error
!= FILE_RETRY
)
1399 static FileProgressStatus
1400 erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
)
1402 FileProgressStatus error
;
1404 if (strcmp (s
, "..") == 0)
1407 if (strcmp (s
, ".") == 0)
1410 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1414 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1417 while (my_rmdir (s
)) {
1419 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1420 if (error
!= FILE_RETRY
)
1429 /* {{{ Panel operate routines */
1432 * Return currently selected entry name or the name of the first marked
1433 * entry if there is one.
1436 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1440 if (get_current_type () == view_tree
) {
1441 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1442 char *tree_name
= tree_selected_name (tree
);
1444 mc_stat (tree_name
, stat_buf
);
1448 if (panel
->marked
) {
1449 for (i
= 0; i
< panel
->count
; i
++)
1450 if (panel
->dir
.list
[i
].f
.marked
) {
1451 *stat_buf
= panel
->dir
.list
[i
].st
;
1452 return panel
->dir
.list
[i
].fname
;
1455 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1456 return panel
->dir
.list
[panel
->selected
].fname
;
1458 g_assert_not_reached ();
1464 compute_dir_size_create_ui (void)
1466 ComputeDirSizeUI
*ui
;
1468 const char *b_name
= N_("&Abort");
1474 ui
= g_new (ComputeDirSizeUI
, 1);
1476 ui
->dlg
= create_dlg (0, 0, 8, COLS
/2, dialog_colors
, NULL
,
1477 NULL
, _("Directory scanning"), DLG_CENTER
);
1478 ui
->dirname
= label_new (3, 3, "");
1479 add_widget (ui
->dlg
, ui
->dirname
);
1481 add_widget (ui
->dlg
,
1482 button_new (5, (ui
->dlg
->cols
- strlen (b_name
))/2,
1483 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
1485 /* We will manage the dialog without any help,
1486 that's why we have to call init_dlg */
1493 compute_dir_size_destroy_ui (ComputeDirSizeUI
*ui
)
1496 /* schedule to update passive panel */
1497 other_panel
->dirty
= 1;
1499 /* close and destroy dialog */
1500 dlg_run_done (ui
->dlg
);
1501 destroy_dlg (ui
->dlg
);
1507 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
1509 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
1516 label_set_text (this->dirname
, name_trunc (dirname
, this->dlg
->cols
- 6));
1518 event
.x
= -1; /* Don't show the GPM cursor */
1519 c
= tty_get_event (&event
, FALSE
, FALSE
);
1523 /* Reinitialize to avoid old values after events other than
1524 selecting a button */
1525 this->dlg
->ret_value
= FILE_CONT
;
1527 dlg_process_event (this->dlg
, c
, &event
);
1529 switch (this->dlg
->ret_value
) {
1541 * Computes the number of bytes used by the files in a directory
1544 compute_dir_size (const char *dirname
, const void *ui
,
1545 compute_dir_size_callback cback
,
1546 off_t
*ret_marked
, double *ret_total
)
1549 struct dirent
*dirent
;
1550 FileProgressStatus ret
= FILE_CONT
;
1552 dir
= mc_opendir (dirname
);
1557 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1562 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
1564 if (ret
!= FILE_CONT
)
1567 if (strcmp (dirent
->d_name
, ".") == 0)
1569 if (strcmp (dirent
->d_name
, "..") == 0)
1572 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1573 res
= mc_lstat (fullname
, &s
);
1580 if (S_ISDIR (s
.st_mode
)) {
1581 off_t subdir_count
= 0;
1582 double subdir_bytes
= 0;
1584 ret
= compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
);
1586 if (ret
!= FILE_CONT
) {
1591 *ret_marked
+= subdir_count
;
1592 *ret_total
+= subdir_bytes
;
1595 *ret_total
+= s
.st_size
;
1607 * panel_compute_totals:
1609 * compute the number of files and the number of bytes
1610 * used up by the whole selection, recursing directories
1611 * as required. In addition, it checks to see if it will
1612 * overwrite any files by doing the copy.
1614 static FileProgressStatus
1615 panel_compute_totals (WPanel
*panel
, const void *ui
,
1616 compute_dir_size_callback cback
,
1617 off_t
*ret_marked
, double *ret_total
)
1624 for (i
= 0; i
< panel
->count
; i
++) {
1627 if (!panel
->dir
.list
[i
].f
.marked
)
1630 s
= &panel
->dir
.list
[i
].st
;
1632 if (S_ISDIR (s
->st_mode
)) {
1634 off_t subdir_count
= 0;
1635 double subdir_bytes
= 0;
1636 FileProgressStatus status
;
1639 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1641 status
= compute_dir_size (dir_name
, ui
, cback
,
1642 &subdir_count
, &subdir_bytes
);
1645 if (status
!= FILE_CONT
)
1648 *ret_marked
+= subdir_count
;
1649 *ret_total
+= subdir_bytes
;
1652 *ret_total
+= s
->st_size
;
1660 * This array introduced to avoid translation problems. The former (op_names)
1661 * is assumed to be nouns, suitable in dialog box titles; this one should
1662 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1663 * (I don't use spaces around the words, because someday they could be
1664 * dropped, when widgets get smarter)
1667 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
1668 static const char *op_names1
[] = {
1669 N_("FileOperation|Copy"),
1670 N_("FileOperation|Move"),
1671 N_("FileOperation|Delete")
1675 * These are formats for building a prompt. Parts encoded as follows:
1676 * %o - operation from op_names1
1677 * %f - file/files or files/directories, as appropriate
1678 * %m - "with source mask" or question mark for delete
1679 * %s - source name (truncated)
1680 * %d - number of marked files
1681 * %e - "to:" or question mark for delete
1683 * xgettext:no-c-format */
1684 static const char *one_format
= N_("%o %f \"%s\"%m");
1685 /* xgettext:no-c-format */
1686 static const char *many_format
= N_("%o %d %f%m");
1688 static const char *prompt_parts
[] = {
1693 N_("files/directories"),
1694 N_(" with source mask:"),
1698 static const char *question_format
= N_("%s?");
1701 * Generate user prompt for panel operation.
1702 * single_source is the name if the source entry or NULL for multiple
1704 * src_stat is only used when single_source is not NULL.
1707 panel_operate_generate_prompt (const WPanel
*panel
, const int operation
,
1708 gboolean single_source
,
1709 const struct stat
*src_stat
)
1711 const char *sp
, *cp
;
1712 char format_string
[BUF_MEDIUM
];
1713 char *dp
= format_string
;
1714 gboolean build_question
= FALSE
;
1717 static gboolean i18n_flag
= FALSE
;
1721 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1722 op_names1
[i
] = Q_(op_names1
[i
]);
1724 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1725 prompt_parts
[i
] = _(prompt_parts
[i
]);
1727 one_format
= _(one_format
);
1728 many_format
= _(many_format
);
1729 question_format
= _(question_format
);
1732 #endif /* ENABLE_NLS */
1734 sp
= single_source
? one_format
: many_format
;
1736 while (*sp
!= '\0') {
1742 cp
= op_names1
[operation
];
1745 if (operation
== OP_DELETE
) {
1747 build_question
= TRUE
;
1749 cp
= prompt_parts
[5];
1752 if (operation
== OP_DELETE
) {
1754 build_question
= TRUE
;
1756 cp
= prompt_parts
[6];
1759 if (single_source
) {
1760 cp
= S_ISDIR (src_stat
->
1761 st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1763 cp
= (panel
->marked
== panel
->dirs_marked
)
1765 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1783 if (build_question
) {
1784 char tmp
[BUF_MEDIUM
];
1786 memmove (tmp
, format_string
, sizeof (tmp
));
1787 g_snprintf (format_string
, sizeof (format_string
),
1788 question_format
, tmp
);
1791 return g_strdup (format_string
);
1794 #ifdef WITH_BACKGROUND
1796 end_bg_process (FileOpContext
*ctx
, enum OperationMode mode
) {
1802 unregister_task_with_pid(pid
);
1803 // file_op_context_destroy(ctx);
1811 * Performs one of the operations on the selection on the source_panel
1812 * (copy, delete, move).
1814 * Returns 1 if did change the directory
1815 * structure, Returns 0 if user aborted
1817 * force_single forces operation on the current entry and affects
1818 * default destination. Current filename is used as default.
1821 panel_operate (void *source_panel
, FileOperation operation
, int force_single
)
1823 WPanel
*panel
= (WPanel
*) source_panel
;
1824 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
1825 || (get_current_type () == view_tree
);
1827 char *source
= NULL
;
1828 #ifdef WITH_FULL_PATHS
1829 char *source_with_path
= NULL
;
1831 # define source_with_path source
1832 #endif /* !WITH_FULL_PATHS */
1835 char *save_cwd
= NULL
, *save_dest
= NULL
;
1836 struct stat src_stat
, dst_stat
;
1838 FileProgressStatus value
;
1845 int do_bg
= 0; /* do background operation? */
1848 static gboolean i18n_flag
= FALSE
;
1850 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1851 op_names
[i
] = Q_(op_names
[i
]);
1854 #endif /* ENABLE_NLS */
1856 free_linklist (&linklist
);
1857 free_linklist (&dest_dirs
);
1859 /* Update panel contents to avoid actions on deleted files */
1860 if (!panel
->is_panelized
) {
1861 update_panels (UP_RELOAD
, UP_KEEPSEL
);
1867 source
= selection (panel
)->fname
;
1868 src_stat
= selection (panel
)->st
;
1870 source
= panel_get_file (panel
, &src_stat
);
1873 if (!strcmp (source
, "..")) {
1874 message (D_ERROR
, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1879 ctx
= file_op_context_new (operation
);
1881 /* Show confirmation dialog */
1882 if (operation
!= OP_DELETE
) {
1887 /* Forced single operations default to the original name */
1890 else if (get_other_type () == view_listing
)
1891 dest_dir
= other_panel
->cwd
;
1893 dest_dir
= panel
->cwd
;
1895 * Add trailing backslash only when do non-local ops.
1896 * It saves user from occasional file renames (when destination
1900 && dest_dir
[0] != '\0'
1901 && dest_dir
[strlen (dest_dir
) - 1] != PATH_SEP
) {
1902 /* add trailing separator */
1903 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char *) NULL
);
1906 dest_dir_
= g_strdup (dest_dir
);
1908 if (dest_dir_
== NULL
) {
1909 file_op_context_destroy (ctx
);
1913 /* Generate confirmation prompt */
1914 format
= panel_operate_generate_prompt (panel
, operation
,
1915 source
!= NULL
, &src_stat
);
1917 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
1918 source
!= NULL
? (void *) source
1919 : (void *) &panel
->marked
,
1925 if (dest
== NULL
|| dest
[0] == '\0') {
1926 file_op_context_destroy (ctx
);
1930 } else if (confirm_delete
) {
1932 char fmd_buf
[BUF_MEDIUM
];
1934 /* Generate confirmation prompt */
1935 format
= panel_operate_generate_prompt (panel
, OP_DELETE
,
1936 source
!= NULL
, &src_stat
);
1939 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
1941 const int fmd_xlen
= 64;
1942 i
= fmd_xlen
- str_term_width1 (format
) - 4;
1943 g_snprintf (fmd_buf
, sizeof (fmd_buf
),
1944 format
, str_trunc (source
, i
));
1952 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2,
1953 _("&Yes"), _("&No"));
1956 file_op_context_destroy (ctx
);
1961 /* Background also need ctx->ui, but not full */
1963 file_op_context_create_ui_without_init (ctx
, 1);
1965 file_op_context_create_ui (ctx
, 1);
1967 #ifdef WITH_BACKGROUND
1968 /* Did the user select to do a background operation? */
1972 v
= do_background (ctx
,
1973 g_strconcat (op_names
[operation
], ": ",
1974 panel
->cwd
, (char *) NULL
));
1976 message (D_ERROR
, MSG_ERROR
,
1977 _(" Sorry, I could not put the job in background "));
1980 /* If we are the parent */
1982 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
1983 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
1984 /* file_op_context_destroy (ctx); */
1988 #endif /* WITH_BACKGROUND */
1990 /* Initialize things */
1991 /* We do not want to trash cache every time file is
1992 created/touched. However, this will make our cache contain
1995 if (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1996 save_dest
= g_strdup (dest
);
1999 if (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1))
2000 save_cwd
= g_strdup (panel
->cwd
);
2003 /* Now, let's do the job */
2005 /* This code is only called by the tree and panel code */
2007 /* We now have ETA in all cases */
2009 /* One file: FIXME mc_chdir will take user out of any vfs */
2010 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
2011 mc_chdir (PATH_SEP_STR
);
2013 /* The source and src_stat variables have been initialized before */
2014 #ifdef WITH_FULL_PATHS
2015 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2016 #endif /* WITH_FULL_PATHS */
2018 if (operation
== OP_DELETE
) {
2019 if (S_ISDIR (src_stat
.st_mode
))
2020 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
2023 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
2025 temp
= transform_source (ctx
, source_with_path
);
2027 value
= transform_error
;
2029 char *repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2030 char *temp2
= concat_dir_and_file (repl_dest
, temp
);
2036 switch (operation
) {
2039 * we use file_mask_op_follow_links only with OP_COPY,
2041 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2043 if (S_ISDIR (src_stat
.st_mode
)) {
2045 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
2046 0, 0, 0, &count
, &bytes
);
2049 copy_file_file (ctx
, source_with_path
, dest
, 1,
2055 if (S_ISDIR (src_stat
.st_mode
))
2057 move_dir_dir (ctx
, source_with_path
, dest
,
2061 move_file_file (ctx
, source_with_path
, dest
,
2066 /* Unknown file operation */
2070 } /* Copy or move operation */
2072 if ((value
== FILE_CONT
) && !force_single
)
2073 unmark_files (panel
);
2076 /* Check destination for copy or move operation */
2077 if (operation
!= OP_DELETE
) {
2078 retry_many_dst_stat
:
2079 dst_result
= mc_stat (dest
, &dst_stat
);
2080 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
2082 (_(" Destination \"%s\" must be a directory \n %s "),
2083 dest
) == FILE_RETRY
)
2084 goto retry_many_dst_stat
;
2089 /* Initialize variables for progress bars */
2090 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
2091 ComputeDirSizeUI
*ui
;
2092 FileProgressStatus status
;
2094 ui
= compute_dir_size_create_ui ();
2095 status
= panel_compute_totals (panel
,
2096 ui
, compute_dir_size_update_ui
,
2097 &ctx
->progress_count
, &ctx
->progress_bytes
);
2098 compute_dir_size_destroy_ui (ui
);
2100 if (status
!= FILE_CONT
)
2103 ctx
->progress_totals_computed
= 1;
2105 ctx
->progress_totals_computed
= 0;
2106 ctx
->progress_count
= panel
->marked
;
2107 ctx
->progress_bytes
= panel
->total
;
2110 /* Loop for every file, perform the actual copy operation */
2111 for (i
= 0; i
< panel
->count
; i
++) {
2112 if (!panel
->dir
.list
[i
].f
.marked
)
2113 continue; /* Skip the unmarked ones */
2115 source
= panel
->dir
.list
[i
].fname
;
2116 src_stat
= panel
->dir
.list
[i
].st
;
2118 #ifdef WITH_FULL_PATHS
2119 g_free (source_with_path
);
2120 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2121 #endif /* WITH_FULL_PATHS */
2123 if (operation
== OP_DELETE
) {
2124 if (S_ISDIR (src_stat
.st_mode
))
2126 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
2129 erase_file (ctx
, source_with_path
, &count
, &bytes
,
2132 temp
= transform_source (ctx
, source_with_path
);
2134 value
= transform_error
;
2137 char *repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2138 char *temp2
= concat_dir_and_file (repl_dest
, temp
);
2142 temp3
= source_with_path
;
2143 source_with_path
= strutils_shell_unescape(source_with_path
);
2146 temp2
= strutils_shell_unescape(temp2
);
2149 switch (operation
) {
2152 * we use file_mask_op_follow_links only with OP_COPY
2154 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2155 if (S_ISDIR (src_stat
.st_mode
))
2157 copy_dir_dir (ctx
, source_with_path
, temp2
,
2158 1, 0, 0, 0, &count
, &bytes
);
2161 copy_file_file (ctx
, source_with_path
,
2162 temp2
, 1, &count
, &bytes
,
2164 free_linklist (&dest_dirs
);
2168 if (S_ISDIR (src_stat
.st_mode
))
2170 move_dir_dir (ctx
, source_with_path
, temp2
,
2174 move_file_file (ctx
, source_with_path
,
2175 temp2
, &count
, &bytes
);
2179 /* Unknown file operation */
2184 } /* Copy or move operation */
2186 if (value
== FILE_ABORT
)
2189 if (value
== FILE_CONT
)
2190 do_file_mark (panel
, i
, 0);
2192 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2197 && file_progress_show_bytes (ctx
, bytes
,
2198 ctx
->progress_bytes
) ==
2202 if (operation
!= OP_DELETE
&& verbose
2203 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2207 } /* Loop for every file */
2208 } /* Many entries */
2213 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2217 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2221 free_linklist (&linklist
);
2222 free_linklist (&dest_dirs
);
2223 #ifdef WITH_FULL_PATHS
2224 g_free (source_with_path
);
2225 #endif /* WITH_FULL_PATHS */
2227 g_free (ctx
->dest_mask
);
2228 ctx
->dest_mask
= NULL
;
2229 #ifdef WITH_BACKGROUND
2230 /* Let our parent know we are saying bye bye */
2231 if (we_are_background
) {
2232 int cur_pid
= getpid();
2233 /* Send pid to parent with child context, it is fork and
2234 don't modify real parent ctx */
2236 parent_call ((void *) end_bg_process
, ctx
, 0);
2241 #endif /* WITH_BACKGROUND */
2243 file_op_context_destroy (ctx
);
2249 /* {{{ Query/status report routines */
2251 static FileProgressStatus
2252 real_do_file_error (enum OperationMode mode
, const char *error
)
2257 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2259 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2277 /* Report error with one file */
2279 file_error (const char *format
, const char *file
)
2281 char buf
[BUF_MEDIUM
];
2283 g_snprintf (buf
, sizeof (buf
), format
,
2284 path_trunc (file
, 30), unix_error_string (errno
));
2286 return do_file_error (buf
);
2289 /* Report error with two files */
2290 static FileProgressStatus
2291 files_error (const char *format
, const char *file1
, const char *file2
)
2293 char buf
[BUF_MEDIUM
];
2294 char *nfile1
= g_strdup (path_trunc (file1
, 15));
2295 char *nfile2
= g_strdup (path_trunc (file2
, 15));
2297 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
,
2298 unix_error_string (errno
));
2303 return do_file_error (buf
);
2306 static FileProgressStatus
2307 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, const char *s
)
2311 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2315 _("\n Directory not empty. \n"
2316 " Delete it recursively? ")
2317 : _("\n Background process: Directory not empty \n"
2318 " Delete it recursively? ");
2319 text
= g_strconcat (_(" Delete: "), path_trunc (s
, 30), " ", (char *) NULL
);
2323 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2324 _("&Yes"), _("&No"),
2325 _("A&ll"), _("Non&e"),
2328 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2333 switch (ctx
->recursive_result
) {
2335 case RECURSIVE_ALWAYS
:
2339 case RECURSIVE_NEVER
:
2342 case RECURSIVE_ABORT
:
2349 #ifdef WITH_BACKGROUND
2350 static FileProgressStatus
2351 do_file_error (const char *str
)
2355 FileProgressStatus (*f
) (enum OperationMode
, const char *);
2357 pntr
.f
= real_do_file_error
;
2359 if (we_are_background
)
2360 return parent_call (pntr
.p
, NULL
, 1, strlen (str
),
2363 return real_do_file_error (Foreground
, str
);
2366 static FileProgressStatus
2367 query_recursive (FileOpContext
*ctx
, const char *s
)
2371 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
2373 pntr
.f
= real_query_recursive
;
2375 if (we_are_background
)
2376 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
2378 return real_query_recursive (ctx
, Foreground
, s
);
2381 static FileProgressStatus
2382 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2383 struct stat
*_d_stat
)
2387 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
2388 struct stat
*, struct stat
*);
2390 pntr
.f
= file_progress_real_query_replace
;
2392 if (we_are_background
)
2393 return parent_call (pntr
.p
,
2396 strlen (destname
), destname
,
2397 sizeof (struct stat
), _s_stat
,
2398 sizeof (struct stat
), _d_stat
);
2400 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2405 static FileProgressStatus
2406 do_file_error (const char *str
)
2408 return real_do_file_error (Foreground
, str
);
2411 static FileProgressStatus
2412 query_recursive (FileOpContext
*ctx
, const char *s
)
2414 return real_query_recursive (ctx
, Foreground
, s
);
2417 static FileProgressStatus
2418 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2419 struct stat
*_d_stat
)
2421 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2425 #endif /* !WITH_BACKGROUND */
2428 Cause emacs to enter folding mode for this file: