3 /* File managing. Important notes on this file:
5 About the use of dialogs in this file:
6 If you want to add a new dialog box (or call a routine that pops
7 up a dialog box), you have to provide a wrapper for background
8 operations (ie, background operations have to up-call to the parent
11 For example, instead of using the message() routine, in this
12 file, you should use one of the stubs that call message with the
13 proper number of arguments (ie, message_1s, message_2s and so on).
15 Actually, that is a rule that should be followed by any routines
16 that may be called from this module.
21 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
23 Written by: 1994, 1995 Janne Kukonlehto
24 1994, 1995 Fred Leeflang
25 1994, 1995, 1996 Miguel de Icaza
26 1995, 1996 Jakub Jelinek
30 The copy code was based in GNU's cp, and was written by:
31 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
33 The move code was based in GNU's mv, and was written by:
34 Mike Parker and David MacKenzie.
36 Janne Kukonlehto added much error recovery to them for being used
37 in an interactive program.
39 This program is free software; you can redistribute it and/or modify
40 it under the terms of the GNU General Public License as published by
41 the Free Software Foundation; either version 2 of the License, or
42 (at your option) any later version.
44 This program is distributed in the hope that it will be useful,
45 but WITHOUT ANY WARRANTY; without even the implied warranty of
46 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 GNU General Public License for more details.
49 You should have received a copy of the GNU General Public License
50 along with this program; if not, write to the Free Software
51 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
55 /* {{{ Include files */
58 /* Hack: the vfs code should not rely on this */
59 #define WITH_FULL_PATHS 1
61 #include <sys/types.h>
68 #endif /* HAVE_UNISTD_H */
76 /* Needed by query_replace */
82 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
87 /* Needed for current_panel, other_panel and WTree */
94 #include "../vfs/vfs.h"
101 * Whether the Midnight Commander tries to provide more
102 * information about copy/move sizes and bytes transfered
103 * at the expense of some speed
105 int file_op_compute_totals
= 1;
107 /* If on, default for "No" in delete operations */
110 /* This is a hard link cache */
121 /* the hard link cache */
122 struct link
*linklist
= NULL
;
124 /* the files-to-be-erased list */
125 struct link
*erase_list
;
128 * In copy_dir_dir we use two additional single linked lists: The first -
129 * variable name `parent_dirs' - holds information about already copied
130 * directories and is used to detect cyclic symbolic links.
131 * The second (`dest_dirs' below) holds information about just created
132 * target directories and is used to detect when an directory is copied
133 * into itself (we don't want to copy infinitly).
134 * Both lists don't use the linkcount and name structure members of struct
137 struct link
*dest_dirs
= 0;
139 char *op_names
[3] = {
147 static int query_replace (FileOpContext
* ctx
, char *destname
,
148 struct stat
*_s_stat
, struct stat
*_d_stat
);
149 static int query_recursive (FileOpContext
* ctx
, char *s
);
150 static int do_file_error (char *str
);
153 enum CaseConvs
{ NO_CONV
= 0, UP_CHAR
= 1, LOW_CHAR
= 2, UP_SECT
=
157 convert_case (int c
, enum CaseConvs
*conversion
)
159 if (*conversion
& UP_CHAR
) {
160 *conversion
&= ~UP_CHAR
;
162 } else if (*conversion
& LOW_CHAR
) {
163 *conversion
&= ~LOW_CHAR
;
165 } else if (*conversion
& UP_SECT
) {
167 } else if (*conversion
& LOW_SECT
) {
173 static int transform_error
= 0;
175 static unsigned char *
176 do_transform_source (FileOpContext
*ctx
, unsigned char *source
)
179 unsigned char *fnsource
= x_basename (source
);
181 enum CaseConvs case_conv
= NO_CONV
;
182 static unsigned char fntarget
[MC_MAXPATHLEN
];
184 len
= strlen (fnsource
);
185 j
= re_match (&ctx
->rx
, fnsource
, len
, 0, &ctx
->regs
);
187 transform_error
= FILE_SKIP
;
190 for (next_reg
= 1, j
= 0, k
= 0; j
< strlen (ctx
->dest_mask
); j
++) {
191 switch (ctx
->dest_mask
[j
]) {
194 if (!isdigit ((unsigned char) ctx
->dest_mask
[j
])) {
195 /* Backslash followed by non-digit */
196 switch (ctx
->dest_mask
[j
]) {
198 case_conv
|= UP_SECT
;
199 case_conv
&= ~LOW_SECT
;
202 case_conv
|= UP_CHAR
;
205 case_conv
|= LOW_SECT
;
206 case_conv
&= ~UP_SECT
;
209 case_conv
|= LOW_CHAR
;
215 /* Backslash as quote mark */
217 convert_case (ctx
->dest_mask
[j
], &case_conv
);
221 /* Backslash followed by digit */
222 next_reg
= ctx
->dest_mask
[j
] - '0';
227 if (next_reg
< 0 || next_reg
>= RE_NREGS
228 || ctx
->regs
.start
[next_reg
] < 0) {
229 message_1s (1, MSG_ERROR
, _(" Invalid target mask "));
230 transform_error
= FILE_ABORT
;
233 for (l
= ctx
->regs
.start
[next_reg
];
234 l
< ctx
->regs
.end
[next_reg
]; l
++)
235 fntarget
[k
++] = convert_case (fnsource
[l
], &case_conv
);
240 fntarget
[k
++] = convert_case (ctx
->dest_mask
[j
], &case_conv
);
248 static unsigned char *
249 transform_source (FileOpContext
*ctx
, unsigned char *source
)
251 unsigned char *s
= g_strdup (source
);
254 /* We remove \n from the filename since regex routines would use \n as an anchor */
255 /* this is just to be allowed to maniupulate file names with \n on it */
256 for (q
= s
; *q
; q
++) {
260 q
= do_transform_source (ctx
, s
);
266 free_linklist (struct link
**linklist
)
268 struct link
*lp
, *lp2
;
270 for (lp
= *linklist
; lp
!= NULL
; lp
= lp2
) {
278 is_in_linklist (struct link
*lp
, char *path
, struct stat
*sb
)
280 ino_t ino
= sb
->st_ino
;
281 dev_t dev
= sb
->st_dev
;
283 vfs
*vfs
= vfs_type (path
);
290 if (lp
->ino
== ino
&& lp
->dev
== dev
)
298 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
299 * and a hardlink was succesfully made
302 check_hardlinks (char *src_name
, char *dst_name
, struct stat
*pstat
)
305 vfs
*my_vfs
= vfs_type (src_name
);
306 ino_t ino
= pstat
->st_ino
;
307 dev_t dev
= pstat
->st_dev
;
308 struct stat link_stat
;
311 #if 1 /* What will happen if we kill this line? mc_link() will fail on this and it is right behaviour... */
312 if (vfs_file_is_ftp (src_name
) || vfs_file_is_smb (src_name
))
315 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
316 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
) {
317 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
318 && link_stat
.st_dev
== dev
319 && vfs_type (lp
->name
) == my_vfs
) {
320 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
322 if (vfs_type (dst_name
) == vfs_type (p
)) {
323 if (!mc_stat (p
, &link_stat
)) {
324 if (!mc_link (p
, dst_name
))
329 message_1s (1, MSG_ERROR
, _(" Cannot make the hardlink "));
332 lp
= (struct link
*) g_malloc (sizeof (struct link
) + strlen (src_name
)
333 + strlen (dst_name
) + 1);
338 strcpy (lp
->name
, src_name
);
339 p
= strchr (lp
->name
, 0) + 1;
340 strcpy (p
, dst_name
);
348 * Duplicate the contents of the symbolic link src_path in dst_path.
349 * Try to make a stable symlink if the option "stable symlink" was
350 * set in the file mask dialog.
351 * If dst_path is an existing symlink it will be deleted silently
352 * (upper levels take already care of existing files at dst_path).
355 make_symlink (FileOpContext
*ctx
, char *src_path
, char *dst_path
)
357 char link_target
[MC_MAXPATHLEN
];
363 if (mc_lstat (dst_path
, &sb
) == 0 && S_ISLNK (sb
.st_mode
))
369 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
);
372 file_error (_(" Cannot read source link \"%s\" \n %s "),
374 if (return_status
== FILE_RETRY
)
375 goto retry_src_readlink
;
376 return return_status
;
378 link_target
[len
] = 0;
380 if (ctx
->stable_symlinks
)
381 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
382 message_1s (1, MSG_ERROR
,
383 _(" Cannot make stable symlinks across "
384 "non-local filesystems: \n\n"
385 " Option Stable Symlinks will be disabled "));
386 ctx
->stable_symlinks
= 0;
389 if (ctx
->stable_symlinks
&& *link_target
!= PATH_SEP
) {
392 p
= g_strdup (src_path
);
393 r
= strrchr (p
, PATH_SEP
);
397 if (*dst_path
== PATH_SEP
)
398 q
= g_strdup (dst_path
);
400 q
= g_strconcat (p
, dst_path
, NULL
);
401 r
= strrchr (q
, PATH_SEP
);
404 s
= g_strconcat (p
, link_target
, NULL
);
405 strcpy (link_target
, s
);
407 s
= diff_two_paths (q
, link_target
);
409 strcpy (link_target
, s
);
418 if (mc_symlink (link_target
, dst_path
) == 0)
422 * if dst_exists, it is obvious that this had failed.
423 * We can delete the old symlink and try again...
425 if (dst_is_symlink
) {
426 if (!mc_unlink (dst_path
))
427 if (mc_symlink (link_target
, dst_path
) == 0)
432 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
434 if (return_status
== FILE_RETRY
)
435 goto retry_dst_symlink
;
436 return return_status
;
440 progress_update_one (FileOpContext
*ctx
,
441 off_t
*progress_count
,
442 double *progress_bytes
, int add
, int is_toplevel_file
)
446 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
448 (*progress_bytes
) += add
;
451 /* Apply some heuristic here to not call the update stuff very often */
453 file_progress_show_count (ctx
, *progress_count
,
454 ctx
->progress_count
);
455 if (ret
!= FILE_CONT
)
458 file_progress_show_bytes (ctx
, *progress_bytes
,
459 ctx
->progress_bytes
);
464 /* Status of the destination file */
466 DEST_NONE
, /* Not created */
467 DEST_SHORT
, /* Created, not fully copied */
468 DEST_FULL
/* Created, fully copied */
472 copy_file_file (FileOpContext
*ctx
, char *src_path
, char *dst_path
,
473 int ask_overwrite
, off_t
*progress_count
,
474 double *progress_bytes
, int is_toplevel_file
)
477 uid_t src_uid
= (uid_t
) - 1;
478 gid_t src_gid
= (gid_t
) - 1;
479 #endif /* !NATIVE_WIN32 */
481 int buf_size
= BUF_8K
;
482 int src_desc
, dest_desc
= -1;
483 int n_read
, n_written
;
484 int src_mode
= 0; /* The mode of the source file */
487 int dst_exists
= 0, appending
= 0;
488 off_t n_read_total
= 0, file_size
= -1;
489 int return_status
, temp_status
;
490 struct timeval tv_transfer_start
;
491 int dst_status
= DEST_NONE
; /* 1 if the file is not fully copied */
493 /* FIXME: We should not be using global variables! */
495 return_status
= FILE_RETRY
;
497 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
498 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
504 if (mc_stat (dst_path
, &sb2
) == 0) {
505 if (S_ISDIR (sb2
.st_mode
)) {
507 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
509 if (return_status
== FILE_RETRY
)
511 return return_status
;
516 while ((*ctx
->stat_func
) (src_path
, &sb
)) {
518 file_error (_(" Cannot stat source file \"%s\" \n %s "),
520 if (return_status
!= FILE_RETRY
)
521 return return_status
;
525 /* .ado: For Win32: no st_ino exists, it is better to just try to
526 * overwrite the target file
529 /* Destination already exists */
530 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
) {
531 message_3s (1, MSG_ERROR
,
532 _(" `%s' and `%s' are the same file "), src_path
,
537 #endif /* !NATIVE_WIN32 */
539 /* Should we replace destination? */
542 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
543 if (return_status
!= FILE_CONT
)
544 return return_status
;
548 if (!ctx
->do_append
) {
549 /* .ado: OS2 and NT don't have hardlinks */
551 /* Check the hardlinks */
552 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
553 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
554 /* We have made a hardlink - no more processing is necessary */
555 return return_status
;
558 if (S_ISLNK (sb
.st_mode
)) {
561 retval
= make_symlink (ctx
, src_path
, dst_path
);
564 #endif /* !NATIVE_WIN32 */
566 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
)
567 || S_ISFIFO (sb
.st_mode
)
568 || S_ISSOCK (sb
.st_mode
)) {
570 (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
574 (" Cannot create special file \"%s\" \n %s "),
576 if (return_status
== FILE_RETRY
)
578 return return_status
;
583 while (ctx
->preserve_uidgid
584 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
587 (" Cannot chown target file \"%s\" \n %s "),
589 if (temp_status
== FILE_RETRY
)
593 #endif /* !NATIVE_WIN32 */
594 while (ctx
->preserve
&&
595 (mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
) <
599 (" Cannot chmod target file \"%s\" \n %s "),
601 if (temp_status
== FILE_RETRY
)
609 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
611 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
613 file_error (_(" Cannot open source file \"%s\" \n %s "),
615 if (return_status
== FILE_RETRY
)
618 return return_status
;
622 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
) {
623 message_1s (1, _("Warning"),
624 _(" Reget failed, about to overwrite file "));
625 ctx
->do_reget
= ctx
->do_append
= 0;
629 while (mc_fstat (src_desc
, &sb
)) {
631 file_error (_(" Cannot fstat source file \"%s\" \n %s "),
633 if (return_status
== FILE_RETRY
)
638 src_mode
= sb
.st_mode
;
642 #endif /* !NATIVE_WIN32 */
643 utb
.actime
= sb
.st_atime
;
644 utb
.modtime
= sb
.st_mtime
;
645 file_size
= sb
.st_size
;
647 /* Create the new regular file with small permissions initially,
648 do not create a security hole. FIXME: You have security hole
649 here, btw. Imagine copying to /tmp and symlink attack :-( */
651 while ((dest_desc
= mc_open (dst_path
, O_WRONLY
|
653 do_append
? O_APPEND
: (O_CREAT
|
657 file_error (_(" Cannot create target file \"%s\" \n %s "),
659 if (return_status
== FILE_RETRY
)
664 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
666 appending
= ctx
->do_append
;
669 /* Find out the optimal buffer size. */
670 while (mc_fstat (dest_desc
, &sb
)) {
672 file_error (_(" Cannot fstat target file \"%s\" \n %s "),
674 if (return_status
== FILE_RETRY
)
678 buf
= (char *) g_malloc (buf_size
);
683 return_status
= file_progress_show (ctx
, 0, file_size
);
687 if (return_status
!= FILE_CONT
)
691 struct timeval tv_current
, tv_last_update
, tv_last_input
;
692 int secs
, update_secs
;
696 tv_last_update
= tv_transfer_start
;
700 if (mc_ctl (src_desc
, MCCTL_IS_NOTREADY
, 0))
703 while ((n_read
= mc_read (src_desc
, buf
, buf_size
)) < 0) {
706 (" Cannot read source file \"%s\" \n %s "),
708 if (return_status
== FILE_RETRY
)
715 gettimeofday (&tv_current
, NULL
);
718 n_read_total
+= n_read
;
720 /* Windows NT ftp servers report that files have no
721 * permissions: -------, so if we happen to have actually
722 * read something, we should fix the permissions.
724 if (!(src_mode
& ((S_IRUSR
| S_IWUSR
| S_IXUSR
) /* user */
725 |(S_IXOTH
| S_IWOTH
| S_IROTH
) /* other */
726 |(S_IXGRP
| S_IWGRP
| S_IRGRP
)))) /* group */
727 src_mode
= S_IRUSR
| S_IWUSR
| S_IROTH
| S_IRGRP
;
728 gettimeofday (&tv_last_input
, NULL
);
732 mc_write (dest_desc
, buf
, n_read
)) < n_read
) {
739 (" Cannot write target file \"%s\" \n %s "),
741 if (return_status
== FILE_RETRY
)
747 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
748 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
751 tv_last_update
= tv_current
;
754 /* 2. Check for a stalled condition */
755 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
757 if (update_secs
> 4) {
758 stalled_msg
= _("(stalled)");
763 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
767 ((dt
/ (double) n_read_total
) * file_size
) - dt
;
768 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
773 /* 4. Compute BPS rate */
776 (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
777 if (ctx
->bps_time
< 1)
779 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
782 file_progress_set_stalled_label (ctx
, stalled_msg
);
784 file_progress_show_bytes (ctx
,
785 *progress_bytes
+ n_read_total
,
786 ctx
->progress_bytes
);
787 if (return_status
== FILE_CONT
) {
789 file_progress_show (ctx
, n_read_total
, file_size
);
792 if (return_status
!= FILE_CONT
)
797 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
803 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
805 file_error (_(" Cannot close source file \"%s\" \n %s "),
807 if (temp_status
== FILE_RETRY
)
809 if (temp_status
== FILE_ABORT
)
810 return_status
= temp_status
;
814 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
816 file_error (_(" Cannot close target file \"%s\" \n %s "),
818 if (temp_status
== FILE_RETRY
)
820 return_status
= temp_status
;
824 if (dst_status
== DEST_SHORT
) {
825 /* Remove short file */
828 query_dialog (_("Copy"),
829 _("Incomplete file was retrieved. Keep it?"),
830 D_ERROR
, 2, _("&Delete"), _("&Keep"));
832 mc_unlink (dst_path
);
833 } else if (dst_status
== DEST_FULL
) {
834 /* Copy has succeeded */
836 if (!appending
&& ctx
->preserve_uidgid
) {
837 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
838 temp_status
= file_error
839 (_(" Cannot chown target file \"%s\" \n %s "),
841 if (temp_status
== FILE_RETRY
)
843 return_status
= temp_status
;
847 #endif /* !NATIVE_WIN32 */
850 * .ado: according to the XPG4 standard, the file must be closed before
851 * chmod can be invoked
853 if (!appending
&& ctx
->preserve
) {
854 while (mc_chmod (dst_path
, src_mode
& ctx
->umask_kill
)) {
857 (" Cannot chmod target file \"%s\" \n %s "),
859 if (temp_status
!= FILE_RETRY
) {
860 return_status
= temp_status
;
864 mc_utime (dst_path
, &utb
);
868 if (return_status
== FILE_CONT
)
870 progress_update_one (ctx
, progress_count
, progress_bytes
,
871 file_size
, is_toplevel_file
);
873 return return_status
;
877 * I think these copy_*_* functions should have a return type.
878 * anyway, this function *must* have two directories as arguments.
880 /* FIXME: This function needs to check the return values of the
883 copy_dir_dir (FileOpContext
*ctx
, char *s
, char *d
, int toplevel
,
884 int move_over
, int delete,
885 struct link
*parent_dirs
,
886 off_t
*progress_count
, double *progress_bytes
)
889 struct stat buf
, cbuf
;
891 char *path
, *mdpath
, *dest_file
, *dest_dir
;
892 int return_status
= FILE_CONT
;
896 /* First get the mode of the source dir */
898 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
900 file_error (_(" Cannot stat source directory \"%s\" \n %s "),
902 if (return_status
== FILE_RETRY
)
904 return return_status
;
907 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
908 /* Don't copy a directory we created before (we don't want to copy
909 infinitely if a directory is copied into itself) */
910 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
914 /* Hmm, hardlink to directory??? - Norbert */
915 /* FIXME: In this step we should do something
916 in case the destination already exist */
917 /* Check the hardlinks */
918 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
919 && check_hardlinks (s
, d
, &cbuf
) == 1) {
920 /* We have made a hardlink - no more processing is necessary */
921 return return_status
;
924 if (!S_ISDIR (cbuf
.st_mode
)) {
927 (" Source directory \"%s\" is not a directory \n %s "),
929 if (return_status
== FILE_RETRY
)
931 return return_status
;
934 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
935 /* we found a cyclic symbolic link */
936 message_2s (1, MSG_ERROR
,
937 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
941 lp
= g_new (struct link
, 1);
942 lp
->vfs
= vfs_type (s
);
943 lp
->ino
= cbuf
.st_ino
;
944 lp
->dev
= cbuf
.st_dev
;
945 lp
->next
= parent_dirs
;
949 /* Now, check if the dest dir exists, if not, create it. */
950 if (mc_stat (d
, &buf
)) {
951 /* Here the dir doesn't exist : make it ! */
954 if (mc_rename (s
, d
) == 0) {
955 g_free (parent_dirs
);
959 dest_dir
= g_strdup (d
);
962 * If the destination directory exists, we want to copy the whole
963 * directory, but we only want this to happen once.
965 * Escape sequences added to the * to compiler warnings.
966 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
967 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
969 if (!S_ISDIR (buf
.st_mode
)) {
972 (" Destination \"%s\" must be a directory \n %s "),
974 if (return_status
== FILE_RETRY
)
976 g_free (parent_dirs
);
977 return return_status
;
980 /* Again, I'm getting curious. Is not d already what we wanted, incl.
981 * masked source basename? Is not this just a relict of the past versions?
982 * I'm afraid this will lead into a two level deep dive :(
984 * I think this is indeed the problem. I cannot remember any case where
985 * we actually would like that behaviour -miguel
987 * It's a documented feature (option `Dive into subdir if exists' in the
988 * copy/move dialog). -Norbert
990 if (toplevel
&& ctx
->dive_into_subdirs
) {
991 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
995 dest_dir
= g_strdup (d
);
1000 if (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
1002 file_error (_(" Cannot create target directory \"%s\" \n %s "),
1004 if (return_status
== FILE_RETRY
)
1005 goto retry_dst_mkdir
;
1009 lp
= g_new (struct link
, 1);
1010 mc_stat (dest_dir
, &buf
);
1011 lp
->vfs
= vfs_type (dest_dir
);
1012 lp
->ino
= buf
.st_ino
;
1013 lp
->dev
= buf
.st_dev
;
1014 lp
->next
= dest_dirs
;
1017 #ifndef NATIVE_WIN32
1018 if (ctx
->preserve_uidgid
) {
1019 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
1022 (" Cannot chown target directory \"%s\" \n %s "),
1024 if (return_status
!= FILE_RETRY
)
1028 #endif /* !NATIVE_WIN32 */
1031 /* open the source dir for reading */
1032 if ((reading
= mc_opendir (s
)) == 0) {
1036 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
1038 * Now, we don't want '.' and '..' to be created / copied at any time
1040 if (!strcmp (next
->d_name
, "."))
1042 if (!strcmp (next
->d_name
, ".."))
1045 /* get the filename and add it to the src directory */
1046 path
= concat_dir_and_file (s
, next
->d_name
);
1048 (*ctx
->stat_func
) (path
, &buf
);
1049 if (S_ISDIR (buf
.st_mode
)) {
1050 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
1052 * From here, we just intend to recursively copy subdirs, not
1053 * the double functionality of copying different when the target
1054 * dir already exists. So, we give the recursive call the flag 0
1055 * meaning no toplevel.
1057 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0,
1058 delete, parent_dirs
,
1059 progress_count
, progress_bytes
);
1062 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
1063 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
1064 progress_count
, progress_bytes
,
1068 if (delete && return_status
== FILE_CONT
) {
1069 if (ctx
->erase_at_end
) {
1070 static struct link
*tail
;
1071 lp
= g_malloc (sizeof (struct link
) + strlen (path
));
1072 strcpy (lp
->name
, path
);
1073 lp
->st_mode
= buf
.st_mode
;
1079 erase_list
= tail
= lp
;
1081 if (S_ISDIR (buf
.st_mode
)) {
1082 return_status
= erase_dir_iff_empty (ctx
, path
);
1084 return_status
= erase_file (ctx
, path
, 0, 0, 0);
1090 mc_closedir (reading
);
1092 if (ctx
->preserve
) {
1093 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1094 utb
.actime
= cbuf
.st_atime
;
1095 utb
.modtime
= cbuf
.st_mtime
;
1096 mc_utime (dest_dir
, &utb
);
1101 g_free (parent_dirs
);
1102 return return_status
;
1107 /* {{{ Move routines */
1110 move_file_file (FileOpContext
*ctx
, char *s
, char *d
,
1111 off_t
*progress_count
, double *progress_bytes
)
1113 struct stat src_stats
, dst_stats
;
1114 int return_status
= FILE_CONT
;
1116 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
1117 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1123 if (mc_lstat (s
, &src_stats
) != 0) {
1124 /* Source doesn't exist */
1126 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1127 if (return_status
== FILE_RETRY
)
1128 goto retry_src_lstat
;
1129 return return_status
;
1132 if (mc_lstat (d
, &dst_stats
) == 0) {
1133 /* Destination already exists */
1134 /* .ado: for Win32, no st_ino exists */
1135 #ifndef NATIVE_WIN32
1136 if (src_stats
.st_dev
== dst_stats
.st_dev
1137 && src_stats
.st_ino
== dst_stats
.st_ino
) {
1138 int msize
= COLS
- 36;
1139 char st
[MC_MAXPATHLEN
];
1140 char dt
[MC_MAXPATHLEN
];
1146 strcpy (st
, name_trunc (s
, msize
));
1147 strcpy (dt
, name_trunc (d
, msize
));
1148 message_3s (1, MSG_ERROR
,
1149 _(" `%s' and `%s' are the same file "), st
, dt
);
1153 #endif /* !NATIVE_WIN32 */
1154 if (S_ISDIR (dst_stats
.st_mode
)) {
1155 message_2s (1, MSG_ERROR
,
1156 _(" Cannot overwrite directory `%s' "), d
);
1161 if (confirm_overwrite
) {
1162 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1163 if (return_status
!= FILE_CONT
)
1164 return return_status
;
1166 /* Ok to overwrite */
1169 if (!ctx
->do_append
) {
1170 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1171 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1172 goto retry_src_remove
;
1174 return return_status
;
1177 if (mc_rename (s
, d
) == 0) {
1182 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1183 one nfs to the same, but on the server it is on two different
1184 filesystems. Then nfs returns EIO instead of EXDEV.
1185 Hope it will not hurt if we always in case of error try to copy/delete. */
1187 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1189 if (errno
!= EXDEV
) {
1191 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1193 if (return_status
== FILE_RETRY
)
1195 return return_status
;
1199 /* Failed because filesystem boundary -> copy the file instead */
1201 copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1202 if (return_status
!= FILE_CONT
)
1203 return return_status
;
1205 if ((return_status
=
1206 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1207 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1208 return return_status
;
1213 if (mc_unlink (s
)) {
1215 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1216 if (return_status
== FILE_RETRY
)
1217 goto retry_src_remove
;
1218 return return_status
;
1221 if (return_status
== FILE_CONT
)
1222 return_status
= progress_update_one (ctx
,
1225 src_stats
.st_size
, 1);
1227 return return_status
;
1231 move_dir_dir (FileOpContext
*ctx
, char *s
, char *d
,
1232 off_t
*progress_count
, double *progress_bytes
)
1234 struct stat sbuf
, dbuf
, destbuf
;
1240 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1241 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1247 if (mc_stat (d
, &dbuf
))
1248 destdir
= g_strdup (d
); /* destination doesn't exist */
1249 else if (!ctx
->dive_into_subdirs
) {
1250 destdir
= g_strdup (d
);
1253 destdir
= concat_dir_and_file (d
, x_basename (s
));
1254 #ifndef NATIVE_WIN32
1255 if (sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
) {
1256 int msize
= COLS
- 36;
1257 char st
[MC_MAXPATHLEN
];
1258 char dt
[MC_MAXPATHLEN
];
1264 strcpy (st
, name_trunc (s
, msize
));
1265 strcpy (dt
, name_trunc (d
, msize
));
1266 message_3s (1, MSG_ERROR
,
1267 _(" `%s' and `%s' are the same directory "), st
, dt
);
1271 #endif /* !NATIVE_WIN32 */
1273 /* Check if the user inputted an existing dir */
1275 if (!mc_stat (destdir
, &destbuf
)) {
1277 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1278 progress_count
, progress_bytes
);
1280 if (return_status
!= FILE_CONT
)
1284 if (S_ISDIR (destbuf
.st_mode
))
1287 (" Cannot overwrite directory \"%s\" %s "),
1291 file_error (_(" Cannot overwrite file \"%s\" %s "),
1293 if (return_status
== FILE_RETRY
)
1294 goto retry_dst_stat
;
1297 return return_status
;
1301 if (mc_rename (s
, destdir
) == 0) {
1302 return_status
= FILE_CONT
;
1305 /* .ado: Drive, Do we need this anymore? */
1308 /* EXDEV: cross device; does not work everywhere */
1309 if (toupper (s
[0]) != toupper (destdir
[0]))
1314 if (errno
!= EXDEV
) {
1317 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1319 if (return_status
== FILE_RETRY
)
1326 /* Failed because of filesystem boundary -> copy dir instead */
1328 copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
,
1331 if (return_status
!= FILE_CONT
)
1334 if ((return_status
=
1335 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1336 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1340 if (ctx
->erase_at_end
) {
1341 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1342 if (S_ISDIR (erase_list
->st_mode
)) {
1344 erase_dir_iff_empty (ctx
, erase_list
->name
);
1347 erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1349 erase_list
= erase_list
->next
;
1353 erase_dir_iff_empty (ctx
, s
);
1357 while (erase_list
) {
1359 erase_list
= erase_list
->next
;
1362 return return_status
;
1367 /* {{{ Erase routines */
1368 /* Don't update progress status if progress_count==NULL */
1370 erase_file (FileOpContext
*ctx
, char *s
, off_t
*progress_count
,
1371 double *progress_bytes
, int is_toplevel_file
)
1376 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1380 if (progress_count
&& mc_lstat (s
, &buf
)) {
1381 /* ignore, most likely the mc_unlink fails, too */
1385 while (mc_unlink (s
)) {
1387 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1388 if (return_status
!= FILE_RETRY
)
1389 return return_status
;
1393 return progress_update_one (ctx
, progress_count
, progress_bytes
,
1394 buf
.st_size
, is_toplevel_file
);
1400 recursive_erase (FileOpContext
*ctx
, char *s
, off_t
*progress_count
,
1401 double *progress_bytes
)
1403 struct dirent
*next
;
1407 int return_status
= FILE_CONT
;
1409 if (!strcmp (s
, ".."))
1412 reading
= mc_opendir (s
);
1417 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1418 if (!strcmp (next
->d_name
, "."))
1420 if (!strcmp (next
->d_name
, ".."))
1422 path
= concat_dir_and_file (s
, next
->d_name
);
1423 if (mc_lstat (path
, &buf
)) {
1425 mc_closedir (reading
);
1428 if (S_ISDIR (buf
.st_mode
))
1431 (ctx
, path
, progress_count
, progress_bytes
)
1435 erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1438 mc_closedir (reading
);
1439 if (return_status
!= FILE_CONT
)
1440 return return_status
;
1441 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1445 while (my_rmdir (s
)) {
1447 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1448 if (return_status
!= FILE_RETRY
)
1449 return return_status
;
1455 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1456 in the directory path points to, 0 else. */
1458 check_dir_is_empty (char *path
)
1464 dir
= mc_opendir (path
);
1468 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1469 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1470 (d
->d_name
[1] == '.'
1471 && d
->d_name
[2] == '\0')))
1472 continue; /* "." or ".." */
1482 erase_dir (FileOpContext
*ctx
, char *s
, off_t
*progress_count
,
1483 double *progress_bytes
)
1487 if (strcmp (s
, "..") == 0)
1490 if (strcmp (s
, ".") == 0)
1493 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1497 /* The old way to detect a non empty directory was:
1498 error = my_rmdir (s);
1499 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1500 For the linux user space nfs server (nfs-server-2.2beta29-2)
1501 we would have to check also for EIO. I hope the new way is
1502 fool proof. (Norbert)
1504 error
= check_dir_is_empty (s
);
1505 if (error
== 0) { /* not empty */
1506 error
= query_recursive (ctx
, s
);
1507 if (error
== FILE_CONT
)
1508 return recursive_erase (ctx
, s
, progress_count
,
1514 while (my_rmdir (s
) == -1) {
1516 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1517 if (error
!= FILE_RETRY
)
1525 erase_dir_iff_empty (FileOpContext
*ctx
, char *s
)
1529 if (strcmp (s
, "..") == 0)
1532 if (strcmp (s
, ".") == 0)
1535 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1539 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1542 while (my_rmdir (s
)) {
1544 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1545 if (error
!= FILE_RETRY
)
1554 /* {{{ Panel operate routines */
1556 /* Returns currently selected file or the first marked file if there is one */
1558 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1562 if (get_current_type () == view_tree
) {
1563 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1564 char *tree_name
= tree_selected_name (tree
);
1566 mc_stat (tree_name
, stat_buf
);
1570 if (panel
->marked
) {
1571 for (i
= 0; i
< panel
->count
; i
++)
1572 if (panel
->dir
.list
[i
].f
.marked
) {
1573 *stat_buf
= panel
->dir
.list
[i
].buf
;
1574 return panel
->dir
.list
[i
].fname
;
1577 *stat_buf
= panel
->dir
.list
[panel
->selected
].buf
;
1578 return panel
->dir
.list
[panel
->selected
].fname
;
1580 g_assert_not_reached ();
1587 * Computes the number of bytes used by the files in a directory
1590 compute_dir_size (char *dirname
, off_t
*ret_marked
, double *ret_total
)
1593 struct dirent
*dirent
;
1595 dir
= mc_opendir (dirname
);
1600 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1605 if (strcmp (dirent
->d_name
, ".") == 0)
1607 if (strcmp (dirent
->d_name
, "..") == 0)
1610 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1612 res
= mc_lstat (fullname
, &s
);
1619 if (S_ISDIR (s
.st_mode
)) {
1620 off_t subdir_count
= 0;
1621 double subdir_bytes
= 0;
1623 compute_dir_size (fullname
, &subdir_count
, &subdir_bytes
);
1625 *ret_marked
+= subdir_count
;
1626 *ret_total
+= subdir_bytes
;
1629 *ret_total
+= s
.st_size
;
1638 * panel_compute_totals:
1640 * compute the number of files and the number of bytes
1641 * used up by the whole selection, recursing directories
1642 * as required. In addition, it checks to see if it will
1643 * overwrite any files by doing the copy.
1646 panel_compute_totals (WPanel
*panel
, off_t
*ret_marked
, double *ret_total
)
1653 for (i
= 0; i
< panel
->count
; i
++) {
1656 if (!panel
->dir
.list
[i
].f
.marked
)
1659 s
= &panel
->dir
.list
[i
].buf
;
1661 if (S_ISDIR (s
->st_mode
)) {
1663 off_t subdir_count
= 0;
1664 double subdir_bytes
= 0;
1667 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1668 compute_dir_size (dir_name
, &subdir_count
, &subdir_bytes
);
1670 *ret_marked
+= subdir_count
;
1671 *ret_total
+= subdir_bytes
;
1675 *ret_total
+= s
->st_size
;
1681 * This array introduced to avoid translation problems. The former (op_names)
1682 * is assumed to be nouns, suitable in dialog box titles; this one should
1683 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1684 * Notice first symbol - it is to fool gettext and force these strings to
1685 * be different for it. First symbol is skipped while building a prompt.
1686 * (I don't use spaces around the words, because someday they could be
1687 * dropped, when widgets get smarter)
1689 static char *op_names1
[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1692 int fmd_xlen
= FMD_XLEN
;
1695 * These are formats for building a prompt. Parts encoded as follows:
1696 * %o - operation from op_names1
1697 * %f - file/files or files/directories, as appropriate
1698 * %m - "with source mask" or question mark for delete
1699 * %s - source name (truncated)
1700 * %d - number of marked files
1701 * %e - "to:" or question mark for delete
1703 * xgettext:no-c-format */
1704 static char *one_format
= N_("%o %f \"%s\"%m");
1705 /* xgettext:no-c-format */
1706 static char *many_format
= N_("%o %d %f%m");
1707 static char *prompt_parts
[] = {
1708 N_("file"), N_("files"), N_("directory"), N_("directories"),
1709 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1713 panel_operate_generate_prompt (WPanel
*panel
, int operation
, int only_one
,
1714 struct stat
*src_stat
)
1716 register char *sp
, *cp
;
1718 char format_string
[BUF_MEDIUM
];
1719 char *dp
= format_string
;
1720 char *source
= NULL
;
1723 static int i18n_flag
= 0;
1725 fmd_init_i18n (FALSE
); /* to get proper fmd_xlen */
1727 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1728 op_names1
[i
] = _(op_names1
[i
]);
1730 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1731 prompt_parts
[i
] = _(prompt_parts
[i
]);
1733 one_format
= _(one_format
);
1734 many_format
= _(many_format
);
1737 #endif /* ENABLE_NLS */
1739 sp
= only_one
? one_format
: many_format
;
1742 source
= panel_get_file (panel
, src_stat
);
1750 cp
= op_names1
[operation
] + 1;
1753 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1756 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[6];
1760 cp
= S_ISDIR (src_stat
->st_mode
) ?
1761 prompt_parts
[2] : prompt_parts
[0];
1763 cp
= (panel
->marked
== panel
->dirs_marked
)
1765 : (panel
->dirs_marked
? prompt_parts
[4]
1785 i
= fmd_xlen
- strlen (format_string
) - 4;
1786 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1787 name_trunc (source
, i
));
1789 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1791 i
= strlen (cmd_buf
) + 6 - fmd_xlen
;
1794 fmd_init_i18n (TRUE
); /* to recalculate positions of child widgets */
1804 * Performs one of the operations on the selection on the source_panel
1805 * (copy, delete, move).
1807 * Returns 1 if did change the directory
1808 * structure, Returns 0 if user aborted
1811 panel_operate (void *source_panel
, FileOperation operation
,
1812 char *thedefault
, int ask_user
)
1814 WPanel
*panel
= source_panel
;
1815 #ifdef WITH_FULL_PATHS
1816 char *source_with_path
= NULL
;
1818 # define source_with_path source
1819 #endif /* !WITH_FULL_PATHS */
1820 char *source
= NULL
;
1823 char *save_cwd
= NULL
, *save_dest
= NULL
;
1824 int only_one
= (get_current_type () == view_tree
)
1825 || (panel
->marked
<= 1);
1826 struct stat src_stat
, dst_stat
;
1834 int do_bg
; /* do background operation? */
1836 ctx
= file_op_context_new ();
1839 free_linklist (&linklist
);
1840 free_linklist (&dest_dirs
);
1841 if (get_current_type () == view_listing
)
1842 if (!panel
->marked
&& !strcmp (selection (panel
)->fname
, "..")) {
1843 message (1, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1844 file_op_context_destroy (ctx
);
1848 /* Generate confirmation prompt */
1850 panel_operate_generate_prompt (panel
, operation
, only_one
,
1853 /* Show confirmation dialog */
1854 if (operation
== OP_DELETE
&& confirm_delete
) {
1858 i
= query_dialog (_(op_names
[operation
]), cmd_buf
,
1859 D_ERROR
, 2, _("&Yes"), _("&No"));
1862 file_op_context_destroy (ctx
);
1865 } else if (operation
!= OP_DELETE
) {
1869 if (thedefault
!= NULL
)
1870 dest_dir
= thedefault
;
1871 else if (get_other_type () == view_listing
)
1872 dest_dir
= opanel
->cwd
;
1874 dest_dir
= panel
->cwd
;
1877 file_mask_dialog (ctx
, operation
, cmd_buf
, dest_dir
,
1880 file_op_context_destroy (ctx
);
1884 file_op_context_destroy (ctx
);
1889 char *all
= "^\\(.*\\)$";
1891 re_compile_pattern (all
, strlen (all
), &ctx
->rx
);
1892 ctx
->dest_mask
= g_strdup ("*");
1894 dest
= g_strdup (thedefault
);
1897 #ifdef WITH_BACKGROUND
1898 /* Did the user select to do a background operation? */
1902 v
= do_background (ctx
,
1903 g_strconcat (op_names
[operation
], ": ",
1906 message (1, MSG_ERROR
,
1907 _(" Sorry, I could not put the job in background "));
1910 /* If we are the parent */
1912 mc_setctl (panel
->cwd
, MCCTL_FORGET_ABOUT
, NULL
);
1913 mc_setctl (dest
, MCCTL_FORGET_ABOUT
, NULL
);
1914 /* file_op_context_destroy (ctx); */
1918 #endif /* WITH_BACKGROUND */
1920 /* Initialize things */
1921 /* We do not want to trash cache every time file is
1922 created/touched. However, this will make our cache contain
1925 if (mc_setctl (dest
, MCCTL_WANT_STALE_DATA
, NULL
))
1926 save_dest
= g_strdup (dest
);
1929 if (mc_setctl (panel
->cwd
, MCCTL_WANT_STALE_DATA
, NULL
))
1930 save_cwd
= g_strdup (panel
->cwd
);
1933 /* Now, let's do the job */
1938 file_op_context_create_ui (ctx
, operation
, 1);
1940 /* This code is only called by the tree and panel code */
1942 /* We now have ETA in all cases */
1944 /* One file: FIXME mc_chdir will take user out of any vfs */
1945 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1946 mc_chdir (PATH_SEP_STR
);
1948 /* The source and src_stat variables have been initialized before */
1949 #ifdef WITH_FULL_PATHS
1950 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1951 #endif /* WITH_FULL_PATHS */
1953 if (operation
== OP_DELETE
) {
1954 if (S_ISDIR (src_stat
.st_mode
))
1955 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1958 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1960 temp
= transform_source (ctx
, source_with_path
);
1963 value
= transform_error
;
1965 temp
= concat_dir_and_file (dest
, temp
);
1970 switch (operation
) {
1973 * we use file_mask_op_follow_links only with OP_COPY,
1975 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1977 if (S_ISDIR (src_stat
.st_mode
))
1979 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
1980 0, 0, 0, &count
, &bytes
);
1983 copy_file_file (ctx
, source_with_path
, dest
, 1,
1988 if (S_ISDIR (src_stat
.st_mode
))
1990 move_dir_dir (ctx
, source_with_path
, dest
,
1994 move_file_file (ctx
, source_with_path
, dest
,
1999 /* Unknown file operation */
2003 } /* Copy or move operation */
2005 if (value
== FILE_CONT
)
2006 unmark_files (panel
);
2009 /* Check destination for copy or move operation */
2010 if (operation
!= OP_DELETE
) {
2011 retry_many_dst_stat
:
2012 dst_result
= mc_stat (dest
, &dst_stat
);
2013 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
2015 (_(" Destination \"%s\" must be a directory \n %s "),
2016 dest
) == FILE_RETRY
)
2017 goto retry_many_dst_stat
;
2022 /* Initialize variables for progress bars */
2023 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
2024 panel_compute_totals (panel
, &ctx
->progress_count
,
2025 &ctx
->progress_bytes
);
2026 ctx
->progress_totals_computed
= 1;
2028 ctx
->progress_totals_computed
= 0;
2029 ctx
->progress_count
= panel
->marked
;
2030 ctx
->progress_bytes
= panel
->total
;
2033 /* Loop for every file, perform the actual copy operation */
2034 for (i
= 0; i
< panel
->count
; i
++) {
2035 if (!panel
->dir
.list
[i
].f
.marked
)
2036 continue; /* Skip the unmarked ones */
2038 source
= panel
->dir
.list
[i
].fname
;
2039 src_stat
= panel
->dir
.list
[i
].buf
;
2041 #ifdef WITH_FULL_PATHS
2042 if (source_with_path
)
2043 g_free (source_with_path
);
2044 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2045 #endif /* WITH_FULL_PATHS */
2047 if (operation
== OP_DELETE
) {
2048 if (S_ISDIR (src_stat
.st_mode
))
2050 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
2053 erase_file (ctx
, source_with_path
, &count
, &bytes
,
2059 temp
= transform_source (ctx
, source_with_path
);
2061 value
= transform_error
;
2063 temp
= concat_dir_and_file (dest
, temp
);
2065 switch (operation
) {
2068 * we use file_mask_op_follow_links only with OP_COPY,
2070 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2071 if (S_ISDIR (src_stat
.st_mode
))
2073 copy_dir_dir (ctx
, source_with_path
, temp
,
2074 1, 0, 0, 0, &count
, &bytes
);
2077 copy_file_file (ctx
, source_with_path
,
2078 temp
, 1, &count
, &bytes
,
2080 free_linklist (&dest_dirs
);
2084 if (S_ISDIR (src_stat
.st_mode
))
2086 move_dir_dir (ctx
, source_with_path
, temp
,
2090 move_file_file (ctx
, source_with_path
,
2091 temp
, &count
, &bytes
);
2095 /* Unknown file operation */
2099 } /* Copy or move operation */
2101 if (value
== FILE_ABORT
)
2104 if (value
== FILE_CONT
)
2105 do_file_mark (panel
, i
, 0);
2107 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2112 && file_progress_show_bytes (ctx
, bytes
,
2113 ctx
->progress_bytes
) ==
2117 if (operation
!= OP_DELETE
&& verbose
2118 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2122 } /* Loop for every file */
2128 mc_setctl (save_cwd
, MCCTL_NO_STALE_DATA
, NULL
);
2132 mc_setctl (save_dest
, MCCTL_NO_STALE_DATA
, NULL
);
2136 free_linklist (&linklist
);
2137 free_linklist (&dest_dirs
);
2138 #ifdef WITH_FULL_PATHS
2139 if (source_with_path
)
2140 g_free (source_with_path
);
2141 #endif /* WITH_FULL_PATHS */
2149 if (ctx
->dest_mask
) {
2150 g_free (ctx
->dest_mask
);
2151 ctx
->dest_mask
= NULL
;
2153 #ifdef WITH_BACKGROUND
2154 /* Let our parent know we are saying bye bye */
2155 if (we_are_background
) {
2157 tell_parent (MSG_CHILD_EXITING
);
2160 #endif /* WITH_BACKGROUND */
2162 file_op_context_destroy (ctx
);
2168 /* {{{ Query/status report routines */
2171 real_do_file_error (enum OperationMode mode
, char *error
)
2176 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2178 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2196 /* Report error with one file */
2198 file_error (char *format
, char *file
)
2200 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
,
2201 name_trunc (file
, 30), unix_error_string (errno
));
2203 return do_file_error (cmd_buf
);
2206 /* Report error with two files */
2208 files_error (char *format
, char *file1
, char *file2
)
2213 strcpy (nfile1
, name_trunc (file1
, 15));
2214 strcpy (nfile2
, name_trunc (file2
, 15));
2216 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
, nfile1
, nfile2
,
2217 unix_error_string (errno
));
2219 return do_file_error (cmd_buf
);
2223 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, char *s
)
2227 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2231 _("\n Directory not empty. \n"
2232 " Delete it recursively? ")
2233 : _("\n Background process: Directory not empty \n"
2234 " Delete it recursively? ");
2235 text
= g_strconcat (_(" Delete: "), name_trunc (s
, 30), " ", NULL
);
2239 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2240 _("&Yes"), _("&No"),
2241 _("A&ll"), _("Non&e"),
2244 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2249 switch (ctx
->recursive_result
) {
2251 case RECURSIVE_ALWAYS
:
2255 case RECURSIVE_NEVER
:
2258 case RECURSIVE_ABORT
:
2265 #ifdef WITH_BACKGROUND
2267 do_file_error (char *str
)
2269 if (we_are_background
)
2270 return parent_call (real_do_file_error
, NULL
, 1, strlen (str
),
2273 return real_do_file_error (Foreground
, str
);
2277 query_recursive (FileOpContext
*ctx
, char *s
)
2279 if (we_are_background
)
2280 return parent_call (real_query_recursive
, ctx
, 1, strlen (s
), s
);
2282 return real_query_recursive (ctx
, Foreground
, s
);
2286 query_replace (FileOpContext
*ctx
, char *destname
, struct stat
*_s_stat
,
2287 struct stat
*_d_stat
)
2289 if (we_are_background
)
2290 return parent_call ((void *) file_progress_real_query_replace
,
2293 strlen (destname
), destname
,
2294 sizeof (struct stat
), _s_stat
,
2295 sizeof (struct stat
), _d_stat
);
2297 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2303 do_file_error (char *str
)
2305 return real_do_file_error (Foreground
, str
);
2309 query_recursive (FileOpContext
*ctx
, char *s
)
2311 return real_query_recursive (ctx
, Foreground
, s
);
2315 query_replace (FileOpContext
*ctx
, char *destname
, struct stat
*_s_stat
,
2316 struct stat
*_d_stat
)
2318 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2322 #endif /* !WITH_BACKGROUND */
2325 Cause emacs to enter folding mode for this file: