2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1994, 1995 Janne Kukonlehto
6 1994, 1995 Fred Leeflang
7 1994, 1995, 1996 Miguel de Icaza
8 1995, 1996 Jakub Jelinek
12 The copy code was based in GNU's cp, and was written by:
13 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
15 The move code was based in GNU's mv, and was written by:
16 Mike Parker and David MacKenzie.
18 Janne Kukonlehto added much error recovery to them for being used
19 in an interactive program.
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
36 * Please note that all dialogs used here must be safe for background
40 /* {{{ Include files */
49 #include <sys/types.h>
53 #include "mhl/memory.h"
54 #include "mhl/escape.h"
64 #include "main.h" /* cmd_buf */
68 #include "background.h" /* we_are_background */
71 /* Needed for current_panel, other_panel and WTree */
78 #include "../vfs/vfs-impl.h"
82 /* Hack: the vfs code should not rely on this */
83 #define WITH_FULL_PATHS 1
88 * Whether the Midnight Commander tries to provide more
89 * information about copy/move sizes and bytes transfered
90 * at the expense of some speed
92 int file_op_compute_totals
= 1;
94 /* This is a hard link cache */
97 struct vfs_class
*vfs
;
105 /* the hard link cache */
106 static struct link
*linklist
= NULL
;
108 /* the files-to-be-erased list */
109 static struct link
*erase_list
;
112 * In copy_dir_dir we use two additional single linked lists: The first -
113 * variable name `parent_dirs' - holds information about already copied
114 * directories and is used to detect cyclic symbolic links.
115 * The second (`dest_dirs' below) holds information about just created
116 * target directories and is used to detect when an directory is copied
117 * into itself (we don't want to copy infinitly).
118 * Both lists don't use the linkcount and name structure members of struct
121 static struct link
*dest_dirs
= 0;
123 const char *op_names
[3] = {
131 static int query_replace (FileOpContext
* ctx
, const char *destname
,
132 struct stat
*_s_stat
, struct stat
*_d_stat
);
133 static int query_recursive (FileOpContext
* ctx
, const char *s
);
134 static int do_file_error (const char *str
);
135 static int erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
);
136 static int erase_file (FileOpContext
*ctx
, const char *s
,
137 off_t
*progress_count
, double *progress_bytes
,
138 int is_toplevel_file
);
139 static int files_error (const char *format
, const char *file1
,
143 enum CaseConvs
{ NO_CONV
= 0, UP_CHAR
= 1, LOW_CHAR
= 2, UP_SECT
=
147 convert_case (char c
, enum CaseConvs
*conversion
)
149 if (*conversion
& UP_CHAR
) {
150 *conversion
&= ~UP_CHAR
;
151 return toupper ((unsigned char) c
);
152 } else if (*conversion
& LOW_CHAR
) {
153 *conversion
&= ~LOW_CHAR
;
154 return tolower ((unsigned char) c
);
155 } else if (*conversion
& UP_SECT
) {
156 return toupper ((unsigned char) c
);
157 } else if (*conversion
& LOW_SECT
) {
158 return tolower ((unsigned char) c
);
163 static int transform_error
= 0;
166 do_transform_source (FileOpContext
*ctx
, const char *source
)
169 const char *fnsource
= x_basename (source
);
171 enum CaseConvs case_conv
= NO_CONV
;
172 static char fntarget
[MC_MAXPATHLEN
];
174 len
= strlen (fnsource
);
175 j
= re_match (&ctx
->rx
, fnsource
, len
, 0, &ctx
->regs
);
177 transform_error
= FILE_SKIP
;
180 for (next_reg
= 1, j
= 0, k
= 0; j
< strlen (ctx
->dest_mask
); j
++) {
181 switch (ctx
->dest_mask
[j
]) {
183 if (mhl_shell_is_char_escaped (&ctx
->dest_mask
[j
])){
184 fntarget
[k
++] = ctx
->dest_mask
[j
++];
185 fntarget
[k
++] = ctx
->dest_mask
[j
];
189 if (!isdigit ((unsigned char) ctx
->dest_mask
[j
])) {
190 /* Backslash followed by non-digit */
191 switch (ctx
->dest_mask
[j
]) {
193 case_conv
|= UP_SECT
;
194 case_conv
&= ~LOW_SECT
;
197 case_conv
|= UP_CHAR
;
200 case_conv
|= LOW_SECT
;
201 case_conv
&= ~UP_SECT
;
204 case_conv
|= LOW_CHAR
;
210 /* Backslash as quote mark */
212 convert_case (ctx
->dest_mask
[j
], &case_conv
);
216 /* Backslash followed by digit */
217 next_reg
= ctx
->dest_mask
[j
] - '0';
223 if (next_reg
< 0 || next_reg
>= RE_NREGS
224 || ctx
->regs
.start
[next_reg
] < 0) {
225 message (1, MSG_ERROR
, _(" Invalid target mask "));
226 transform_error
= FILE_ABORT
;
229 for (l
= (size_t) ctx
->regs
.start
[next_reg
];
230 l
< (size_t) ctx
->regs
.end
[next_reg
]; l
++)
231 fntarget
[k
++] = convert_case (fnsource
[l
], &case_conv
);
236 fntarget
[k
++] = convert_case (ctx
->dest_mask
[j
], &case_conv
);
245 transform_source (FileOpContext
*ctx
, const char *source
)
247 char *s
= g_strdup (source
);
251 /* We remove \n from the filename since regex routines would use \n as an anchor */
252 /* this is just to be allowed to maniupulate file names with \n on it */
253 for (q
= s
; *q
; q
++) {
257 p
= do_transform_source (ctx
, s
);
263 free_linklist (struct link
**linklist
)
265 struct link
*lp
, *lp2
;
267 for (lp
= *linklist
; lp
!= NULL
; lp
= lp2
) {
275 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
277 ino_t ino
= sb
->st_ino
;
278 dev_t dev
= sb
->st_dev
;
280 struct vfs_class
*vfs
= vfs_get_class (path
);
287 if (lp
->ino
== ino
&& lp
->dev
== dev
)
295 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
296 * and a hardlink was succesfully made
299 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
302 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
303 ino_t ino
= pstat
->st_ino
;
304 dev_t dev
= pstat
->st_dev
;
305 struct stat link_stat
;
308 if (vfs_file_class_flags (src_name
) & VFSF_NOLINKS
)
311 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
312 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
) {
313 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
314 && link_stat
.st_dev
== dev
315 && vfs_get_class (lp
->name
) == my_vfs
) {
316 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
318 if (vfs_get_class (dst_name
) == vfs_get_class (p
)) {
319 if (!mc_stat (p
, &link_stat
)) {
320 if (!mc_link (p
, dst_name
))
325 message (1, MSG_ERROR
, _(" Cannot make the hardlink "));
328 lp
= (struct link
*) g_malloc (sizeof (struct link
) + strlen (src_name
)
329 + strlen (dst_name
) + 1);
335 strcpy (lp
->name
, src_name
);
336 lpdstname
= lp
->name
+ strlen(lp
->name
) + 1;
337 strcpy (lpdstname
, dst_name
);
345 * Duplicate the contents of the symbolic link src_path in dst_path.
346 * Try to make a stable symlink if the option "stable symlink" was
347 * set in the file mask dialog.
348 * If dst_path is an existing symlink it will be deleted silently
349 * (upper levels take already care of existing files at dst_path).
352 make_symlink (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
)
354 char link_target
[MC_MAXPATHLEN
];
360 if (mc_lstat (dst_path
, &sb
) == 0 && S_ISLNK (sb
.st_mode
))
366 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
369 file_error (_(" Cannot read source link \"%s\" \n %s "),
371 if (return_status
== FILE_RETRY
)
372 goto retry_src_readlink
;
373 return return_status
;
375 link_target
[len
] = 0;
377 if (ctx
->stable_symlinks
)
378 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
379 message (1, MSG_ERROR
,
380 _(" Cannot make stable symlinks across "
381 "non-local filesystems: \n\n"
382 " Option Stable Symlinks will be disabled "));
383 ctx
->stable_symlinks
= 0;
386 if (ctx
->stable_symlinks
&& *link_target
!= PATH_SEP
) {
389 const char *r
= strrchr (src_path
, PATH_SEP
);
392 p
= g_strndup (src_path
, r
- src_path
+ 1);
393 if (*dst_path
== PATH_SEP
)
394 q
= g_strdup (dst_path
);
396 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
397 s
= strrchr (q
, PATH_SEP
);
400 s
= g_strconcat (p
, link_target
, (char *) NULL
);
402 g_strlcpy (link_target
, s
, sizeof (link_target
));
404 s
= diff_two_paths (q
, link_target
);
406 g_strlcpy (link_target
, s
, sizeof (link_target
));
415 if (mc_symlink (link_target
, dst_path
) == 0)
419 * if dst_exists, it is obvious that this had failed.
420 * We can delete the old symlink and try again...
422 if (dst_is_symlink
) {
423 if (!mc_unlink (dst_path
))
424 if (mc_symlink (link_target
, dst_path
) == 0)
429 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
431 if (return_status
== FILE_RETRY
)
432 goto retry_dst_symlink
;
433 return return_status
;
437 progress_update_one (FileOpContext
*ctx
,
438 off_t
*progress_count
,
439 double *progress_bytes
, off_t add
, int is_toplevel_file
)
443 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
445 (*progress_bytes
) += add
;
448 /* Apply some heuristic here to not call the update stuff very often */
450 file_progress_show_count (ctx
, *progress_count
,
451 ctx
->progress_count
);
452 if (ret
!= FILE_CONT
)
455 file_progress_show_bytes (ctx
, *progress_bytes
,
456 ctx
->progress_bytes
);
461 /* Status of the destination file */
463 DEST_NONE
, /* Not created */
464 DEST_SHORT
, /* Created, not fully copied */
465 DEST_FULL
/* Created, fully copied */
469 copy_file_file (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
,
470 int ask_overwrite
, off_t
*progress_count
,
471 double *progress_bytes
, int is_toplevel_file
)
473 uid_t src_uid
= (uid_t
) - 1;
474 gid_t src_gid
= (gid_t
) - 1;
477 int buf_size
= BUF_8K
;
478 int src_desc
, dest_desc
= -1;
479 int n_read
, n_written
;
480 mode_t src_mode
= 0; /* The mode of the source file */
483 int dst_exists
= 0, appending
= 0;
484 off_t n_read_total
= 0, file_size
= -1;
485 int return_status
, temp_status
;
486 struct timeval tv_transfer_start
;
487 int dst_status
= DEST_NONE
; /* 1 if the file is not fully copied */
489 /* FIXME: We should not be using global variables! */
491 return_status
= FILE_RETRY
;
493 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
494 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
499 while (mc_stat (dst_path
, &sb2
) == 0) {
500 if (S_ISDIR (sb2
.st_mode
)) {
502 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
504 if (return_status
== FILE_RETRY
)
506 return return_status
;
512 while ((*ctx
->stat_func
) (src_path
, &sb
)) {
514 file_error (_(" Cannot stat source file \"%s\" \n %s "),
516 if (return_status
!= FILE_RETRY
)
517 return return_status
;
521 /* Destination already exists */
522 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
) {
523 message (1, MSG_ERROR
,
524 _(" `%s' and `%s' are the same file "), src_path
, dst_path
);
529 /* Should we replace destination? */
532 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
533 if (return_status
!= FILE_CONT
)
534 return return_status
;
538 if (!ctx
->do_append
) {
539 /* Check the hardlinks */
540 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
541 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
542 /* We have made a hardlink - no more processing is necessary */
546 if (S_ISLNK (sb
.st_mode
)) {
549 retval
= make_symlink (ctx
, src_path
, dst_path
);
553 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
554 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) ||
555 S_ISSOCK (sb
.st_mode
)) {
556 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
558 return_status
= file_error (
559 _(" Cannot create special file \"%s\" \n %s "), dst_path
);
560 if (return_status
== FILE_RETRY
)
562 return return_status
;
566 while (ctx
->preserve_uidgid
567 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
568 temp_status
= file_error (
569 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
570 if (temp_status
== FILE_RETRY
)
574 while (ctx
->preserve
&&
575 mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
)) {
576 temp_status
= file_error (
577 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
578 if (temp_status
== FILE_RETRY
)
586 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
588 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
589 return_status
= file_error (
590 _(" Cannot open source file \"%s\" \n %s "), src_path
);
591 if (return_status
== FILE_RETRY
)
594 return return_status
;
598 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
) {
599 message (1, _("Warning"),
600 _(" Reget failed, about to overwrite file "));
601 ctx
->do_reget
= ctx
->do_append
= 0;
605 while (mc_fstat (src_desc
, &sb
)) {
606 return_status
= file_error (
607 _(" Cannot fstat source file \"%s\" \n %s "), src_path
);
608 if (return_status
== FILE_RETRY
)
613 src_mode
= sb
.st_mode
;
616 utb
.actime
= sb
.st_atime
;
617 utb
.modtime
= sb
.st_mtime
;
618 file_size
= sb
.st_size
;
620 /* Create the new regular file with small permissions initially,
621 do not create a security hole. FIXME: You have security hole
622 here, btw. Imagine copying to /tmp and symlink attack :-( */
624 while ((dest_desc
= mc_open (dst_path
, O_WRONLY
| (ctx
->do_append
?
625 O_APPEND
: (O_CREAT
| O_TRUNC
)), 0600)) < 0) {
626 return_status
= file_error (
627 _(" Cannot create target file \"%s\" \n %s "), dst_path
);
628 if (return_status
== FILE_RETRY
)
633 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
635 appending
= ctx
->do_append
;
638 /* Find out the optimal buffer size. */
639 while (mc_fstat (dest_desc
, &sb
)) {
640 return_status
= file_error (
641 _(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
642 if (return_status
== FILE_RETRY
)
646 buf
= g_malloc (buf_size
);
651 return_status
= file_progress_show (ctx
, 0, file_size
);
655 if (return_status
!= FILE_CONT
)
659 struct timeval tv_current
, tv_last_update
, tv_last_input
;
660 int secs
, update_secs
;
662 const char *stalled_msg
;
664 tv_last_update
= tv_transfer_start
;
668 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
671 while ((n_read
= mc_read (src_desc
, buf
, buf_size
)) < 0) {
672 return_status
= file_error (
673 _(" Cannot read source file \"%s\" \n %s "), src_path
);
674 if (return_status
== FILE_RETRY
)
681 gettimeofday (&tv_current
, NULL
);
685 n_read_total
+= n_read
;
687 /* Windows NT ftp servers report that files have no
688 * permissions: -------, so if we happen to have actually
689 * read something, we should fix the permissions.
691 if (!(src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)))
692 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
693 gettimeofday (&tv_last_input
, NULL
);
697 mc_write (dest_desc
, t
, n_read
)) < n_read
) {
704 file_error (_(" Cannot write target file \"%s\" \n %s "),
706 if (return_status
!= FILE_RETRY
)
711 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
712 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
715 tv_last_update
= tv_current
;
718 /* 2. Check for a stalled condition */
719 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
721 if (update_secs
> 4) {
722 stalled_msg
= _("(stalled)");
727 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
731 ((dt
/ (double) n_read_total
) * file_size
) - dt
;
732 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
737 /* 4. Compute BPS rate */
740 (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
741 if (ctx
->bps_time
< 1)
743 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
746 file_progress_set_stalled_label (ctx
, stalled_msg
);
747 return_status
= file_progress_show_bytes (ctx
, *progress_bytes
+
748 n_read_total
+ ctx
->do_reget
, ctx
->progress_bytes
);
749 if (return_status
== FILE_CONT
) {
751 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
);
754 if (return_status
!= FILE_CONT
)
759 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
764 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
765 temp_status
= file_error (
766 _(" Cannot close source file \"%s\" \n %s "), src_path
);
767 if (temp_status
== FILE_RETRY
)
769 if (temp_status
== FILE_ABORT
)
770 return_status
= temp_status
;
774 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
775 temp_status
= file_error (
776 _(" Cannot close target file \"%s\" \n %s "), dst_path
);
777 if (temp_status
== FILE_RETRY
)
779 return_status
= temp_status
;
783 if (dst_status
== DEST_SHORT
) {
784 /* Remove short file */
786 result
= query_dialog (_("Copy"),
787 _("Incomplete file was retrieved. Keep it?"),
788 D_ERROR
, 2, _("&Delete"), _("&Keep"));
790 mc_unlink (dst_path
);
791 } else if (dst_status
== DEST_FULL
) {
792 /* Copy has succeeded */
793 if (!appending
&& ctx
->preserve_uidgid
) {
794 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
795 temp_status
= file_error (
796 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
797 if (temp_status
== FILE_RETRY
)
799 return_status
= temp_status
;
804 if (!appending
&& ctx
->preserve
) {
805 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
))) {
806 temp_status
= file_error (
807 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
808 if (temp_status
!= FILE_RETRY
) {
809 return_status
= temp_status
;
813 mc_utime (dst_path
, &utb
);
817 if (return_status
== FILE_CONT
)
819 progress_update_one (ctx
, progress_count
, progress_bytes
,
820 file_size
, is_toplevel_file
);
822 return return_status
;
826 * I think these copy_*_* functions should have a return type.
827 * anyway, this function *must* have two directories as arguments.
829 /* FIXME: This function needs to check the return values of the
832 copy_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
, int toplevel
,
833 int move_over
, int delete, struct link
*parent_dirs
,
834 off_t
*progress_count
, double *progress_bytes
)
837 struct stat buf
, cbuf
;
839 char *path
, *mdpath
, *dest_file
, *dest_dir
;
840 int return_status
= FILE_CONT
;
844 /* First get the mode of the source dir */
846 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
848 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
849 if (return_status
== FILE_RETRY
)
851 return return_status
;
854 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
855 /* Don't copy a directory we created before (we don't want to copy
856 infinitely if a directory is copied into itself) */
857 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
861 /* Hmm, hardlink to directory??? - Norbert */
862 /* FIXME: In this step we should do something
863 in case the destination already exist */
864 /* Check the hardlinks */
865 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
866 && check_hardlinks (s
, d
, &cbuf
) == 1) {
867 /* We have made a hardlink - no more processing is necessary */
868 return return_status
;
871 if (!S_ISDIR (cbuf
.st_mode
)) {
873 file_error (_(" Source \"%s\" is not a directory \n %s "), s
);
874 if (return_status
== FILE_RETRY
)
876 return return_status
;
879 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
880 /* we found a cyclic symbolic link */
881 message (1, MSG_ERROR
,
882 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
886 lp
= g_new (struct link
, 1);
887 lp
->vfs
= vfs_get_class (s
);
888 lp
->ino
= cbuf
.st_ino
;
889 lp
->dev
= cbuf
.st_dev
;
890 lp
->next
= parent_dirs
;
894 /* Now, check if the dest dir exists, if not, create it. */
895 if (mc_stat (d
, &buf
)) {
896 /* Here the dir doesn't exist : make it ! */
899 if (mc_rename (s
, d
) == 0) {
900 g_free (parent_dirs
);
904 dest_dir
= g_strdup (d
);
907 * If the destination directory exists, we want to copy the whole
908 * directory, but we only want this to happen once.
910 * Escape sequences added to the * to compiler warnings.
911 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
912 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
914 if (!S_ISDIR (buf
.st_mode
)) {
915 return_status
= file_error(
916 _(" Destination \"%s\" must be a directory \n %s "), d
);
917 if (return_status
== FILE_RETRY
)
919 g_free (parent_dirs
);
920 return return_status
;
922 /* Dive into subdir if exists */
923 if (toplevel
&& ctx
->dive_into_subdirs
) {
924 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
926 dest_dir
= g_strdup (d
);
930 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
931 return_status
= file_error (
932 _(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
933 if (return_status
!= FILE_RETRY
)
937 lp
= g_new (struct link
, 1);
938 mc_stat (dest_dir
, &buf
);
939 lp
->vfs
= vfs_get_class (dest_dir
);
940 lp
->ino
= buf
.st_ino
;
941 lp
->dev
= buf
.st_dev
;
942 lp
->next
= dest_dirs
;
945 if (ctx
->preserve_uidgid
) {
946 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
947 return_status
= file_error (
948 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
949 if (return_status
!= FILE_RETRY
)
955 /* open the source dir for reading */
956 if ((reading
= mc_opendir (s
)) == 0) {
960 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
962 * Now, we don't want '.' and '..' to be created / copied at any time
964 if (!strcmp (next
->d_name
, "."))
966 if (!strcmp (next
->d_name
, ".."))
969 /* get the filename and add it to the src directory */
970 path
= concat_dir_and_file (s
, next
->d_name
);
972 (*ctx
->stat_func
) (path
, &buf
);
973 if (S_ISDIR (buf
.st_mode
)) {
974 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
976 * From here, we just intend to recursively copy subdirs, not
977 * the double functionality of copying different when the target
978 * dir already exists. So, we give the recursive call the flag 0
979 * meaning no toplevel.
981 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0, delete,
982 parent_dirs
, progress_count
, progress_bytes
);
985 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
986 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
987 progress_count
, progress_bytes
, 0);
990 if (delete && return_status
== FILE_CONT
) {
991 if (ctx
->erase_at_end
) {
992 static struct link
*tail
;
993 lp
= g_malloc (sizeof (struct link
) + strlen (path
));
994 strcpy (lp
->name
, path
);
995 lp
->st_mode
= buf
.st_mode
;
1001 erase_list
= tail
= lp
;
1003 if (S_ISDIR (buf
.st_mode
)) {
1004 return_status
= erase_dir_iff_empty (ctx
, path
);
1006 return_status
= erase_file (ctx
, path
, 0, 0, 0);
1011 mc_closedir (reading
);
1013 if (ctx
->preserve
) {
1014 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1015 utb
.actime
= cbuf
.st_atime
;
1016 utb
.modtime
= cbuf
.st_mtime
;
1017 mc_utime (dest_dir
, &utb
);
1022 g_free (parent_dirs
);
1023 return return_status
;
1028 /* {{{ Move routines */
1031 move_file_file (FileOpContext
*ctx
, const char *s
, const char *d
,
1032 off_t
*progress_count
, double *progress_bytes
)
1034 struct stat src_stats
, dst_stats
;
1035 int return_status
= FILE_CONT
;
1036 gboolean copy_done
= FALSE
;
1038 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
1039 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1044 while (mc_lstat (s
, &src_stats
) != 0) {
1045 /* Source doesn't exist */
1047 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1048 if (return_status
!= FILE_RETRY
)
1049 return return_status
;
1052 if (mc_lstat (d
, &dst_stats
) == 0) {
1053 if (src_stats
.st_dev
== dst_stats
.st_dev
1054 && src_stats
.st_ino
== dst_stats
.st_ino
) {
1055 int msize
= COLS
- 36;
1056 char st
[MC_MAXPATHLEN
];
1057 char dt
[MC_MAXPATHLEN
];
1063 strcpy (st
, path_trunc (s
, msize
));
1064 strcpy (dt
, path_trunc (d
, msize
));
1065 message (1, MSG_ERROR
,
1066 _(" `%s' and `%s' are the same file "), st
, dt
);
1071 if (S_ISDIR (dst_stats
.st_mode
)) {
1072 message (1, MSG_ERROR
,
1073 _(" Cannot overwrite directory `%s' "), d
);
1078 if (confirm_overwrite
) {
1079 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1080 if (return_status
!= FILE_CONT
)
1081 return return_status
;
1083 /* Ok to overwrite */
1086 if (!ctx
->do_append
) {
1087 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1088 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1089 goto retry_src_remove
;
1091 return return_status
;
1094 if (mc_rename (s
, d
) == 0) {
1095 return progress_update_one (ctx
, progress_count
,
1097 src_stats
.st_size
, 1);
1101 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1102 one nfs to the same, but on the server it is on two different
1103 filesystems. Then nfs returns EIO instead of EXDEV.
1104 Hope it will not hurt if we always in case of error try to copy/delete. */
1106 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1108 if (errno
!= EXDEV
) {
1110 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1112 if (return_status
== FILE_RETRY
)
1114 return return_status
;
1118 /* Failed because filesystem boundary -> copy the file instead */
1120 copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1121 if (return_status
!= FILE_CONT
)
1122 return return_status
;
1126 if ((return_status
=
1127 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1128 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1129 return return_status
;
1134 if (mc_unlink (s
)) {
1136 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1137 if (return_status
== FILE_RETRY
)
1138 goto retry_src_remove
;
1139 return return_status
;
1143 return_status
= progress_update_one (ctx
,
1146 src_stats
.st_size
, 1);
1149 return return_status
;
1153 move_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
,
1154 off_t
*progress_count
, double *progress_bytes
)
1156 struct stat sbuf
, dbuf
, destbuf
;
1162 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1163 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1169 if (mc_stat (d
, &dbuf
))
1170 destdir
= g_strdup (d
); /* destination doesn't exist */
1171 else if (!ctx
->dive_into_subdirs
) {
1172 destdir
= g_strdup (d
);
1175 destdir
= concat_dir_and_file (d
, x_basename (s
));
1177 if (sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
) {
1178 int msize
= COLS
- 36;
1179 char st
[MC_MAXPATHLEN
];
1180 char dt
[MC_MAXPATHLEN
];
1186 strcpy (st
, path_trunc (s
, msize
));
1187 strcpy (dt
, path_trunc (d
, msize
));
1188 message (1, MSG_ERROR
,
1189 _(" `%s' and `%s' are the same directory "), st
, dt
);
1194 /* Check if the user inputted an existing dir */
1196 if (!mc_stat (destdir
, &destbuf
)) {
1198 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1199 progress_count
, progress_bytes
);
1201 if (return_status
!= FILE_CONT
)
1205 if (S_ISDIR (destbuf
.st_mode
))
1208 (" Cannot overwrite directory \"%s\" %s "),
1212 file_error (_(" Cannot overwrite file \"%s\" %s "),
1214 if (return_status
== FILE_RETRY
)
1215 goto retry_dst_stat
;
1218 return return_status
;
1222 if (mc_rename (s
, destdir
) == 0) {
1223 return_status
= FILE_CONT
;
1227 if (errno
!= EXDEV
) {
1230 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1232 if (return_status
== FILE_RETRY
)
1236 /* Failed because of filesystem boundary -> copy dir instead */
1238 copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
,
1241 if (return_status
!= FILE_CONT
)
1244 if ((return_status
=
1245 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1246 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1250 if (ctx
->erase_at_end
) {
1251 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1252 if (S_ISDIR (erase_list
->st_mode
)) {
1254 erase_dir_iff_empty (ctx
, erase_list
->name
);
1257 erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1259 erase_list
= erase_list
->next
;
1263 erase_dir_iff_empty (ctx
, s
);
1267 while (erase_list
) {
1269 erase_list
= erase_list
->next
;
1272 return return_status
;
1277 /* {{{ Erase routines */
1278 /* Don't update progress status if progress_count==NULL */
1280 erase_file (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1281 double *progress_bytes
, int is_toplevel_file
)
1286 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1290 if (progress_count
&& mc_lstat (s
, &buf
)) {
1291 /* ignore, most likely the mc_unlink fails, too */
1295 while (mc_unlink (s
)) {
1297 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1298 if (return_status
!= FILE_RETRY
)
1299 return return_status
;
1303 return progress_update_one (ctx
, progress_count
, progress_bytes
,
1304 buf
.st_size
, is_toplevel_file
);
1310 recursive_erase (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1311 double *progress_bytes
)
1313 struct dirent
*next
;
1317 int return_status
= FILE_CONT
;
1319 if (!strcmp (s
, ".."))
1322 reading
= mc_opendir (s
);
1327 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1328 if (!strcmp (next
->d_name
, "."))
1330 if (!strcmp (next
->d_name
, ".."))
1332 path
= concat_dir_and_file (s
, next
->d_name
);
1333 if (mc_lstat (path
, &buf
)) {
1335 mc_closedir (reading
);
1338 if (S_ISDIR (buf
.st_mode
))
1341 (ctx
, path
, progress_count
, progress_bytes
)
1345 erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1348 mc_closedir (reading
);
1349 if (return_status
!= FILE_CONT
)
1350 return return_status
;
1351 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1355 while (my_rmdir (s
)) {
1357 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1358 if (return_status
!= FILE_RETRY
)
1359 return return_status
;
1365 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1366 in the directory path points to, 0 else. */
1368 check_dir_is_empty (const char *path
)
1374 dir
= mc_opendir (path
);
1378 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1379 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1380 (d
->d_name
[1] == '.'
1381 && d
->d_name
[2] == '\0')))
1382 continue; /* "." or ".." */
1392 erase_dir (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1393 double *progress_bytes
)
1397 if (strcmp (s
, "..") == 0)
1400 if (strcmp (s
, ".") == 0)
1403 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1407 /* The old way to detect a non empty directory was:
1408 error = my_rmdir (s);
1409 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1410 For the linux user space nfs server (nfs-server-2.2beta29-2)
1411 we would have to check also for EIO. I hope the new way is
1412 fool proof. (Norbert)
1414 error
= check_dir_is_empty (s
);
1415 if (error
== 0) { /* not empty */
1416 error
= query_recursive (ctx
, s
);
1417 if (error
== FILE_CONT
)
1418 return recursive_erase (ctx
, s
, progress_count
,
1424 while (my_rmdir (s
) == -1) {
1426 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1427 if (error
!= FILE_RETRY
)
1435 erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
)
1439 if (strcmp (s
, "..") == 0)
1442 if (strcmp (s
, ".") == 0)
1445 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1449 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1452 while (my_rmdir (s
)) {
1454 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1455 if (error
!= FILE_RETRY
)
1464 /* {{{ Panel operate routines */
1467 * Return currently selected entry name or the name of the first marked
1468 * entry if there is one.
1471 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1475 if (get_current_type () == view_tree
) {
1476 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1477 char *tree_name
= tree_selected_name (tree
);
1479 mc_stat (tree_name
, stat_buf
);
1483 if (panel
->marked
) {
1484 for (i
= 0; i
< panel
->count
; i
++)
1485 if (panel
->dir
.list
[i
].f
.marked
) {
1486 *stat_buf
= panel
->dir
.list
[i
].st
;
1487 return panel
->dir
.list
[i
].fname
;
1490 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1491 return panel
->dir
.list
[panel
->selected
].fname
;
1493 g_assert_not_reached ();
1500 * Computes the number of bytes used by the files in a directory
1503 compute_dir_size (const char *dirname
, off_t
*ret_marked
, double *ret_total
)
1506 struct dirent
*dirent
;
1508 dir
= mc_opendir (dirname
);
1513 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1518 if (strcmp (dirent
->d_name
, ".") == 0)
1520 if (strcmp (dirent
->d_name
, "..") == 0)
1523 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1525 res
= mc_lstat (fullname
, &s
);
1532 if (S_ISDIR (s
.st_mode
)) {
1533 off_t subdir_count
= 0;
1534 double subdir_bytes
= 0;
1536 compute_dir_size (fullname
, &subdir_count
, &subdir_bytes
);
1538 *ret_marked
+= subdir_count
;
1539 *ret_total
+= subdir_bytes
;
1542 *ret_total
+= s
.st_size
;
1551 * panel_compute_totals:
1553 * compute the number of files and the number of bytes
1554 * used up by the whole selection, recursing directories
1555 * as required. In addition, it checks to see if it will
1556 * overwrite any files by doing the copy.
1559 panel_compute_totals (WPanel
*panel
, off_t
*ret_marked
, double *ret_total
)
1566 for (i
= 0; i
< panel
->count
; i
++) {
1569 if (!panel
->dir
.list
[i
].f
.marked
)
1572 s
= &panel
->dir
.list
[i
].st
;
1574 if (S_ISDIR (s
->st_mode
)) {
1576 off_t subdir_count
= 0;
1577 double subdir_bytes
= 0;
1580 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1581 compute_dir_size (dir_name
, &subdir_count
, &subdir_bytes
);
1583 *ret_marked
+= subdir_count
;
1584 *ret_total
+= subdir_bytes
;
1588 *ret_total
+= s
->st_size
;
1594 * This array introduced to avoid translation problems. The former (op_names)
1595 * is assumed to be nouns, suitable in dialog box titles; this one should
1596 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1597 * Notice first symbol - it is to fool gettext and force these strings to
1598 * be different for it. First symbol is skipped while building a prompt.
1599 * (I don't use spaces around the words, because someday they could be
1600 * dropped, when widgets get smarter)
1602 static const char *op_names1
[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1605 int fmd_xlen
= FMD_XLEN
;
1608 * These are formats for building a prompt. Parts encoded as follows:
1609 * %o - operation from op_names1
1610 * %f - file/files or files/directories, as appropriate
1611 * %m - "with source mask" or question mark for delete
1612 * %s - source name (truncated)
1613 * %d - number of marked files
1614 * %e - "to:" or question mark for delete
1616 * xgettext:no-c-format */
1617 static const char *one_format
= N_("%o %f \"%s\"%m");
1618 /* xgettext:no-c-format */
1619 static const char *many_format
= N_("%o %d %f%m");
1620 static const char *prompt_parts
[] = {
1621 N_("file"), N_("files"), N_("directory"), N_("directories"),
1622 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1626 * Generate user prompt for panel operation.
1627 * single_source is the name if the source entry or NULL for multiple
1629 * src_stat is only used when single_source is not NULL.
1632 panel_operate_generate_prompt (const WPanel
*panel
, const int operation
,
1633 const char *single_source
,
1634 const struct stat
*src_stat
)
1636 register const char *sp
, *cp
;
1638 char format_string
[BUF_MEDIUM
];
1639 char *dp
= format_string
;
1642 static int i18n_flag
= 0;
1644 fmd_init_i18n (FALSE
); /* to get proper fmd_xlen */
1646 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1647 op_names1
[i
] = _(op_names1
[i
]);
1649 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1650 prompt_parts
[i
] = _(prompt_parts
[i
]);
1652 one_format
= _(one_format
);
1653 many_format
= _(many_format
);
1656 #endif /* ENABLE_NLS */
1658 sp
= single_source
? one_format
: many_format
;
1666 cp
= op_names1
[operation
] + 1;
1669 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1672 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[6];
1675 if (single_source
) {
1676 cp
= S_ISDIR (src_stat
->
1677 st_mode
) ? prompt_parts
[2] :
1680 cp
= (panel
->marked
== panel
->dirs_marked
)
1682 : (panel
->dirs_marked
? prompt_parts
[4]
1701 if (single_source
) {
1702 i
= fmd_xlen
- strlen (format_string
) - 4;
1703 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1704 name_trunc (single_source
, i
));
1706 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1708 i
= strlen (cmd_buf
) + 6 - fmd_xlen
;
1711 fmd_init_i18n (TRUE
); /* to recalculate positions of child widgets */
1719 * Performs one of the operations on the selection on the source_panel
1720 * (copy, delete, move).
1722 * Returns 1 if did change the directory
1723 * structure, Returns 0 if user aborted
1725 * force_single forces operation on the current entry and affects
1726 * default destination. Current filename is used as default.
1729 panel_operate (void *source_panel
, FileOperation operation
,
1732 WPanel
*panel
= source_panel
;
1733 #ifdef WITH_FULL_PATHS
1734 char *source_with_path
= NULL
;
1736 # define source_with_path source
1737 #endif /* !WITH_FULL_PATHS */
1738 char *source
= NULL
;
1740 const char *temp
= NULL
;
1741 char *save_cwd
= NULL
, *save_dest
= NULL
;
1742 int single_entry
= (get_current_type () == view_tree
)
1743 || (panel
->marked
<= 1) || force_single
;
1744 struct stat src_stat
, dst_stat
;
1752 int do_bg
= 0; /* do background operation? */
1754 free_linklist (&linklist
);
1755 free_linklist (&dest_dirs
);
1759 source
= selection (panel
)->fname
;
1760 src_stat
= selection (panel
)->st
;
1762 source
= panel_get_file (panel
, &src_stat
);
1765 if (!strcmp (source
, "..")) {
1766 message (1, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1771 /* Generate confirmation prompt */
1772 panel_operate_generate_prompt (panel
, operation
, source
, &src_stat
);
1774 ctx
= file_op_context_new (operation
);
1776 /* Show confirmation dialog */
1777 if (operation
== OP_DELETE
&& confirm_delete
) {
1781 i
= query_dialog (_(op_names
[operation
]), cmd_buf
, D_ERROR
, 2,
1782 _("&Yes"), _("&No"));
1785 file_op_context_destroy (ctx
);
1788 } else if (operation
!= OP_DELETE
) {
1791 /* Forced single operations default to the original name */
1794 else if (get_other_type () == view_listing
)
1795 dest_dir
= other_panel
->cwd
;
1797 dest_dir
= panel
->cwd
;
1800 file_mask_dialog (ctx
, operation
, cmd_buf
, dest_dir
,
1801 single_entry
, &do_bg
);
1803 file_op_context_destroy (ctx
);
1807 file_op_context_destroy (ctx
);
1812 #ifdef WITH_BACKGROUND
1813 /* Did the user select to do a background operation? */
1817 v
= do_background (ctx
,
1818 g_strconcat (op_names
[operation
], ": ",
1821 message (1, MSG_ERROR
,
1822 _(" Sorry, I could not put the job in background "));
1825 /* If we are the parent */
1827 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
1828 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
1829 /* file_op_context_destroy (ctx); */
1833 #endif /* WITH_BACKGROUND */
1835 /* Initialize things */
1836 /* We do not want to trash cache every time file is
1837 created/touched. However, this will make our cache contain
1840 if (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1841 save_dest
= g_strdup (dest
);
1844 if (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1845 save_cwd
= g_strdup (panel
->cwd
);
1848 /* Now, let's do the job */
1853 file_op_context_create_ui (ctx
, 1);
1855 /* This code is only called by the tree and panel code */
1857 /* We now have ETA in all cases */
1859 /* One file: FIXME mc_chdir will take user out of any vfs */
1860 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1861 mc_chdir (PATH_SEP_STR
);
1863 /* The source and src_stat variables have been initialized before */
1864 #ifdef WITH_FULL_PATHS
1865 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1866 #endif /* WITH_FULL_PATHS */
1868 if (operation
== OP_DELETE
) {
1869 if (S_ISDIR (src_stat
.st_mode
))
1870 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1873 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1875 temp
= transform_source (ctx
, source_with_path
);
1878 value
= transform_error
;
1880 char *temp2
= concat_dir_and_file (dest
, temp
);
1885 switch (operation
) {
1888 * we use file_mask_op_follow_links only with OP_COPY,
1890 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1892 if (S_ISDIR (src_stat
.st_mode
))
1894 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
1895 0, 0, 0, &count
, &bytes
);
1898 copy_file_file (ctx
, source_with_path
, dest
, 1,
1903 if (S_ISDIR (src_stat
.st_mode
))
1905 move_dir_dir (ctx
, source_with_path
, dest
,
1909 move_file_file (ctx
, source_with_path
, dest
,
1914 /* Unknown file operation */
1918 } /* Copy or move operation */
1920 if ((value
== FILE_CONT
) && !force_single
)
1921 unmark_files (panel
);
1924 /* Check destination for copy or move operation */
1925 if (operation
!= OP_DELETE
) {
1926 retry_many_dst_stat
:
1927 dst_result
= mc_stat (dest
, &dst_stat
);
1928 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
1930 (_(" Destination \"%s\" must be a directory \n %s "),
1931 dest
) == FILE_RETRY
)
1932 goto retry_many_dst_stat
;
1937 /* Initialize variables for progress bars */
1938 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
1939 panel_compute_totals (panel
, &ctx
->progress_count
,
1940 &ctx
->progress_bytes
);
1941 ctx
->progress_totals_computed
= 1;
1943 ctx
->progress_totals_computed
= 0;
1944 ctx
->progress_count
= panel
->marked
;
1945 ctx
->progress_bytes
= panel
->total
;
1948 /* Loop for every file, perform the actual copy operation */
1949 for (i
= 0; i
< panel
->count
; i
++) {
1950 if (!panel
->dir
.list
[i
].f
.marked
)
1951 continue; /* Skip the unmarked ones */
1953 source
= panel
->dir
.list
[i
].fname
;
1954 src_stat
= panel
->dir
.list
[i
].st
;
1956 #ifdef WITH_FULL_PATHS
1957 g_free (source_with_path
);
1958 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1959 #endif /* WITH_FULL_PATHS */
1961 if (operation
== OP_DELETE
) {
1962 if (S_ISDIR (src_stat
.st_mode
))
1964 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1967 erase_file (ctx
, source_with_path
, &count
, &bytes
,
1970 temp
= transform_source (ctx
, source_with_path
);
1972 value
= transform_error
;
1974 char *temp2
= concat_dir_and_file (dest
, temp
);
1976 source_with_path
= mhl_shell_unescape_buf(source_with_path
);
1977 temp2
= mhl_shell_unescape_buf(temp2
);
1979 switch (operation
) {
1982 * we use file_mask_op_follow_links only with OP_COPY
1984 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1985 if (S_ISDIR (src_stat
.st_mode
))
1987 copy_dir_dir (ctx
, source_with_path
, temp2
,
1988 1, 0, 0, 0, &count
, &bytes
);
1991 copy_file_file (ctx
, source_with_path
,
1992 temp2
, 1, &count
, &bytes
,
1994 free_linklist (&dest_dirs
);
1998 if (S_ISDIR (src_stat
.st_mode
))
2000 move_dir_dir (ctx
, source_with_path
, temp2
,
2004 move_file_file (ctx
, source_with_path
,
2005 temp2
, &count
, &bytes
);
2009 /* Unknown file operation */
2014 } /* Copy or move operation */
2016 if (value
== FILE_ABORT
)
2019 if (value
== FILE_CONT
)
2020 do_file_mark (panel
, i
, 0);
2022 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2027 && file_progress_show_bytes (ctx
, bytes
,
2028 ctx
->progress_bytes
) ==
2032 if (operation
!= OP_DELETE
&& verbose
2033 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2037 } /* Loop for every file */
2038 } /* Many entries */
2043 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2047 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2051 free_linklist (&linklist
);
2052 free_linklist (&dest_dirs
);
2053 #ifdef WITH_FULL_PATHS
2054 g_free (source_with_path
);
2055 #endif /* WITH_FULL_PATHS */
2057 g_free (ctx
->dest_mask
);
2058 ctx
->dest_mask
= NULL
;
2059 #ifdef WITH_BACKGROUND
2060 /* Let our parent know we are saying bye bye */
2061 if (we_are_background
) {
2065 #endif /* WITH_BACKGROUND */
2067 file_op_context_destroy (ctx
);
2073 /* {{{ Query/status report routines */
2076 real_do_file_error (enum OperationMode mode
, const char *error
)
2081 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2083 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2101 /* Report error with one file */
2103 file_error (const char *format
, const char *file
)
2105 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
,
2106 path_trunc (file
, 30), unix_error_string (errno
));
2108 return do_file_error (cmd_buf
);
2111 /* Report error with two files */
2113 files_error (const char *format
, const char *file1
, const char *file2
)
2118 strcpy (nfile1
, path_trunc (file1
, 15));
2119 strcpy (nfile2
, path_trunc (file2
, 15));
2121 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
, nfile1
, nfile2
,
2122 unix_error_string (errno
));
2124 return do_file_error (cmd_buf
);
2128 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, const char *s
)
2132 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2136 _("\n Directory not empty. \n"
2137 " Delete it recursively? ")
2138 : _("\n Background process: Directory not empty \n"
2139 " Delete it recursively? ");
2140 text
= g_strconcat (_(" Delete: "), path_trunc (s
, 30), " ", (char *) NULL
);
2144 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2145 _("&Yes"), _("&No"),
2146 _("A&ll"), _("Non&e"),
2149 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2154 switch (ctx
->recursive_result
) {
2156 case RECURSIVE_ALWAYS
:
2160 case RECURSIVE_NEVER
:
2163 case RECURSIVE_ABORT
:
2170 #ifdef WITH_BACKGROUND
2172 do_file_error (const char *str
)
2174 if (we_are_background
)
2175 return parent_call (real_do_file_error
, NULL
, 1, strlen (str
),
2178 return real_do_file_error (Foreground
, str
);
2182 query_recursive (FileOpContext
*ctx
, const char *s
)
2184 if (we_are_background
)
2185 return parent_call (real_query_recursive
, ctx
, 1, strlen (s
), s
);
2187 return real_query_recursive (ctx
, Foreground
, s
);
2191 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2192 struct stat
*_d_stat
)
2194 if (we_are_background
)
2195 return parent_call ((void *) file_progress_real_query_replace
,
2198 strlen (destname
), destname
,
2199 sizeof (struct stat
), _s_stat
,
2200 sizeof (struct stat
), _d_stat
);
2202 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2208 do_file_error (const char *str
)
2210 return real_do_file_error (Foreground
, str
);
2214 query_recursive (FileOpContext
*ctx
, const char *s
)
2216 return real_query_recursive (ctx
, Foreground
, s
);
2220 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2221 struct stat
*_d_stat
)
2223 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2227 #endif /* !WITH_BACKGROUND */
2230 Cause emacs to enter folding mode for this file: