2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1994, 1995 Janne Kukonlehto
6 1994, 1995 Fred Leeflang
7 1994, 1995, 1996 Miguel de Icaza
8 1995, 1996 Jakub Jelinek
12 The copy code was based in GNU's cp, and was written by:
13 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
15 The move code was based in GNU's mv, and was written by:
16 Mike Parker and David MacKenzie.
18 Janne Kukonlehto added much error recovery to them for being used
19 in an interactive program.
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
36 * Please note that all dialogs used here must be safe for background
40 /* {{{ Include files */
49 #include <sys/types.h>
61 #include "main.h" /* cmd_buf */
65 #include "background.h" /* we_are_background */
67 /* Needed for current_panel, other_panel and WTree */
74 #include "../vfs/vfs-impl.h"
78 /* Hack: the vfs code should not rely on this */
79 #define WITH_FULL_PATHS 1
84 * Whether the Midnight Commander tries to provide more
85 * information about copy/move sizes and bytes transfered
86 * at the expense of some speed
88 int file_op_compute_totals
= 1;
90 /* This is a hard link cache */
93 struct vfs_class
*vfs
;
101 /* the hard link cache */
102 static struct link
*linklist
= NULL
;
104 /* the files-to-be-erased list */
105 static struct link
*erase_list
;
108 * In copy_dir_dir we use two additional single linked lists: The first -
109 * variable name `parent_dirs' - holds information about already copied
110 * directories and is used to detect cyclic symbolic links.
111 * The second (`dest_dirs' below) holds information about just created
112 * target directories and is used to detect when an directory is copied
113 * into itself (we don't want to copy infinitly).
114 * Both lists don't use the linkcount and name structure members of struct
117 static struct link
*dest_dirs
= 0;
119 const char *op_names
[3] = {
127 static int query_replace (FileOpContext
* ctx
, const char *destname
,
128 struct stat
*_s_stat
, struct stat
*_d_stat
);
129 static int query_recursive (FileOpContext
* ctx
, const char *s
);
130 static int do_file_error (const char *str
);
131 static int erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
);
132 static int erase_file (FileOpContext
*ctx
, const char *s
,
133 off_t
*progress_count
, double *progress_bytes
,
134 int is_toplevel_file
);
135 static int files_error (const char *format
, const char *file1
,
139 enum CaseConvs
{ NO_CONV
= 0, UP_CHAR
= 1, LOW_CHAR
= 2, UP_SECT
=
143 convert_case (char c
, enum CaseConvs
*conversion
)
145 if (*conversion
& UP_CHAR
) {
146 *conversion
&= ~UP_CHAR
;
147 return toupper ((unsigned char) c
);
148 } else if (*conversion
& LOW_CHAR
) {
149 *conversion
&= ~LOW_CHAR
;
150 return tolower ((unsigned char) c
);
151 } else if (*conversion
& UP_SECT
) {
152 return toupper ((unsigned char) c
);
153 } else if (*conversion
& LOW_SECT
) {
154 return tolower ((unsigned char) c
);
159 static int transform_error
= 0;
162 do_transform_source (FileOpContext
*ctx
, const char *source
)
165 const char *fnsource
= x_basename (source
);
167 enum CaseConvs case_conv
= NO_CONV
;
168 static char fntarget
[MC_MAXPATHLEN
];
170 len
= strlen (fnsource
);
171 j
= re_match (&ctx
->rx
, fnsource
, len
, 0, &ctx
->regs
);
173 transform_error
= FILE_SKIP
;
176 for (next_reg
= 1, j
= 0, k
= 0; j
< strlen (ctx
->dest_mask
); j
++) {
177 switch (ctx
->dest_mask
[j
]) {
180 if (!isdigit ((unsigned char) ctx
->dest_mask
[j
])) {
181 /* Backslash followed by non-digit */
182 switch (ctx
->dest_mask
[j
]) {
184 case_conv
|= UP_SECT
;
185 case_conv
&= ~LOW_SECT
;
188 case_conv
|= UP_CHAR
;
191 case_conv
|= LOW_SECT
;
192 case_conv
&= ~UP_SECT
;
195 case_conv
|= LOW_CHAR
;
201 /* Backslash as quote mark */
203 convert_case (ctx
->dest_mask
[j
], &case_conv
);
207 /* Backslash followed by digit */
208 next_reg
= ctx
->dest_mask
[j
] - '0';
213 if (next_reg
< 0 || next_reg
>= RE_NREGS
214 || ctx
->regs
.start
[next_reg
] < 0) {
215 message (1, MSG_ERROR
, _(" Invalid target mask "));
216 transform_error
= FILE_ABORT
;
219 for (l
= (size_t) ctx
->regs
.start
[next_reg
];
220 l
< (size_t) ctx
->regs
.end
[next_reg
]; l
++)
221 fntarget
[k
++] = convert_case (fnsource
[l
], &case_conv
);
226 fntarget
[k
++] = convert_case (ctx
->dest_mask
[j
], &case_conv
);
235 transform_source (FileOpContext
*ctx
, const char *source
)
237 char *s
= g_strdup (source
);
241 /* We remove \n from the filename since regex routines would use \n as an anchor */
242 /* this is just to be allowed to maniupulate file names with \n on it */
243 for (q
= s
; *q
; q
++) {
247 p
= do_transform_source (ctx
, s
);
253 free_linklist (struct link
**linklist
)
255 struct link
*lp
, *lp2
;
257 for (lp
= *linklist
; lp
!= NULL
; lp
= lp2
) {
265 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
267 ino_t ino
= sb
->st_ino
;
268 dev_t dev
= sb
->st_dev
;
270 struct vfs_class
*vfs
= vfs_get_class (path
);
277 if (lp
->ino
== ino
&& lp
->dev
== dev
)
285 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
286 * and a hardlink was succesfully made
289 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
292 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
293 ino_t ino
= pstat
->st_ino
;
294 dev_t dev
= pstat
->st_dev
;
295 struct stat link_stat
;
298 if (vfs_file_class_flags (src_name
) & VFSF_NOLINKS
)
301 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
302 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
) {
303 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
304 && link_stat
.st_dev
== dev
305 && vfs_get_class (lp
->name
) == my_vfs
) {
306 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
308 if (vfs_get_class (dst_name
) == vfs_get_class (p
)) {
309 if (!mc_stat (p
, &link_stat
)) {
310 if (!mc_link (p
, dst_name
))
315 message (1, MSG_ERROR
, _(" Cannot make the hardlink "));
318 lp
= (struct link
*) g_malloc (sizeof (struct link
) + strlen (src_name
)
319 + strlen (dst_name
) + 1);
325 strcpy (lp
->name
, src_name
);
326 lpdstname
= lp
->name
+ strlen(lp
->name
) + 1;
327 strcpy (lpdstname
, dst_name
);
335 * Duplicate the contents of the symbolic link src_path in dst_path.
336 * Try to make a stable symlink if the option "stable symlink" was
337 * set in the file mask dialog.
338 * If dst_path is an existing symlink it will be deleted silently
339 * (upper levels take already care of existing files at dst_path).
342 make_symlink (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
)
344 char link_target
[MC_MAXPATHLEN
];
350 if (mc_lstat (dst_path
, &sb
) == 0 && S_ISLNK (sb
.st_mode
))
356 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
359 file_error (_(" Cannot read source link \"%s\" \n %s "),
361 if (return_status
== FILE_RETRY
)
362 goto retry_src_readlink
;
363 return return_status
;
365 link_target
[len
] = 0;
367 if (ctx
->stable_symlinks
)
368 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
369 message (1, MSG_ERROR
,
370 _(" Cannot make stable symlinks across "
371 "non-local filesystems: \n\n"
372 " Option Stable Symlinks will be disabled "));
373 ctx
->stable_symlinks
= 0;
376 if (ctx
->stable_symlinks
&& *link_target
!= PATH_SEP
) {
379 const char *r
= strrchr (src_path
, PATH_SEP
);
382 p
= g_strndup (src_path
, r
- src_path
+ 1);
383 if (*dst_path
== PATH_SEP
)
384 q
= g_strdup (dst_path
);
386 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
387 s
= strrchr (q
, PATH_SEP
);
390 s
= g_strconcat (p
, link_target
, (char *) NULL
);
392 g_strlcpy (link_target
, s
, sizeof (link_target
));
394 s
= diff_two_paths (q
, link_target
);
396 g_strlcpy (link_target
, s
, sizeof (link_target
));
405 if (mc_symlink (link_target
, dst_path
) == 0)
409 * if dst_exists, it is obvious that this had failed.
410 * We can delete the old symlink and try again...
412 if (dst_is_symlink
) {
413 if (!mc_unlink (dst_path
))
414 if (mc_symlink (link_target
, dst_path
) == 0)
419 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
421 if (return_status
== FILE_RETRY
)
422 goto retry_dst_symlink
;
423 return return_status
;
427 progress_update_one (FileOpContext
*ctx
,
428 off_t
*progress_count
,
429 double *progress_bytes
, off_t add
, int is_toplevel_file
)
433 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
435 (*progress_bytes
) += add
;
438 /* Apply some heuristic here to not call the update stuff very often */
440 file_progress_show_count (ctx
, *progress_count
,
441 ctx
->progress_count
);
442 if (ret
!= FILE_CONT
)
445 file_progress_show_bytes (ctx
, *progress_bytes
,
446 ctx
->progress_bytes
);
451 /* Status of the destination file */
453 DEST_NONE
, /* Not created */
454 DEST_SHORT
, /* Created, not fully copied */
455 DEST_FULL
/* Created, fully copied */
459 copy_file_file (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
,
460 int ask_overwrite
, off_t
*progress_count
,
461 double *progress_bytes
, int is_toplevel_file
)
463 uid_t src_uid
= (uid_t
) - 1;
464 gid_t src_gid
= (gid_t
) - 1;
467 int buf_size
= BUF_8K
;
468 int src_desc
, dest_desc
= -1;
469 int n_read
, n_written
;
470 mode_t src_mode
= 0; /* The mode of the source file */
473 int dst_exists
= 0, appending
= 0;
474 off_t n_read_total
= 0, file_size
= -1;
475 int return_status
, temp_status
;
476 struct timeval tv_transfer_start
;
477 int dst_status
= DEST_NONE
; /* 1 if the file is not fully copied */
479 /* FIXME: We should not be using global variables! */
481 return_status
= FILE_RETRY
;
483 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
484 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
489 while (mc_stat (dst_path
, &sb2
) == 0) {
490 if (S_ISDIR (sb2
.st_mode
)) {
492 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
494 if (return_status
== FILE_RETRY
)
496 return return_status
;
502 while ((*ctx
->stat_func
) (src_path
, &sb
)) {
504 file_error (_(" Cannot stat source file \"%s\" \n %s "),
506 if (return_status
!= FILE_RETRY
)
507 return return_status
;
511 /* Destination already exists */
512 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
) {
513 message (1, MSG_ERROR
,
514 _(" `%s' and `%s' are the same file "), src_path
, dst_path
);
519 /* Should we replace destination? */
522 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
523 if (return_status
!= FILE_CONT
)
524 return return_status
;
528 if (!ctx
->do_append
) {
529 /* Check the hardlinks */
530 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
531 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
532 /* We have made a hardlink - no more processing is necessary */
536 if (S_ISLNK (sb
.st_mode
)) {
539 retval
= make_symlink (ctx
, src_path
, dst_path
);
543 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
544 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) ||
545 S_ISSOCK (sb
.st_mode
)) {
546 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
548 return_status
= file_error (
549 _(" Cannot create special file \"%s\" \n %s "), dst_path
);
550 if (return_status
== FILE_RETRY
)
552 return return_status
;
556 while (ctx
->preserve_uidgid
557 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
558 temp_status
= file_error (
559 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
560 if (temp_status
== FILE_RETRY
)
564 while (ctx
->preserve
&&
565 mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
)) {
566 temp_status
= file_error (
567 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
568 if (temp_status
== FILE_RETRY
)
576 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
578 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
579 return_status
= file_error (
580 _(" Cannot open source file \"%s\" \n %s "), src_path
);
581 if (return_status
== FILE_RETRY
)
584 return return_status
;
588 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
) {
589 message (1, _("Warning"),
590 _(" Reget failed, about to overwrite file "));
591 ctx
->do_reget
= ctx
->do_append
= 0;
595 while (mc_fstat (src_desc
, &sb
)) {
596 return_status
= file_error (
597 _(" Cannot fstat source file \"%s\" \n %s "), src_path
);
598 if (return_status
== FILE_RETRY
)
603 src_mode
= sb
.st_mode
;
606 utb
.actime
= sb
.st_atime
;
607 utb
.modtime
= sb
.st_mtime
;
608 file_size
= sb
.st_size
;
610 /* Create the new regular file with small permissions initially,
611 do not create a security hole. FIXME: You have security hole
612 here, btw. Imagine copying to /tmp and symlink attack :-( */
614 while ((dest_desc
= mc_open (dst_path
, O_WRONLY
| (ctx
->do_append
?
615 O_APPEND
: (O_CREAT
| O_TRUNC
)), 0600)) < 0) {
616 return_status
= file_error (
617 _(" Cannot create target file \"%s\" \n %s "), dst_path
);
618 if (return_status
== FILE_RETRY
)
623 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
625 appending
= ctx
->do_append
;
628 /* Find out the optimal buffer size. */
629 while (mc_fstat (dest_desc
, &sb
)) {
630 return_status
= file_error (
631 _(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
632 if (return_status
== FILE_RETRY
)
636 buf
= g_malloc (buf_size
);
641 return_status
= file_progress_show (ctx
, 0, file_size
);
645 if (return_status
!= FILE_CONT
)
649 struct timeval tv_current
, tv_last_update
, tv_last_input
;
650 int secs
, update_secs
;
652 const char *stalled_msg
;
654 tv_last_update
= tv_transfer_start
;
658 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
661 while ((n_read
= mc_read (src_desc
, buf
, buf_size
)) < 0) {
662 return_status
= file_error (
663 _(" Cannot read source file \"%s\" \n %s "), src_path
);
664 if (return_status
== FILE_RETRY
)
671 gettimeofday (&tv_current
, NULL
);
675 n_read_total
+= n_read
;
677 /* Windows NT ftp servers report that files have no
678 * permissions: -------, so if we happen to have actually
679 * read something, we should fix the permissions.
681 if (!(src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)))
682 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
683 gettimeofday (&tv_last_input
, NULL
);
687 mc_write (dest_desc
, t
, n_read
)) < n_read
) {
694 file_error (_(" Cannot write target file \"%s\" \n %s "),
696 if (return_status
!= FILE_RETRY
)
701 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
702 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
705 tv_last_update
= tv_current
;
708 /* 2. Check for a stalled condition */
709 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
711 if (update_secs
> 4) {
712 stalled_msg
= _("(stalled)");
717 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
721 ((dt
/ (double) n_read_total
) * file_size
) - dt
;
722 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
727 /* 4. Compute BPS rate */
730 (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
731 if (ctx
->bps_time
< 1)
733 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
736 file_progress_set_stalled_label (ctx
, stalled_msg
);
737 return_status
= file_progress_show_bytes (ctx
, *progress_bytes
+
738 n_read_total
+ ctx
->do_reget
, ctx
->progress_bytes
);
739 if (return_status
== FILE_CONT
) {
741 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
);
744 if (return_status
!= FILE_CONT
)
749 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
754 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
755 temp_status
= file_error (
756 _(" Cannot close source file \"%s\" \n %s "), src_path
);
757 if (temp_status
== FILE_RETRY
)
759 if (temp_status
== FILE_ABORT
)
760 return_status
= temp_status
;
764 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
765 temp_status
= file_error (
766 _(" Cannot close target file \"%s\" \n %s "), dst_path
);
767 if (temp_status
== FILE_RETRY
)
769 return_status
= temp_status
;
773 if (dst_status
== DEST_SHORT
) {
774 /* Remove short file */
776 result
= query_dialog (_("Copy"),
777 _("Incomplete file was retrieved. Keep it?"),
778 D_ERROR
, 2, _("&Delete"), _("&Keep"));
780 mc_unlink (dst_path
);
781 } else if (dst_status
== DEST_FULL
) {
782 /* Copy has succeeded */
783 if (!appending
&& ctx
->preserve_uidgid
) {
784 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
785 temp_status
= file_error (
786 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
787 if (temp_status
== FILE_RETRY
)
789 return_status
= temp_status
;
795 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
))) {
796 temp_status
= file_error (
797 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
798 if (temp_status
!= FILE_RETRY
) {
799 return_status
= temp_status
;
803 mc_utime (dst_path
, &utb
);
807 if (return_status
== FILE_CONT
)
809 progress_update_one (ctx
, progress_count
, progress_bytes
,
810 file_size
, is_toplevel_file
);
812 return return_status
;
816 * I think these copy_*_* functions should have a return type.
817 * anyway, this function *must* have two directories as arguments.
819 /* FIXME: This function needs to check the return values of the
822 copy_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
, int toplevel
,
823 int move_over
, int delete, struct link
*parent_dirs
,
824 off_t
*progress_count
, double *progress_bytes
)
827 struct stat buf
, cbuf
;
829 char *path
, *mdpath
, *dest_file
, *dest_dir
;
830 int return_status
= FILE_CONT
;
834 /* First get the mode of the source dir */
836 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
838 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
839 if (return_status
== FILE_RETRY
)
841 return return_status
;
844 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
845 /* Don't copy a directory we created before (we don't want to copy
846 infinitely if a directory is copied into itself) */
847 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
851 /* Hmm, hardlink to directory??? - Norbert */
852 /* FIXME: In this step we should do something
853 in case the destination already exist */
854 /* Check the hardlinks */
855 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
856 && check_hardlinks (s
, d
, &cbuf
) == 1) {
857 /* We have made a hardlink - no more processing is necessary */
858 return return_status
;
861 if (!S_ISDIR (cbuf
.st_mode
)) {
863 file_error (_(" Source \"%s\" is not a directory \n %s "), s
);
864 if (return_status
== FILE_RETRY
)
866 return return_status
;
869 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
870 /* we found a cyclic symbolic link */
871 message (1, MSG_ERROR
,
872 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
876 lp
= g_new (struct link
, 1);
877 lp
->vfs
= vfs_get_class (s
);
878 lp
->ino
= cbuf
.st_ino
;
879 lp
->dev
= cbuf
.st_dev
;
880 lp
->next
= parent_dirs
;
884 /* Now, check if the dest dir exists, if not, create it. */
885 if (mc_stat (d
, &buf
)) {
886 /* Here the dir doesn't exist : make it ! */
889 if (mc_rename (s
, d
) == 0) {
890 g_free (parent_dirs
);
894 dest_dir
= g_strdup (d
);
897 * If the destination directory exists, we want to copy the whole
898 * directory, but we only want this to happen once.
900 * Escape sequences added to the * to compiler warnings.
901 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
902 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
904 if (!S_ISDIR (buf
.st_mode
)) {
905 return_status
= file_error(
906 _(" Destination \"%s\" must be a directory \n %s "), d
);
907 if (return_status
== FILE_RETRY
)
909 g_free (parent_dirs
);
910 return return_status
;
912 /* Dive into subdir if exists */
913 if (toplevel
&& ctx
->dive_into_subdirs
) {
914 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
916 dest_dir
= g_strdup (d
);
920 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
921 return_status
= file_error (
922 _(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
923 if (return_status
!= FILE_RETRY
)
927 lp
= g_new (struct link
, 1);
928 mc_stat (dest_dir
, &buf
);
929 lp
->vfs
= vfs_get_class (dest_dir
);
930 lp
->ino
= buf
.st_ino
;
931 lp
->dev
= buf
.st_dev
;
932 lp
->next
= dest_dirs
;
935 if (ctx
->preserve_uidgid
) {
936 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
937 return_status
= file_error (
938 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
939 if (return_status
!= FILE_RETRY
)
945 /* open the source dir for reading */
946 if ((reading
= mc_opendir (s
)) == 0) {
950 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
952 * Now, we don't want '.' and '..' to be created / copied at any time
954 if (!strcmp (next
->d_name
, "."))
956 if (!strcmp (next
->d_name
, ".."))
959 /* get the filename and add it to the src directory */
960 path
= concat_dir_and_file (s
, next
->d_name
);
962 (*ctx
->stat_func
) (path
, &buf
);
963 if (S_ISDIR (buf
.st_mode
)) {
964 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
966 * From here, we just intend to recursively copy subdirs, not
967 * the double functionality of copying different when the target
968 * dir already exists. So, we give the recursive call the flag 0
969 * meaning no toplevel.
971 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0, delete,
972 parent_dirs
, progress_count
, progress_bytes
);
975 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
976 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
977 progress_count
, progress_bytes
, 0);
980 if (delete && return_status
== FILE_CONT
) {
981 if (ctx
->erase_at_end
) {
982 static struct link
*tail
;
983 lp
= g_malloc (sizeof (struct link
) + strlen (path
));
984 strcpy (lp
->name
, path
);
985 lp
->st_mode
= buf
.st_mode
;
991 erase_list
= tail
= lp
;
993 if (S_ISDIR (buf
.st_mode
)) {
994 return_status
= erase_dir_iff_empty (ctx
, path
);
996 return_status
= erase_file (ctx
, path
, 0, 0, 0);
1001 mc_closedir (reading
);
1003 if (ctx
->preserve
) {
1004 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1005 utb
.actime
= cbuf
.st_atime
;
1006 utb
.modtime
= cbuf
.st_mtime
;
1007 mc_utime (dest_dir
, &utb
);
1012 g_free (parent_dirs
);
1013 return return_status
;
1018 /* {{{ Move routines */
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 int 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 int msize
= COLS
- 36;
1046 char st
[MC_MAXPATHLEN
];
1047 char dt
[MC_MAXPATHLEN
];
1053 strcpy (st
, path_trunc (s
, msize
));
1054 strcpy (dt
, path_trunc (d
, msize
));
1055 message (1, MSG_ERROR
,
1056 _(" `%s' and `%s' are the same file "), st
, dt
);
1061 if (S_ISDIR (dst_stats
.st_mode
)) {
1062 message (1, MSG_ERROR
,
1063 _(" Cannot overwrite directory `%s' "), d
);
1068 if (confirm_overwrite
) {
1069 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1070 if (return_status
!= FILE_CONT
)
1071 return return_status
;
1073 /* Ok to overwrite */
1076 if (!ctx
->do_append
) {
1077 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1078 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1079 goto retry_src_remove
;
1081 return return_status
;
1084 if (mc_rename (s
, d
) == 0) {
1085 return progress_update_one (ctx
, progress_count
,
1087 src_stats
.st_size
, 1);
1091 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1092 one nfs to the same, but on the server it is on two different
1093 filesystems. Then nfs returns EIO instead of EXDEV.
1094 Hope it will not hurt if we always in case of error try to copy/delete. */
1096 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1098 if (errno
!= EXDEV
) {
1100 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1102 if (return_status
== FILE_RETRY
)
1104 return return_status
;
1108 /* Failed because filesystem boundary -> copy the file instead */
1110 copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1111 if (return_status
!= FILE_CONT
)
1112 return return_status
;
1116 if ((return_status
=
1117 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1118 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1119 return return_status
;
1124 if (mc_unlink (s
)) {
1126 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1127 if (return_status
== FILE_RETRY
)
1128 goto retry_src_remove
;
1129 return return_status
;
1133 return_status
= progress_update_one (ctx
,
1136 src_stats
.st_size
, 1);
1139 return return_status
;
1143 move_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
,
1144 off_t
*progress_count
, double *progress_bytes
)
1146 struct stat sbuf
, dbuf
, destbuf
;
1152 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1153 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1159 if (mc_stat (d
, &dbuf
))
1160 destdir
= g_strdup (d
); /* destination doesn't exist */
1161 else if (!ctx
->dive_into_subdirs
) {
1162 destdir
= g_strdup (d
);
1165 destdir
= concat_dir_and_file (d
, x_basename (s
));
1167 if (sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
) {
1168 int msize
= COLS
- 36;
1169 char st
[MC_MAXPATHLEN
];
1170 char dt
[MC_MAXPATHLEN
];
1176 strcpy (st
, path_trunc (s
, msize
));
1177 strcpy (dt
, path_trunc (d
, msize
));
1178 message (1, MSG_ERROR
,
1179 _(" `%s' and `%s' are the same directory "), st
, dt
);
1184 /* Check if the user inputted an existing dir */
1186 if (!mc_stat (destdir
, &destbuf
)) {
1188 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1189 progress_count
, progress_bytes
);
1191 if (return_status
!= FILE_CONT
)
1195 if (S_ISDIR (destbuf
.st_mode
))
1198 (" Cannot overwrite directory \"%s\" %s "),
1202 file_error (_(" Cannot overwrite file \"%s\" %s "),
1204 if (return_status
== FILE_RETRY
)
1205 goto retry_dst_stat
;
1208 return return_status
;
1212 if (mc_rename (s
, destdir
) == 0) {
1213 return_status
= FILE_CONT
;
1217 if (errno
!= EXDEV
) {
1220 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1222 if (return_status
== FILE_RETRY
)
1226 /* Failed because of filesystem boundary -> copy dir instead */
1228 copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
,
1231 if (return_status
!= FILE_CONT
)
1234 if ((return_status
=
1235 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1236 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1240 if (ctx
->erase_at_end
) {
1241 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1242 if (S_ISDIR (erase_list
->st_mode
)) {
1244 erase_dir_iff_empty (ctx
, erase_list
->name
);
1247 erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1249 erase_list
= erase_list
->next
;
1253 erase_dir_iff_empty (ctx
, s
);
1257 while (erase_list
) {
1259 erase_list
= erase_list
->next
;
1262 return return_status
;
1267 /* {{{ Erase routines */
1268 /* Don't update progress status if progress_count==NULL */
1270 erase_file (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1271 double *progress_bytes
, int is_toplevel_file
)
1276 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1280 if (progress_count
&& mc_lstat (s
, &buf
)) {
1281 /* ignore, most likely the mc_unlink fails, too */
1285 while (mc_unlink (s
)) {
1287 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1288 if (return_status
!= FILE_RETRY
)
1289 return return_status
;
1293 return progress_update_one (ctx
, progress_count
, progress_bytes
,
1294 buf
.st_size
, is_toplevel_file
);
1300 recursive_erase (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1301 double *progress_bytes
)
1303 struct dirent
*next
;
1307 int return_status
= FILE_CONT
;
1309 if (!strcmp (s
, ".."))
1312 reading
= mc_opendir (s
);
1317 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1318 if (!strcmp (next
->d_name
, "."))
1320 if (!strcmp (next
->d_name
, ".."))
1322 path
= concat_dir_and_file (s
, next
->d_name
);
1323 if (mc_lstat (path
, &buf
)) {
1325 mc_closedir (reading
);
1328 if (S_ISDIR (buf
.st_mode
))
1331 (ctx
, path
, progress_count
, progress_bytes
)
1335 erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1338 mc_closedir (reading
);
1339 if (return_status
!= FILE_CONT
)
1340 return return_status
;
1341 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1345 while (my_rmdir (s
)) {
1347 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1348 if (return_status
!= FILE_RETRY
)
1349 return return_status
;
1355 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1356 in the directory path points to, 0 else. */
1358 check_dir_is_empty (const char *path
)
1364 dir
= mc_opendir (path
);
1368 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1369 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1370 (d
->d_name
[1] == '.'
1371 && d
->d_name
[2] == '\0')))
1372 continue; /* "." or ".." */
1382 erase_dir (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1383 double *progress_bytes
)
1387 if (strcmp (s
, "..") == 0)
1390 if (strcmp (s
, ".") == 0)
1393 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1397 /* The old way to detect a non empty directory was:
1398 error = my_rmdir (s);
1399 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1400 For the linux user space nfs server (nfs-server-2.2beta29-2)
1401 we would have to check also for EIO. I hope the new way is
1402 fool proof. (Norbert)
1404 error
= check_dir_is_empty (s
);
1405 if (error
== 0) { /* not empty */
1406 error
= query_recursive (ctx
, s
);
1407 if (error
== FILE_CONT
)
1408 return recursive_erase (ctx
, s
, progress_count
,
1414 while (my_rmdir (s
) == -1) {
1416 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1417 if (error
!= FILE_RETRY
)
1425 erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
)
1429 if (strcmp (s
, "..") == 0)
1432 if (strcmp (s
, ".") == 0)
1435 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1439 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1442 while (my_rmdir (s
)) {
1444 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1445 if (error
!= FILE_RETRY
)
1454 /* {{{ Panel operate routines */
1457 * Return currently selected entry name or the name of the first marked
1458 * entry if there is one.
1461 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1465 if (get_current_type () == view_tree
) {
1466 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1467 char *tree_name
= tree_selected_name (tree
);
1469 mc_stat (tree_name
, stat_buf
);
1473 if (panel
->marked
) {
1474 for (i
= 0; i
< panel
->count
; i
++)
1475 if (panel
->dir
.list
[i
].f
.marked
) {
1476 *stat_buf
= panel
->dir
.list
[i
].st
;
1477 return panel
->dir
.list
[i
].fname
;
1480 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1481 return panel
->dir
.list
[panel
->selected
].fname
;
1483 g_assert_not_reached ();
1490 * Computes the number of bytes used by the files in a directory
1493 compute_dir_size (const char *dirname
, off_t
*ret_marked
, double *ret_total
)
1496 struct dirent
*dirent
;
1498 dir
= mc_opendir (dirname
);
1503 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1508 if (strcmp (dirent
->d_name
, ".") == 0)
1510 if (strcmp (dirent
->d_name
, "..") == 0)
1513 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1515 res
= mc_lstat (fullname
, &s
);
1522 if (S_ISDIR (s
.st_mode
)) {
1523 off_t subdir_count
= 0;
1524 double subdir_bytes
= 0;
1526 compute_dir_size (fullname
, &subdir_count
, &subdir_bytes
);
1528 *ret_marked
+= subdir_count
;
1529 *ret_total
+= subdir_bytes
;
1532 *ret_total
+= s
.st_size
;
1541 * panel_compute_totals:
1543 * compute the number of files and the number of bytes
1544 * used up by the whole selection, recursing directories
1545 * as required. In addition, it checks to see if it will
1546 * overwrite any files by doing the copy.
1549 panel_compute_totals (WPanel
*panel
, off_t
*ret_marked
, double *ret_total
)
1556 for (i
= 0; i
< panel
->count
; i
++) {
1559 if (!panel
->dir
.list
[i
].f
.marked
)
1562 s
= &panel
->dir
.list
[i
].st
;
1564 if (S_ISDIR (s
->st_mode
)) {
1566 off_t subdir_count
= 0;
1567 double subdir_bytes
= 0;
1570 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1571 compute_dir_size (dir_name
, &subdir_count
, &subdir_bytes
);
1573 *ret_marked
+= subdir_count
;
1574 *ret_total
+= subdir_bytes
;
1578 *ret_total
+= s
->st_size
;
1584 * This array introduced to avoid translation problems. The former (op_names)
1585 * is assumed to be nouns, suitable in dialog box titles; this one should
1586 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1587 * Notice first symbol - it is to fool gettext and force these strings to
1588 * be different for it. First symbol is skipped while building a prompt.
1589 * (I don't use spaces around the words, because someday they could be
1590 * dropped, when widgets get smarter)
1592 static const char *op_names1
[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1595 int fmd_xlen
= FMD_XLEN
;
1598 * These are formats for building a prompt. Parts encoded as follows:
1599 * %o - operation from op_names1
1600 * %f - file/files or files/directories, as appropriate
1601 * %m - "with source mask" or question mark for delete
1602 * %s - source name (truncated)
1603 * %d - number of marked files
1604 * %e - "to:" or question mark for delete
1606 * xgettext:no-c-format */
1607 static const char *one_format
= N_("%o %f \"%s\"%m");
1608 /* xgettext:no-c-format */
1609 static const char *many_format
= N_("%o %d %f%m");
1610 static const char *prompt_parts
[] = {
1611 N_("file"), N_("files"), N_("directory"), N_("directories"),
1612 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1616 * Generate user prompt for panel operation.
1617 * single_source is the name if the source entry or NULL for multiple
1619 * src_stat is only used when single_source is not NULL.
1622 panel_operate_generate_prompt (const WPanel
*panel
, const int operation
,
1623 const char *single_source
,
1624 const struct stat
*src_stat
)
1626 register const char *sp
, *cp
;
1628 char format_string
[BUF_MEDIUM
];
1629 char *dp
= format_string
;
1632 static int i18n_flag
= 0;
1634 fmd_init_i18n (FALSE
); /* to get proper fmd_xlen */
1636 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1637 op_names1
[i
] = _(op_names1
[i
]);
1639 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1640 prompt_parts
[i
] = _(prompt_parts
[i
]);
1642 one_format
= _(one_format
);
1643 many_format
= _(many_format
);
1646 #endif /* ENABLE_NLS */
1648 sp
= single_source
? one_format
: many_format
;
1656 cp
= op_names1
[operation
] + 1;
1659 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1662 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[6];
1665 if (single_source
) {
1666 cp
= S_ISDIR (src_stat
->
1667 st_mode
) ? prompt_parts
[2] :
1670 cp
= (panel
->marked
== panel
->dirs_marked
)
1672 : (panel
->dirs_marked
? prompt_parts
[4]
1691 if (single_source
) {
1692 i
= fmd_xlen
- strlen (format_string
) - 4;
1693 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1694 name_trunc (single_source
, i
));
1696 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1698 i
= strlen (cmd_buf
) + 6 - fmd_xlen
;
1701 fmd_init_i18n (TRUE
); /* to recalculate positions of child widgets */
1709 * Performs one of the operations on the selection on the source_panel
1710 * (copy, delete, move).
1712 * Returns 1 if did change the directory
1713 * structure, Returns 0 if user aborted
1715 * force_single forces operation on the current entry and affects
1716 * default destination. Current filename is used as default.
1719 panel_operate (void *source_panel
, FileOperation operation
,
1722 WPanel
*panel
= source_panel
;
1723 #ifdef WITH_FULL_PATHS
1724 char *source_with_path
= NULL
;
1726 # define source_with_path source
1727 #endif /* !WITH_FULL_PATHS */
1728 char *source
= NULL
;
1730 const char *temp
= NULL
;
1731 char *save_cwd
= NULL
, *save_dest
= NULL
;
1732 int single_entry
= (get_current_type () == view_tree
)
1733 || (panel
->marked
<= 1) || force_single
;
1734 struct stat src_stat
, dst_stat
;
1742 int do_bg
= 0; /* do background operation? */
1744 free_linklist (&linklist
);
1745 free_linklist (&dest_dirs
);
1749 source
= selection (panel
)->fname
;
1750 src_stat
= selection (panel
)->st
;
1752 source
= panel_get_file (panel
, &src_stat
);
1755 if (!strcmp (source
, "..")) {
1756 message (1, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1761 /* Generate confirmation prompt */
1762 panel_operate_generate_prompt (panel
, operation
, source
, &src_stat
);
1764 ctx
= file_op_context_new (operation
);
1766 /* Show confirmation dialog */
1767 if (operation
== OP_DELETE
&& confirm_delete
) {
1771 i
= query_dialog (_(op_names
[operation
]), cmd_buf
, D_ERROR
, 2,
1772 _("&Yes"), _("&No"));
1775 file_op_context_destroy (ctx
);
1778 } else if (operation
!= OP_DELETE
) {
1781 /* Forced single operations default to the original name */
1784 else if (get_other_type () == view_listing
)
1785 dest_dir
= other_panel
->cwd
;
1787 dest_dir
= panel
->cwd
;
1790 file_mask_dialog (ctx
, operation
, cmd_buf
, dest_dir
,
1791 single_entry
, &do_bg
);
1793 file_op_context_destroy (ctx
);
1797 file_op_context_destroy (ctx
);
1802 #ifdef WITH_BACKGROUND
1803 /* Did the user select to do a background operation? */
1807 v
= do_background (ctx
,
1808 g_strconcat (op_names
[operation
], ": ",
1811 message (1, MSG_ERROR
,
1812 _(" Sorry, I could not put the job in background "));
1815 /* If we are the parent */
1817 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
1818 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
1819 /* file_op_context_destroy (ctx); */
1823 #endif /* WITH_BACKGROUND */
1825 /* Initialize things */
1826 /* We do not want to trash cache every time file is
1827 created/touched. However, this will make our cache contain
1830 if (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1831 save_dest
= g_strdup (dest
);
1834 if (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1835 save_cwd
= g_strdup (panel
->cwd
);
1838 /* Now, let's do the job */
1843 file_op_context_create_ui (ctx
, 1);
1845 /* This code is only called by the tree and panel code */
1847 /* We now have ETA in all cases */
1849 /* One file: FIXME mc_chdir will take user out of any vfs */
1850 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1851 mc_chdir (PATH_SEP_STR
);
1853 /* The source and src_stat variables have been initialized before */
1854 #ifdef WITH_FULL_PATHS
1855 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1856 #endif /* WITH_FULL_PATHS */
1858 if (operation
== OP_DELETE
) {
1859 if (S_ISDIR (src_stat
.st_mode
))
1860 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1863 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1865 temp
= transform_source (ctx
, source_with_path
);
1868 value
= transform_error
;
1870 char *temp2
= concat_dir_and_file (dest
, temp
);
1875 switch (operation
) {
1878 * we use file_mask_op_follow_links only with OP_COPY,
1880 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1882 if (S_ISDIR (src_stat
.st_mode
))
1884 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
1885 0, 0, 0, &count
, &bytes
);
1888 copy_file_file (ctx
, source_with_path
, dest
, 1,
1893 if (S_ISDIR (src_stat
.st_mode
))
1895 move_dir_dir (ctx
, source_with_path
, dest
,
1899 move_file_file (ctx
, source_with_path
, dest
,
1904 /* Unknown file operation */
1908 } /* Copy or move operation */
1910 if ((value
== FILE_CONT
) && !force_single
)
1911 unmark_files (panel
);
1914 /* Check destination for copy or move operation */
1915 if (operation
!= OP_DELETE
) {
1916 retry_many_dst_stat
:
1917 dst_result
= mc_stat (dest
, &dst_stat
);
1918 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
1920 (_(" Destination \"%s\" must be a directory \n %s "),
1921 dest
) == FILE_RETRY
)
1922 goto retry_many_dst_stat
;
1927 /* Initialize variables for progress bars */
1928 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
1929 panel_compute_totals (panel
, &ctx
->progress_count
,
1930 &ctx
->progress_bytes
);
1931 ctx
->progress_totals_computed
= 1;
1933 ctx
->progress_totals_computed
= 0;
1934 ctx
->progress_count
= panel
->marked
;
1935 ctx
->progress_bytes
= panel
->total
;
1938 /* Loop for every file, perform the actual copy operation */
1939 for (i
= 0; i
< panel
->count
; i
++) {
1940 if (!panel
->dir
.list
[i
].f
.marked
)
1941 continue; /* Skip the unmarked ones */
1943 source
= panel
->dir
.list
[i
].fname
;
1944 src_stat
= panel
->dir
.list
[i
].st
;
1946 #ifdef WITH_FULL_PATHS
1947 g_free (source_with_path
);
1948 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1949 #endif /* WITH_FULL_PATHS */
1951 if (operation
== OP_DELETE
) {
1952 if (S_ISDIR (src_stat
.st_mode
))
1954 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1957 erase_file (ctx
, source_with_path
, &count
, &bytes
,
1960 temp
= transform_source (ctx
, source_with_path
);
1962 value
= transform_error
;
1964 char *temp2
= concat_dir_and_file (dest
, temp
);
1966 switch (operation
) {
1969 * we use file_mask_op_follow_links only with OP_COPY
1971 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1972 if (S_ISDIR (src_stat
.st_mode
))
1974 copy_dir_dir (ctx
, source_with_path
, temp2
,
1975 1, 0, 0, 0, &count
, &bytes
);
1978 copy_file_file (ctx
, source_with_path
,
1979 temp2
, 1, &count
, &bytes
,
1981 free_linklist (&dest_dirs
);
1985 if (S_ISDIR (src_stat
.st_mode
))
1987 move_dir_dir (ctx
, source_with_path
, temp2
,
1991 move_file_file (ctx
, source_with_path
,
1992 temp2
, &count
, &bytes
);
1996 /* Unknown file operation */
2001 } /* Copy or move operation */
2003 if (value
== FILE_ABORT
)
2006 if (value
== FILE_CONT
)
2007 do_file_mark (panel
, i
, 0);
2009 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2014 && file_progress_show_bytes (ctx
, bytes
,
2015 ctx
->progress_bytes
) ==
2019 if (operation
!= OP_DELETE
&& verbose
2020 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2024 } /* Loop for every file */
2025 } /* Many entries */
2030 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2034 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2038 free_linklist (&linklist
);
2039 free_linklist (&dest_dirs
);
2040 #ifdef WITH_FULL_PATHS
2041 g_free (source_with_path
);
2042 #endif /* WITH_FULL_PATHS */
2044 g_free (ctx
->dest_mask
);
2045 ctx
->dest_mask
= NULL
;
2046 #ifdef WITH_BACKGROUND
2047 /* Let our parent know we are saying bye bye */
2048 if (we_are_background
) {
2052 #endif /* WITH_BACKGROUND */
2054 file_op_context_destroy (ctx
);
2060 /* {{{ Query/status report routines */
2063 real_do_file_error (enum OperationMode mode
, const char *error
)
2068 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2070 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2088 /* Report error with one file */
2090 file_error (const char *format
, const char *file
)
2092 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
,
2093 path_trunc (file
, 30), unix_error_string (errno
));
2095 return do_file_error (cmd_buf
);
2098 /* Report error with two files */
2100 files_error (const char *format
, const char *file1
, const char *file2
)
2105 strcpy (nfile1
, path_trunc (file1
, 15));
2106 strcpy (nfile2
, path_trunc (file2
, 15));
2108 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
, nfile1
, nfile2
,
2109 unix_error_string (errno
));
2111 return do_file_error (cmd_buf
);
2115 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, const char *s
)
2119 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2123 _("\n Directory not empty. \n"
2124 " Delete it recursively? ")
2125 : _("\n Background process: Directory not empty \n"
2126 " Delete it recursively? ");
2127 text
= g_strconcat (_(" Delete: "), path_trunc (s
, 30), " ", (char *) NULL
);
2131 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2132 _("&Yes"), _("&No"),
2133 _("A&ll"), _("Non&e"),
2136 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2141 switch (ctx
->recursive_result
) {
2143 case RECURSIVE_ALWAYS
:
2147 case RECURSIVE_NEVER
:
2150 case RECURSIVE_ABORT
:
2157 #ifdef WITH_BACKGROUND
2159 do_file_error (const char *str
)
2161 if (we_are_background
)
2162 return parent_call (real_do_file_error
, NULL
, 1, strlen (str
),
2165 return real_do_file_error (Foreground
, str
);
2169 query_recursive (FileOpContext
*ctx
, const char *s
)
2171 if (we_are_background
)
2172 return parent_call (real_query_recursive
, ctx
, 1, strlen (s
), s
);
2174 return real_query_recursive (ctx
, Foreground
, s
);
2178 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2179 struct stat
*_d_stat
)
2181 if (we_are_background
)
2182 return parent_call ((void *) file_progress_real_query_replace
,
2185 strlen (destname
), destname
,
2186 sizeof (struct stat
), _s_stat
,
2187 sizeof (struct stat
), _d_stat
);
2189 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2195 do_file_error (const char *str
)
2197 return real_do_file_error (Foreground
, str
);
2201 query_recursive (FileOpContext
*ctx
, const char *s
)
2203 return real_query_recursive (ctx
, Foreground
, s
);
2207 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2208 struct stat
*_d_stat
)
2210 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2214 #endif /* !WITH_BACKGROUND */
2217 Cause emacs to enter folding mode for this file: