2 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
4 Written by: 1994, 1995 Janne Kukonlehto
5 1994, 1995 Fred Leeflang
6 1994, 1995, 1996 Miguel de Icaza
7 1995, 1996 Jakub Jelinek
11 The copy code was based in GNU's cp, and was written by:
12 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
14 The move code was based in GNU's mv, and was written by:
15 Mike Parker and David MacKenzie.
17 Janne Kukonlehto added much error recovery to them for being used
18 in an interactive program.
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation; either version 2 of the License, or
23 (at your option) any later version.
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program; if not, write to the Free Software
32 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
35 * Please note that all dialogs used here must be safe for background
39 /* {{{ Include files */
42 /* Hack: the vfs code should not rely on this */
43 #define WITH_FULL_PATHS 1
45 #include <sys/types.h>
52 #endif /* HAVE_UNISTD_H */
64 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
68 #include "background.h" /* we_are_background */
70 /* Needed for current_panel, other_panel and WTree */
83 * Whether the Midnight Commander tries to provide more
84 * information about copy/move sizes and bytes transfered
85 * at the expense of some speed
87 int file_op_compute_totals
= 1;
89 /* If on, default for "No" in delete operations */
92 /* This is a hard link cache */
95 struct vfs_class
*vfs
;
103 /* the hard link cache */
104 static struct link
*linklist
= NULL
;
106 /* the files-to-be-erased list */
107 static struct link
*erase_list
;
110 * In copy_dir_dir we use two additional single linked lists: The first -
111 * variable name `parent_dirs' - holds information about already copied
112 * directories and is used to detect cyclic symbolic links.
113 * The second (`dest_dirs' below) holds information about just created
114 * target directories and is used to detect when an directory is copied
115 * into itself (we don't want to copy infinitly).
116 * Both lists don't use the linkcount and name structure members of struct
119 static struct link
*dest_dirs
= 0;
121 char *op_names
[3] = {
129 static int query_replace (FileOpContext
* ctx
, char *destname
,
130 struct stat
*_s_stat
, struct stat
*_d_stat
);
131 static int query_recursive (FileOpContext
* ctx
, char *s
);
132 static int do_file_error (char *str
);
133 static int erase_dir_iff_empty (FileOpContext
*ctx
, char *s
);
134 static int erase_file (FileOpContext
*ctx
, char *s
,
135 off_t
*progress_count
, double *progress_bytes
,
136 int is_toplevel_file
);
137 static int files_error (const char *format
, const char *file1
,
141 enum CaseConvs
{ NO_CONV
= 0, UP_CHAR
= 1, LOW_CHAR
= 2, UP_SECT
=
145 convert_case (int c
, enum CaseConvs
*conversion
)
147 if (*conversion
& UP_CHAR
) {
148 *conversion
&= ~UP_CHAR
;
150 } else if (*conversion
& LOW_CHAR
) {
151 *conversion
&= ~LOW_CHAR
;
153 } else if (*conversion
& UP_SECT
) {
155 } else if (*conversion
& LOW_SECT
) {
161 static int transform_error
= 0;
163 static unsigned char *
164 do_transform_source (FileOpContext
*ctx
, unsigned char *source
)
167 unsigned char *fnsource
= x_basename (source
);
169 enum CaseConvs case_conv
= NO_CONV
;
170 static unsigned char fntarget
[MC_MAXPATHLEN
];
172 len
= strlen (fnsource
);
173 j
= re_match (&ctx
->rx
, fnsource
, len
, 0, &ctx
->regs
);
175 transform_error
= FILE_SKIP
;
178 for (next_reg
= 1, j
= 0, k
= 0; j
< strlen (ctx
->dest_mask
); j
++) {
179 switch (ctx
->dest_mask
[j
]) {
182 if (!isdigit ((unsigned char) ctx
->dest_mask
[j
])) {
183 /* Backslash followed by non-digit */
184 switch (ctx
->dest_mask
[j
]) {
186 case_conv
|= UP_SECT
;
187 case_conv
&= ~LOW_SECT
;
190 case_conv
|= UP_CHAR
;
193 case_conv
|= LOW_SECT
;
194 case_conv
&= ~UP_SECT
;
197 case_conv
|= LOW_CHAR
;
203 /* Backslash as quote mark */
205 convert_case (ctx
->dest_mask
[j
], &case_conv
);
209 /* Backslash followed by digit */
210 next_reg
= ctx
->dest_mask
[j
] - '0';
215 if (next_reg
< 0 || next_reg
>= RE_NREGS
216 || ctx
->regs
.start
[next_reg
] < 0) {
217 message (1, MSG_ERROR
, _(" Invalid target mask "));
218 transform_error
= FILE_ABORT
;
221 for (l
= ctx
->regs
.start
[next_reg
];
222 l
< ctx
->regs
.end
[next_reg
]; l
++)
223 fntarget
[k
++] = convert_case (fnsource
[l
], &case_conv
);
228 fntarget
[k
++] = convert_case (ctx
->dest_mask
[j
], &case_conv
);
236 static unsigned char *
237 transform_source (FileOpContext
*ctx
, unsigned char *source
)
239 unsigned char *s
= g_strdup (source
);
242 /* We remove \n from the filename since regex routines would use \n as an anchor */
243 /* this is just to be allowed to maniupulate file names with \n on it */
244 for (q
= s
; *q
; q
++) {
248 q
= do_transform_source (ctx
, s
);
254 free_linklist (struct link
**linklist
)
256 struct link
*lp
, *lp2
;
258 for (lp
= *linklist
; lp
!= NULL
; lp
= lp2
) {
266 is_in_linklist (struct link
*lp
, char *path
, struct stat
*sb
)
268 ino_t ino
= sb
->st_ino
;
269 dev_t dev
= sb
->st_dev
;
271 struct vfs_class
*vfs
= vfs_get_class (path
);
278 if (lp
->ino
== ino
&& lp
->dev
== dev
)
286 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
287 * and a hardlink was succesfully made
290 check_hardlinks (char *src_name
, char *dst_name
, struct stat
*pstat
)
293 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
294 ino_t ino
= pstat
->st_ino
;
295 dev_t dev
= pstat
->st_dev
;
296 struct stat link_stat
;
299 if (vfs_file_class_flags (src_name
) & VFSF_NOLINKS
)
302 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
303 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
) {
304 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
305 && link_stat
.st_dev
== dev
306 && vfs_get_class (lp
->name
) == my_vfs
) {
307 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
309 if (vfs_get_class (dst_name
) == vfs_get_class (p
)) {
310 if (!mc_stat (p
, &link_stat
)) {
311 if (!mc_link (p
, dst_name
))
316 message (1, MSG_ERROR
, _(" Cannot make the hardlink "));
319 lp
= (struct link
*) g_malloc (sizeof (struct link
) + strlen (src_name
)
320 + strlen (dst_name
) + 1);
325 strcpy (lp
->name
, src_name
);
326 p
= strchr (lp
->name
, 0) + 1;
327 strcpy (p
, 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
, char *src_path
, 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 p
= g_strdup (src_path
);
380 r
= strrchr (p
, PATH_SEP
);
384 if (*dst_path
== PATH_SEP
)
385 q
= g_strdup (dst_path
);
387 q
= g_strconcat (p
, dst_path
, NULL
);
388 r
= strrchr (q
, PATH_SEP
);
391 s
= g_strconcat (p
, link_target
, NULL
);
392 strcpy (link_target
, s
);
394 s
= diff_two_paths (q
, link_target
);
396 strcpy (link_target
, s
);
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
, int 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
, char *src_path
, 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 int 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
)
490 if (mc_stat (dst_path
, &sb2
) == 0) {
491 if (S_ISDIR (sb2
.st_mode
)) {
493 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
495 if (return_status
== FILE_RETRY
)
497 return return_status
;
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
,
520 /* Should we replace destination? */
523 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
524 if (return_status
!= FILE_CONT
)
525 return return_status
;
529 if (!ctx
->do_append
) {
530 /* Check the hardlinks */
531 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
532 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
533 /* We have made a hardlink - no more processing is necessary */
534 return return_status
;
537 if (S_ISLNK (sb
.st_mode
)) {
540 retval
= make_symlink (ctx
, src_path
, dst_path
);
544 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
)
545 || S_ISFIFO (sb
.st_mode
)
546 || S_ISSOCK (sb
.st_mode
)) {
548 (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
552 (" Cannot create special file \"%s\" \n %s "),
554 if (return_status
== FILE_RETRY
)
556 return return_status
;
560 while (ctx
->preserve_uidgid
561 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
564 (" Cannot chown target file \"%s\" \n %s "),
566 if (temp_status
== FILE_RETRY
)
570 while (ctx
->preserve
&&
571 (mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
) <
575 (" Cannot chmod target file \"%s\" \n %s "),
577 if (temp_status
== FILE_RETRY
)
585 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
587 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
589 file_error (_(" Cannot open source file \"%s\" \n %s "),
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
)) {
607 file_error (_(" Cannot fstat source file \"%s\" \n %s "),
609 if (return_status
== FILE_RETRY
)
614 src_mode
= sb
.st_mode
;
617 utb
.actime
= sb
.st_atime
;
618 utb
.modtime
= sb
.st_mtime
;
619 file_size
= sb
.st_size
;
621 /* Create the new regular file with small permissions initially,
622 do not create a security hole. FIXME: You have security hole
623 here, btw. Imagine copying to /tmp and symlink attack :-( */
625 while ((dest_desc
= mc_open (dst_path
, O_WRONLY
|
627 do_append
? O_APPEND
: (O_CREAT
|
631 file_error (_(" Cannot create target file \"%s\" \n %s "),
633 if (return_status
== FILE_RETRY
)
638 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
640 appending
= ctx
->do_append
;
643 /* Find out the optimal buffer size. */
644 while (mc_fstat (dest_desc
, &sb
)) {
646 file_error (_(" Cannot fstat target file \"%s\" \n %s "),
648 if (return_status
== FILE_RETRY
)
652 buf
= (char *) g_malloc (buf_size
);
657 return_status
= file_progress_show (ctx
, 0, file_size
);
661 if (return_status
!= FILE_CONT
)
665 struct timeval tv_current
, tv_last_update
, tv_last_input
;
666 int secs
, update_secs
;
670 tv_last_update
= tv_transfer_start
;
674 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
677 while ((n_read
= mc_read (src_desc
, buf
, buf_size
)) < 0) {
680 (" Cannot read source file \"%s\" \n %s "),
682 if (return_status
== FILE_RETRY
)
689 gettimeofday (&tv_current
, NULL
);
693 n_read_total
+= n_read
;
695 /* Windows NT ftp servers report that files have no
696 * permissions: -------, so if we happen to have actually
697 * read something, we should fix the permissions.
699 if (!(src_mode
& ((S_IRUSR
| S_IWUSR
| S_IXUSR
) /* user */
700 |(S_IXOTH
| S_IWOTH
| S_IROTH
) /* other */
701 |(S_IXGRP
| S_IWGRP
| S_IRGRP
)))) /* group */
702 src_mode
= S_IRUSR
| S_IWUSR
| S_IROTH
| S_IRGRP
;
703 gettimeofday (&tv_last_input
, NULL
);
707 mc_write (dest_desc
, t
, n_read
)) < n_read
) {
715 (" Cannot write target file \"%s\" \n %s "),
717 if (return_status
!= FILE_RETRY
)
722 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
723 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
726 tv_last_update
= tv_current
;
729 /* 2. Check for a stalled condition */
730 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
732 if (update_secs
> 4) {
733 stalled_msg
= _("(stalled)");
738 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
742 ((dt
/ (double) n_read_total
) * file_size
) - dt
;
743 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
748 /* 4. Compute BPS rate */
751 (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
752 if (ctx
->bps_time
< 1)
754 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
757 file_progress_set_stalled_label (ctx
, stalled_msg
);
759 file_progress_show_bytes (ctx
,
760 *progress_bytes
+ n_read_total
,
761 ctx
->progress_bytes
);
762 if (return_status
== FILE_CONT
) {
764 file_progress_show (ctx
, n_read_total
, file_size
);
767 if (return_status
!= FILE_CONT
)
772 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
778 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
780 file_error (_(" Cannot close source file \"%s\" \n %s "),
782 if (temp_status
== FILE_RETRY
)
784 if (temp_status
== FILE_ABORT
)
785 return_status
= temp_status
;
789 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
791 file_error (_(" Cannot close target file \"%s\" \n %s "),
793 if (temp_status
== FILE_RETRY
)
795 return_status
= temp_status
;
799 if (dst_status
== DEST_SHORT
) {
800 /* Remove short file */
803 query_dialog (_("Copy"),
804 _("Incomplete file was retrieved. Keep it?"),
805 D_ERROR
, 2, _("&Delete"), _("&Keep"));
807 mc_unlink (dst_path
);
808 } else if (dst_status
== DEST_FULL
) {
809 /* Copy has succeeded */
810 if (!appending
&& ctx
->preserve_uidgid
) {
811 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
812 temp_status
= file_error
813 (_(" Cannot chown target file \"%s\" \n %s "),
815 if (temp_status
== FILE_RETRY
)
817 return_status
= temp_status
;
823 * .ado: according to the XPG4 standard, the file must be closed before
824 * chmod can be invoked
826 if (!appending
&& ctx
->preserve
) {
827 while (mc_chmod (dst_path
, src_mode
& ctx
->umask_kill
)) {
830 (" Cannot chmod target file \"%s\" \n %s "),
832 if (temp_status
!= FILE_RETRY
) {
833 return_status
= temp_status
;
837 mc_utime (dst_path
, &utb
);
841 if (return_status
== FILE_CONT
)
843 progress_update_one (ctx
, progress_count
, progress_bytes
,
844 file_size
, is_toplevel_file
);
846 return return_status
;
850 * I think these copy_*_* functions should have a return type.
851 * anyway, this function *must* have two directories as arguments.
853 /* FIXME: This function needs to check the return values of the
856 copy_dir_dir (FileOpContext
*ctx
, char *s
, char *d
, int toplevel
,
857 int move_over
, int delete,
858 struct link
*parent_dirs
,
859 off_t
*progress_count
, double *progress_bytes
)
862 struct stat buf
, cbuf
;
864 char *path
, *mdpath
, *dest_file
, *dest_dir
;
865 int return_status
= FILE_CONT
;
869 /* First get the mode of the source dir */
871 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
873 file_error (_(" Cannot stat source directory \"%s\" \n %s "),
875 if (return_status
== FILE_RETRY
)
877 return return_status
;
880 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
881 /* Don't copy a directory we created before (we don't want to copy
882 infinitely if a directory is copied into itself) */
883 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
887 /* Hmm, hardlink to directory??? - Norbert */
888 /* FIXME: In this step we should do something
889 in case the destination already exist */
890 /* Check the hardlinks */
891 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
892 && check_hardlinks (s
, d
, &cbuf
) == 1) {
893 /* We have made a hardlink - no more processing is necessary */
894 return return_status
;
897 if (!S_ISDIR (cbuf
.st_mode
)) {
900 (" Source directory \"%s\" is not a directory \n %s "),
902 if (return_status
== FILE_RETRY
)
904 return return_status
;
907 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
908 /* we found a cyclic symbolic link */
909 message (1, MSG_ERROR
,
910 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
914 lp
= g_new (struct link
, 1);
915 lp
->vfs
= vfs_get_class (s
);
916 lp
->ino
= cbuf
.st_ino
;
917 lp
->dev
= cbuf
.st_dev
;
918 lp
->next
= parent_dirs
;
922 /* Now, check if the dest dir exists, if not, create it. */
923 if (mc_stat (d
, &buf
)) {
924 /* Here the dir doesn't exist : make it ! */
927 if (mc_rename (s
, d
) == 0) {
928 g_free (parent_dirs
);
932 dest_dir
= g_strdup (d
);
935 * If the destination directory exists, we want to copy the whole
936 * directory, but we only want this to happen once.
938 * Escape sequences added to the * to compiler warnings.
939 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
940 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
942 if (!S_ISDIR (buf
.st_mode
)) {
945 (" Destination \"%s\" must be a directory \n %s "),
947 if (return_status
== FILE_RETRY
)
949 g_free (parent_dirs
);
950 return return_status
;
953 /* Again, I'm getting curious. Is not d already what we wanted, incl.
954 * masked source basename? Is not this just a relict of the past versions?
955 * I'm afraid this will lead into a two level deep dive :(
957 * I think this is indeed the problem. I cannot remember any case where
958 * we actually would like that behavior -miguel
960 * It's a documented feature (option `Dive into subdir if exists' in the
961 * copy/move dialog). -Norbert
963 if (toplevel
&& ctx
->dive_into_subdirs
) {
964 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
968 dest_dir
= g_strdup (d
);
973 if (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
975 file_error (_(" Cannot create target directory \"%s\" \n %s "),
977 if (return_status
== FILE_RETRY
)
978 goto retry_dst_mkdir
;
982 lp
= g_new (struct link
, 1);
983 mc_stat (dest_dir
, &buf
);
984 lp
->vfs
= vfs_get_class (dest_dir
);
985 lp
->ino
= buf
.st_ino
;
986 lp
->dev
= buf
.st_dev
;
987 lp
->next
= dest_dirs
;
990 if (ctx
->preserve_uidgid
) {
991 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
994 (" Cannot chown target directory \"%s\" \n %s "),
996 if (return_status
!= FILE_RETRY
)
1002 /* open the source dir for reading */
1003 if ((reading
= mc_opendir (s
)) == 0) {
1007 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
1009 * Now, we don't want '.' and '..' to be created / copied at any time
1011 if (!strcmp (next
->d_name
, "."))
1013 if (!strcmp (next
->d_name
, ".."))
1016 /* get the filename and add it to the src directory */
1017 path
= concat_dir_and_file (s
, next
->d_name
);
1019 (*ctx
->stat_func
) (path
, &buf
);
1020 if (S_ISDIR (buf
.st_mode
)) {
1021 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
1023 * From here, we just intend to recursively copy subdirs, not
1024 * the double functionality of copying different when the target
1025 * dir already exists. So, we give the recursive call the flag 0
1026 * meaning no toplevel.
1028 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0,
1029 delete, parent_dirs
,
1030 progress_count
, progress_bytes
);
1033 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
1034 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
1035 progress_count
, progress_bytes
,
1039 if (delete && return_status
== FILE_CONT
) {
1040 if (ctx
->erase_at_end
) {
1041 static struct link
*tail
;
1042 lp
= g_malloc (sizeof (struct link
) + strlen (path
));
1043 strcpy (lp
->name
, path
);
1044 lp
->st_mode
= buf
.st_mode
;
1050 erase_list
= tail
= lp
;
1052 if (S_ISDIR (buf
.st_mode
)) {
1053 return_status
= erase_dir_iff_empty (ctx
, path
);
1055 return_status
= erase_file (ctx
, path
, 0, 0, 0);
1061 mc_closedir (reading
);
1063 if (ctx
->preserve
) {
1064 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1065 utb
.actime
= cbuf
.st_atime
;
1066 utb
.modtime
= cbuf
.st_mtime
;
1067 mc_utime (dest_dir
, &utb
);
1072 g_free (parent_dirs
);
1073 return return_status
;
1078 /* {{{ Move routines */
1081 move_file_file (FileOpContext
*ctx
, char *s
, char *d
,
1082 off_t
*progress_count
, double *progress_bytes
)
1084 struct stat src_stats
, dst_stats
;
1085 int return_status
= FILE_CONT
;
1087 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
1088 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1093 while (mc_lstat (s
, &src_stats
) != 0) {
1094 /* Source doesn't exist */
1096 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1097 if (return_status
!= FILE_RETRY
)
1098 return return_status
;
1101 if (mc_lstat (d
, &dst_stats
) == 0) {
1102 if (src_stats
.st_dev
== dst_stats
.st_dev
1103 && src_stats
.st_ino
== dst_stats
.st_ino
) {
1104 int msize
= COLS
- 36;
1105 char st
[MC_MAXPATHLEN
];
1106 char dt
[MC_MAXPATHLEN
];
1112 strcpy (st
, name_trunc (s
, msize
));
1113 strcpy (dt
, name_trunc (d
, msize
));
1114 message (1, MSG_ERROR
,
1115 _(" `%s' and `%s' are the same file "), st
, dt
);
1120 if (S_ISDIR (dst_stats
.st_mode
)) {
1121 message (1, MSG_ERROR
,
1122 _(" Cannot overwrite directory `%s' "), d
);
1127 if (confirm_overwrite
) {
1128 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1129 if (return_status
!= FILE_CONT
)
1130 return return_status
;
1132 /* Ok to overwrite */
1135 if (!ctx
->do_append
) {
1136 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1137 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1138 goto retry_src_remove
;
1140 return return_status
;
1143 if (mc_rename (s
, d
) == 0) {
1148 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1149 one nfs to the same, but on the server it is on two different
1150 filesystems. Then nfs returns EIO instead of EXDEV.
1151 Hope it will not hurt if we always in case of error try to copy/delete. */
1153 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1155 if (errno
!= EXDEV
) {
1157 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1159 if (return_status
== FILE_RETRY
)
1161 return return_status
;
1165 /* Failed because filesystem boundary -> copy the file instead */
1167 copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1168 if (return_status
!= FILE_CONT
)
1169 return return_status
;
1171 if ((return_status
=
1172 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1173 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1174 return return_status
;
1179 if (mc_unlink (s
)) {
1181 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1182 if (return_status
== FILE_RETRY
)
1183 goto retry_src_remove
;
1184 return return_status
;
1187 if (return_status
== FILE_CONT
)
1188 return_status
= progress_update_one (ctx
,
1191 src_stats
.st_size
, 1);
1193 return return_status
;
1197 move_dir_dir (FileOpContext
*ctx
, char *s
, char *d
,
1198 off_t
*progress_count
, double *progress_bytes
)
1200 struct stat sbuf
, dbuf
, destbuf
;
1206 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1207 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1213 if (mc_stat (d
, &dbuf
))
1214 destdir
= g_strdup (d
); /* destination doesn't exist */
1215 else if (!ctx
->dive_into_subdirs
) {
1216 destdir
= g_strdup (d
);
1219 destdir
= concat_dir_and_file (d
, x_basename (s
));
1221 if (sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
) {
1222 int msize
= COLS
- 36;
1223 char st
[MC_MAXPATHLEN
];
1224 char dt
[MC_MAXPATHLEN
];
1230 strcpy (st
, name_trunc (s
, msize
));
1231 strcpy (dt
, name_trunc (d
, msize
));
1232 message (1, MSG_ERROR
,
1233 _(" `%s' and `%s' are the same directory "), st
, dt
);
1238 /* Check if the user inputted an existing dir */
1240 if (!mc_stat (destdir
, &destbuf
)) {
1242 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1243 progress_count
, progress_bytes
);
1245 if (return_status
!= FILE_CONT
)
1249 if (S_ISDIR (destbuf
.st_mode
))
1252 (" Cannot overwrite directory \"%s\" %s "),
1256 file_error (_(" Cannot overwrite file \"%s\" %s "),
1258 if (return_status
== FILE_RETRY
)
1259 goto retry_dst_stat
;
1262 return return_status
;
1266 if (mc_rename (s
, destdir
) == 0) {
1267 return_status
= FILE_CONT
;
1270 /* .ado: Drive, Do we need this anymore? */
1273 /* EXDEV: cross device; does not work everywhere */
1274 if (toupper (s
[0]) != toupper (destdir
[0]))
1279 if (errno
!= EXDEV
) {
1282 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1284 if (return_status
== FILE_RETRY
)
1291 /* Failed because of filesystem boundary -> copy dir instead */
1293 copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
,
1296 if (return_status
!= FILE_CONT
)
1299 if ((return_status
=
1300 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1301 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1305 if (ctx
->erase_at_end
) {
1306 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1307 if (S_ISDIR (erase_list
->st_mode
)) {
1309 erase_dir_iff_empty (ctx
, erase_list
->name
);
1312 erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1314 erase_list
= erase_list
->next
;
1318 erase_dir_iff_empty (ctx
, s
);
1322 while (erase_list
) {
1324 erase_list
= erase_list
->next
;
1327 return return_status
;
1332 /* {{{ Erase routines */
1333 /* Don't update progress status if progress_count==NULL */
1335 erase_file (FileOpContext
*ctx
, char *s
, off_t
*progress_count
,
1336 double *progress_bytes
, int is_toplevel_file
)
1341 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1345 if (progress_count
&& mc_lstat (s
, &buf
)) {
1346 /* ignore, most likely the mc_unlink fails, too */
1350 while (mc_unlink (s
)) {
1352 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1353 if (return_status
!= FILE_RETRY
)
1354 return return_status
;
1358 return progress_update_one (ctx
, progress_count
, progress_bytes
,
1359 buf
.st_size
, is_toplevel_file
);
1365 recursive_erase (FileOpContext
*ctx
, char *s
, off_t
*progress_count
,
1366 double *progress_bytes
)
1368 struct dirent
*next
;
1372 int return_status
= FILE_CONT
;
1374 if (!strcmp (s
, ".."))
1377 reading
= mc_opendir (s
);
1382 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1383 if (!strcmp (next
->d_name
, "."))
1385 if (!strcmp (next
->d_name
, ".."))
1387 path
= concat_dir_and_file (s
, next
->d_name
);
1388 if (mc_lstat (path
, &buf
)) {
1390 mc_closedir (reading
);
1393 if (S_ISDIR (buf
.st_mode
))
1396 (ctx
, path
, progress_count
, progress_bytes
)
1400 erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1403 mc_closedir (reading
);
1404 if (return_status
!= FILE_CONT
)
1405 return return_status
;
1406 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1410 while (my_rmdir (s
)) {
1412 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1413 if (return_status
!= FILE_RETRY
)
1414 return return_status
;
1420 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1421 in the directory path points to, 0 else. */
1423 check_dir_is_empty (char *path
)
1429 dir
= mc_opendir (path
);
1433 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1434 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1435 (d
->d_name
[1] == '.'
1436 && d
->d_name
[2] == '\0')))
1437 continue; /* "." or ".." */
1447 erase_dir (FileOpContext
*ctx
, char *s
, off_t
*progress_count
,
1448 double *progress_bytes
)
1452 if (strcmp (s
, "..") == 0)
1455 if (strcmp (s
, ".") == 0)
1458 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1462 /* The old way to detect a non empty directory was:
1463 error = my_rmdir (s);
1464 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1465 For the linux user space nfs server (nfs-server-2.2beta29-2)
1466 we would have to check also for EIO. I hope the new way is
1467 fool proof. (Norbert)
1469 error
= check_dir_is_empty (s
);
1470 if (error
== 0) { /* not empty */
1471 error
= query_recursive (ctx
, s
);
1472 if (error
== FILE_CONT
)
1473 return recursive_erase (ctx
, s
, progress_count
,
1479 while (my_rmdir (s
) == -1) {
1481 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1482 if (error
!= FILE_RETRY
)
1490 erase_dir_iff_empty (FileOpContext
*ctx
, char *s
)
1494 if (strcmp (s
, "..") == 0)
1497 if (strcmp (s
, ".") == 0)
1500 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1504 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1507 while (my_rmdir (s
)) {
1509 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1510 if (error
!= FILE_RETRY
)
1519 /* {{{ Panel operate routines */
1522 * Return currently selected entry name or the name of the first marked
1523 * entry if there is one.
1526 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1530 if (get_current_type () == view_tree
) {
1531 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1532 char *tree_name
= tree_selected_name (tree
);
1534 mc_stat (tree_name
, stat_buf
);
1538 if (panel
->marked
) {
1539 for (i
= 0; i
< panel
->count
; i
++)
1540 if (panel
->dir
.list
[i
].f
.marked
) {
1541 *stat_buf
= panel
->dir
.list
[i
].st
;
1542 return panel
->dir
.list
[i
].fname
;
1545 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1546 return panel
->dir
.list
[panel
->selected
].fname
;
1548 g_assert_not_reached ();
1555 * Computes the number of bytes used by the files in a directory
1558 compute_dir_size (char *dirname
, off_t
*ret_marked
, double *ret_total
)
1561 struct dirent
*dirent
;
1563 dir
= mc_opendir (dirname
);
1568 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1573 if (strcmp (dirent
->d_name
, ".") == 0)
1575 if (strcmp (dirent
->d_name
, "..") == 0)
1578 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1580 res
= mc_lstat (fullname
, &s
);
1587 if (S_ISDIR (s
.st_mode
)) {
1588 off_t subdir_count
= 0;
1589 double subdir_bytes
= 0;
1591 compute_dir_size (fullname
, &subdir_count
, &subdir_bytes
);
1593 *ret_marked
+= subdir_count
;
1594 *ret_total
+= subdir_bytes
;
1597 *ret_total
+= s
.st_size
;
1606 * panel_compute_totals:
1608 * compute the number of files and the number of bytes
1609 * used up by the whole selection, recursing directories
1610 * as required. In addition, it checks to see if it will
1611 * overwrite any files by doing the copy.
1614 panel_compute_totals (WPanel
*panel
, off_t
*ret_marked
, double *ret_total
)
1621 for (i
= 0; i
< panel
->count
; i
++) {
1624 if (!panel
->dir
.list
[i
].f
.marked
)
1627 s
= &panel
->dir
.list
[i
].st
;
1629 if (S_ISDIR (s
->st_mode
)) {
1631 off_t subdir_count
= 0;
1632 double subdir_bytes
= 0;
1635 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1636 compute_dir_size (dir_name
, &subdir_count
, &subdir_bytes
);
1638 *ret_marked
+= subdir_count
;
1639 *ret_total
+= subdir_bytes
;
1643 *ret_total
+= s
->st_size
;
1649 * This array introduced to avoid translation problems. The former (op_names)
1650 * is assumed to be nouns, suitable in dialog box titles; this one should
1651 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1652 * Notice first symbol - it is to fool gettext and force these strings to
1653 * be different for it. First symbol is skipped while building a prompt.
1654 * (I don't use spaces around the words, because someday they could be
1655 * dropped, when widgets get smarter)
1657 static char *op_names1
[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1660 int fmd_xlen
= FMD_XLEN
;
1663 * These are formats for building a prompt. Parts encoded as follows:
1664 * %o - operation from op_names1
1665 * %f - file/files or files/directories, as appropriate
1666 * %m - "with source mask" or question mark for delete
1667 * %s - source name (truncated)
1668 * %d - number of marked files
1669 * %e - "to:" or question mark for delete
1671 * xgettext:no-c-format */
1672 static char *one_format
= N_("%o %f \"%s\"%m");
1673 /* xgettext:no-c-format */
1674 static char *many_format
= N_("%o %d %f%m");
1675 static char *prompt_parts
[] = {
1676 N_("file"), N_("files"), N_("directory"), N_("directories"),
1677 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1681 * Generate user prompt for panel operation.
1682 * single_source is the name if the source entry or NULL for multiple
1684 * src_stat is only used when single_source is not NULL.
1687 panel_operate_generate_prompt (const WPanel
*panel
, const int operation
,
1688 const char *single_source
,
1689 const struct stat
*src_stat
)
1691 register char *sp
, *cp
;
1693 char format_string
[BUF_MEDIUM
];
1694 char *dp
= format_string
;
1697 static int i18n_flag
= 0;
1699 fmd_init_i18n (FALSE
); /* to get proper fmd_xlen */
1701 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1702 op_names1
[i
] = _(op_names1
[i
]);
1704 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1705 prompt_parts
[i
] = _(prompt_parts
[i
]);
1707 one_format
= _(one_format
);
1708 many_format
= _(many_format
);
1711 #endif /* ENABLE_NLS */
1713 sp
= single_source
? one_format
: many_format
;
1721 cp
= op_names1
[operation
] + 1;
1724 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1727 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[6];
1730 if (single_source
) {
1731 cp
= S_ISDIR (src_stat
->
1732 st_mode
) ? prompt_parts
[2] :
1735 cp
= (panel
->marked
== panel
->dirs_marked
)
1737 : (panel
->dirs_marked
? prompt_parts
[4]
1756 if (single_source
) {
1757 i
= fmd_xlen
- strlen (format_string
) - 4;
1758 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1759 name_trunc (single_source
, i
));
1761 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1763 i
= strlen (cmd_buf
) + 6 - fmd_xlen
;
1766 fmd_init_i18n (TRUE
); /* to recalculate positions of child widgets */
1774 * Performs one of the operations on the selection on the source_panel
1775 * (copy, delete, move).
1777 * Returns 1 if did change the directory
1778 * structure, Returns 0 if user aborted
1780 * force_single forces operation on the current entry and affects
1781 * default destination. Current filename is used as default.
1784 panel_operate (void *source_panel
, FileOperation operation
,
1787 WPanel
*panel
= source_panel
;
1788 #ifdef WITH_FULL_PATHS
1789 char *source_with_path
= NULL
;
1791 # define source_with_path source
1792 #endif /* !WITH_FULL_PATHS */
1793 char *source
= NULL
;
1796 char *save_cwd
= NULL
, *save_dest
= NULL
;
1797 int single_entry
= (get_current_type () == view_tree
)
1798 || (panel
->marked
<= 1) || force_single
;
1799 struct stat src_stat
, dst_stat
;
1807 int do_bg
= 0; /* do background operation? */
1809 free_linklist (&linklist
);
1810 free_linklist (&dest_dirs
);
1814 source
= selection (panel
)->fname
;
1815 src_stat
= selection (panel
)->st
;
1817 source
= panel_get_file (panel
, &src_stat
);
1820 if (!strcmp (source
, "..")) {
1821 message (1, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1826 /* Generate confirmation prompt */
1827 panel_operate_generate_prompt (panel
, operation
, source
, &src_stat
);
1829 ctx
= file_op_context_new (operation
);
1831 /* Show confirmation dialog */
1832 if (operation
== OP_DELETE
&& confirm_delete
) {
1836 i
= query_dialog (_(op_names
[operation
]), cmd_buf
, D_ERROR
, 2,
1837 _("&Yes"), _("&No"));
1840 file_op_context_destroy (ctx
);
1843 } else if (operation
!= OP_DELETE
) {
1846 /* Forced single operations default to the original name */
1849 else if (get_other_type () == view_listing
)
1850 dest_dir
= other_panel
->cwd
;
1852 dest_dir
= panel
->cwd
;
1855 file_mask_dialog (ctx
, operation
, cmd_buf
, dest_dir
,
1856 single_entry
, &do_bg
);
1858 file_op_context_destroy (ctx
);
1862 file_op_context_destroy (ctx
);
1867 #ifdef WITH_BACKGROUND
1868 /* Did the user select to do a background operation? */
1872 v
= do_background (ctx
,
1873 g_strconcat (op_names
[operation
], ": ",
1876 message (1, MSG_ERROR
,
1877 _(" Sorry, I could not put the job in background "));
1880 /* If we are the parent */
1882 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
1883 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
1884 /* file_op_context_destroy (ctx); */
1888 #endif /* WITH_BACKGROUND */
1890 /* Initialize things */
1891 /* We do not want to trash cache every time file is
1892 created/touched. However, this will make our cache contain
1895 if (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1896 save_dest
= g_strdup (dest
);
1899 if (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1900 save_cwd
= g_strdup (panel
->cwd
);
1903 /* Now, let's do the job */
1908 file_op_context_create_ui (ctx
, 1);
1910 /* This code is only called by the tree and panel code */
1912 /* We now have ETA in all cases */
1914 /* One file: FIXME mc_chdir will take user out of any vfs */
1915 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1916 mc_chdir (PATH_SEP_STR
);
1918 /* The source and src_stat variables have been initialized before */
1919 #ifdef WITH_FULL_PATHS
1920 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1921 #endif /* WITH_FULL_PATHS */
1923 if (operation
== OP_DELETE
) {
1924 if (S_ISDIR (src_stat
.st_mode
))
1925 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1928 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1930 temp
= transform_source (ctx
, source_with_path
);
1933 value
= transform_error
;
1935 temp
= concat_dir_and_file (dest
, temp
);
1940 switch (operation
) {
1943 * we use file_mask_op_follow_links only with OP_COPY,
1945 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1947 if (S_ISDIR (src_stat
.st_mode
))
1949 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
1950 0, 0, 0, &count
, &bytes
);
1953 copy_file_file (ctx
, source_with_path
, dest
, 1,
1958 if (S_ISDIR (src_stat
.st_mode
))
1960 move_dir_dir (ctx
, source_with_path
, dest
,
1964 move_file_file (ctx
, source_with_path
, dest
,
1969 /* Unknown file operation */
1973 } /* Copy or move operation */
1975 if ((value
== FILE_CONT
) && !force_single
)
1976 unmark_files (panel
);
1979 /* Check destination for copy or move operation */
1980 if (operation
!= OP_DELETE
) {
1981 retry_many_dst_stat
:
1982 dst_result
= mc_stat (dest
, &dst_stat
);
1983 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
1985 (_(" Destination \"%s\" must be a directory \n %s "),
1986 dest
) == FILE_RETRY
)
1987 goto retry_many_dst_stat
;
1992 /* Initialize variables for progress bars */
1993 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
1994 panel_compute_totals (panel
, &ctx
->progress_count
,
1995 &ctx
->progress_bytes
);
1996 ctx
->progress_totals_computed
= 1;
1998 ctx
->progress_totals_computed
= 0;
1999 ctx
->progress_count
= panel
->marked
;
2000 ctx
->progress_bytes
= panel
->total
;
2003 /* Loop for every file, perform the actual copy operation */
2004 for (i
= 0; i
< panel
->count
; i
++) {
2005 if (!panel
->dir
.list
[i
].f
.marked
)
2006 continue; /* Skip the unmarked ones */
2008 source
= panel
->dir
.list
[i
].fname
;
2009 src_stat
= panel
->dir
.list
[i
].st
;
2011 #ifdef WITH_FULL_PATHS
2012 if (source_with_path
)
2013 g_free (source_with_path
);
2014 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2015 #endif /* WITH_FULL_PATHS */
2017 if (operation
== OP_DELETE
) {
2018 if (S_ISDIR (src_stat
.st_mode
))
2020 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
2023 erase_file (ctx
, source_with_path
, &count
, &bytes
,
2029 temp
= transform_source (ctx
, source_with_path
);
2031 value
= transform_error
;
2033 temp
= concat_dir_and_file (dest
, temp
);
2035 switch (operation
) {
2038 * we use file_mask_op_follow_links only with OP_COPY
2040 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2041 if (S_ISDIR (src_stat
.st_mode
))
2043 copy_dir_dir (ctx
, source_with_path
, temp
,
2044 1, 0, 0, 0, &count
, &bytes
);
2047 copy_file_file (ctx
, source_with_path
,
2048 temp
, 1, &count
, &bytes
,
2050 free_linklist (&dest_dirs
);
2054 if (S_ISDIR (src_stat
.st_mode
))
2056 move_dir_dir (ctx
, source_with_path
, temp
,
2060 move_file_file (ctx
, source_with_path
,
2061 temp
, &count
, &bytes
);
2065 /* Unknown file operation */
2069 } /* Copy or move operation */
2071 if (value
== FILE_ABORT
)
2074 if (value
== FILE_CONT
)
2075 do_file_mark (panel
, i
, 0);
2077 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2082 && file_progress_show_bytes (ctx
, bytes
,
2083 ctx
->progress_bytes
) ==
2087 if (operation
!= OP_DELETE
&& verbose
2088 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2092 } /* Loop for every file */
2093 } /* Many entries */
2098 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2102 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2106 free_linklist (&linklist
);
2107 free_linklist (&dest_dirs
);
2108 #ifdef WITH_FULL_PATHS
2109 if (source_with_path
)
2110 g_free (source_with_path
);
2111 #endif /* WITH_FULL_PATHS */
2119 if (ctx
->dest_mask
) {
2120 g_free (ctx
->dest_mask
);
2121 ctx
->dest_mask
= NULL
;
2123 #ifdef WITH_BACKGROUND
2124 /* Let our parent know we are saying bye bye */
2125 if (we_are_background
) {
2129 #endif /* WITH_BACKGROUND */
2131 file_op_context_destroy (ctx
);
2137 /* {{{ Query/status report routines */
2140 real_do_file_error (enum OperationMode mode
, char *error
)
2145 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2147 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2165 /* Report error with one file */
2167 file_error (char *format
, char *file
)
2169 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
,
2170 name_trunc (file
, 30), unix_error_string (errno
));
2172 return do_file_error (cmd_buf
);
2175 /* Report error with two files */
2177 files_error (const char *format
, const char *file1
, const char *file2
)
2182 strcpy (nfile1
, name_trunc (file1
, 15));
2183 strcpy (nfile2
, name_trunc (file2
, 15));
2185 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
, nfile1
, nfile2
,
2186 unix_error_string (errno
));
2188 return do_file_error (cmd_buf
);
2192 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, char *s
)
2196 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2200 _("\n Directory not empty. \n"
2201 " Delete it recursively? ")
2202 : _("\n Background process: Directory not empty \n"
2203 " Delete it recursively? ");
2204 text
= g_strconcat (_(" Delete: "), name_trunc (s
, 30), " ", NULL
);
2208 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2209 _("&Yes"), _("&No"),
2210 _("A&ll"), _("Non&e"),
2213 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2218 switch (ctx
->recursive_result
) {
2220 case RECURSIVE_ALWAYS
:
2224 case RECURSIVE_NEVER
:
2227 case RECURSIVE_ABORT
:
2234 #ifdef WITH_BACKGROUND
2236 do_file_error (char *str
)
2238 if (we_are_background
)
2239 return parent_call (real_do_file_error
, NULL
, 1, strlen (str
),
2242 return real_do_file_error (Foreground
, str
);
2246 query_recursive (FileOpContext
*ctx
, char *s
)
2248 if (we_are_background
)
2249 return parent_call (real_query_recursive
, ctx
, 1, strlen (s
), s
);
2251 return real_query_recursive (ctx
, Foreground
, s
);
2255 query_replace (FileOpContext
*ctx
, char *destname
, struct stat
*_s_stat
,
2256 struct stat
*_d_stat
)
2258 if (we_are_background
)
2259 return parent_call ((void *) file_progress_real_query_replace
,
2262 strlen (destname
), destname
,
2263 sizeof (struct stat
), _s_stat
,
2264 sizeof (struct stat
), _d_stat
);
2266 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2272 do_file_error (char *str
)
2274 return real_do_file_error (Foreground
, str
);
2278 query_recursive (FileOpContext
*ctx
, char *s
)
2280 return real_query_recursive (ctx
, Foreground
, s
);
2284 query_replace (FileOpContext
*ctx
, char *destname
, struct stat
*_s_stat
,
2285 struct stat
*_d_stat
)
2287 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2291 #endif /* !WITH_BACKGROUND */
2294 Cause emacs to enter folding mode for this file: