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
, _(" Could not 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 can not 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 /* No problem with Gnome, as get_current_type never returns view_tree there */
1563 if (get_current_type () == view_tree
) {
1564 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1566 mc_stat (tree
->selected_ptr
->name
, stat_buf
);
1567 return tree
->selected_ptr
->name
;
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 ();
1585 is_wildcarded (char *p
)
1590 else if (*p
== '\\' && p
[1] >= '1' && p
[1] <= '9')
1599 * Computes the number of bytes used by the files in a directory
1602 compute_dir_size (char *dirname
, off_t
*ret_marked
, double *ret_total
)
1605 struct dirent
*dirent
;
1607 dir
= mc_opendir (dirname
);
1612 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1617 if (strcmp (dirent
->d_name
, ".") == 0)
1619 if (strcmp (dirent
->d_name
, "..") == 0)
1622 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1624 res
= mc_lstat (fullname
, &s
);
1631 if (S_ISDIR (s
.st_mode
)) {
1632 off_t subdir_count
= 0;
1633 double subdir_bytes
= 0;
1635 compute_dir_size (fullname
, &subdir_count
, &subdir_bytes
);
1637 *ret_marked
+= subdir_count
;
1638 *ret_total
+= subdir_bytes
;
1641 *ret_total
+= s
.st_size
;
1650 * panel_compute_totals:
1652 * compute the number of files and the number of bytes
1653 * used up by the whole selection, recursing directories
1654 * as required. In addition, it checks to see if it will
1655 * overwrite any files by doing the copy.
1658 panel_compute_totals (WPanel
*panel
, off_t
*ret_marked
, double *ret_total
)
1665 for (i
= 0; i
< panel
->count
; i
++) {
1668 if (!panel
->dir
.list
[i
].f
.marked
)
1671 s
= &panel
->dir
.list
[i
].buf
;
1673 if (S_ISDIR (s
->st_mode
)) {
1675 off_t subdir_count
= 0;
1676 double subdir_bytes
= 0;
1679 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1680 compute_dir_size (dir_name
, &subdir_count
, &subdir_bytes
);
1682 *ret_marked
+= subdir_count
;
1683 *ret_total
+= subdir_bytes
;
1687 *ret_total
+= s
->st_size
;
1693 * This array introduced to avoid translation problems. The former (op_names)
1694 * is assumed to be nouns, suitable in dialog box titles; this one should
1695 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1696 * Notice first symbol - it is to fool gettext and force these strings to
1697 * be different for it. First symbol is skipped while building a prompt.
1698 * (I don't use spaces around the words, because someday they could be
1699 * dropped, when widgets get smarter)
1701 static char *op_names1
[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1704 int fmd_xlen
= FMD_XLEN
;
1707 * These are formats for building a prompt. Parts encoded as follows:
1708 * %o - operation from op_names1
1709 * %f - file/files or files/directories, as appropriate
1710 * %m - "with source mask" or question mark for delete
1711 * %s - source name (truncated)
1712 * %d - number of marked files
1713 * %e - "to:" or question mark for delete
1715 * xgettext:no-c-format */
1716 static char *one_format
= N_("%o %f \"%s\"%m");
1717 /* xgettext:no-c-format */
1718 static char *many_format
= N_("%o %d %f%m");
1719 static char *prompt_parts
[] = {
1720 N_("file"), N_("files"), N_("directory"), N_("directories"),
1721 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1725 panel_operate_generate_prompt (WPanel
*panel
, int operation
, int only_one
,
1726 struct stat
*src_stat
)
1728 register char *sp
, *cp
;
1730 char format_string
[BUF_MEDIUM
];
1731 char *dp
= format_string
;
1732 char *source
= NULL
;
1735 static int i18n_flag
= 0;
1737 fmd_init_i18n (FALSE
); /* to get proper fmd_xlen */
1739 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1740 op_names1
[i
] = _(op_names1
[i
]);
1742 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1743 prompt_parts
[i
] = _(prompt_parts
[i
]);
1745 one_format
= _(one_format
);
1746 many_format
= _(many_format
);
1749 #endif /* ENABLE_NLS */
1751 sp
= only_one
? one_format
: many_format
;
1754 source
= panel_get_file (panel
, src_stat
);
1762 cp
= op_names1
[operation
] + 1;
1765 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1768 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[6];
1772 cp
= S_ISDIR (src_stat
->st_mode
) ?
1773 prompt_parts
[2] : prompt_parts
[0];
1775 cp
= (panel
->marked
== panel
->dirs_marked
)
1777 : (panel
->dirs_marked
? prompt_parts
[4]
1797 i
= fmd_xlen
- strlen (format_string
) - 4;
1798 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1799 name_trunc (source
, i
));
1801 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1803 i
= strlen (cmd_buf
) + 6 - fmd_xlen
;
1806 fmd_init_i18n (TRUE
); /* to recalculate positions of child widgets */
1816 * Performs one of the operations on the selection on the source_panel
1817 * (copy, delete, move).
1819 * Returns 1 if did change the directory
1820 * structure, Returns 0 if user aborted
1823 panel_operate (void *source_panel
, FileOperation operation
,
1824 char *thedefault
, int ask_user
)
1826 WPanel
*panel
= source_panel
;
1827 #ifdef WITH_FULL_PATHS
1828 char *source_with_path
= NULL
;
1830 # define source_with_path source
1831 #endif /* !WITH_FULL_PATHS */
1832 char *source
= NULL
;
1835 char *save_cwd
= NULL
, *save_dest
= NULL
;
1836 int only_one
= (get_current_type () == view_tree
)
1837 || (panel
->marked
<= 1);
1838 struct stat src_stat
, dst_stat
;
1846 int do_bg
; /* do background operation? */
1848 ctx
= file_op_context_new ();
1851 ctx
->rx
.buffer
= NULL
;
1852 free_linklist (&linklist
);
1853 free_linklist (&dest_dirs
);
1854 if (get_current_type () == view_listing
)
1855 if (!panel
->marked
&& !strcmp (selection (panel
)->fname
, "..")) {
1856 message (1, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1857 file_op_context_destroy (ctx
);
1861 if (operation
< OP_COPY
|| operation
> OP_DELETE
) {
1862 file_op_context_destroy (ctx
);
1866 /* Generate confirmation prompt */
1868 panel_operate_generate_prompt (panel
, operation
, only_one
,
1871 /* Show confirmation dialog */
1872 if (operation
== OP_DELETE
&& confirm_delete
) {
1876 i
= query_dialog (_(op_names
[operation
]), cmd_buf
,
1877 D_ERROR
, 2, _("&Yes"), _("&No"));
1880 file_op_context_destroy (ctx
);
1883 } else if (operation
!= OP_DELETE
) {
1884 ctx
->rx
.buffer
= (char *) g_malloc (MC_MAXPATHLEN
);
1885 ctx
->rx
.allocated
= MC_MAXPATHLEN
;
1886 ctx
->rx
.translate
= 0;
1891 if (thedefault
!= NULL
)
1892 dest_dir
= thedefault
;
1893 else if (get_other_type () == view_listing
)
1894 dest_dir
= opanel
->cwd
;
1896 dest_dir
= panel
->cwd
;
1899 file_mask_dialog (ctx
, operation
, cmd_buf
, dest_dir
,
1902 g_free (ctx
->rx
.buffer
);
1903 file_op_context_destroy (ctx
);
1907 g_free (ctx
->rx
.buffer
);
1908 file_op_context_destroy (ctx
);
1913 char *all
= "^\\(.*\\)$";
1915 re_compile_pattern (all
, strlen (all
), &ctx
->rx
);
1916 ctx
->dest_mask
= g_strdup ("*");
1918 dest
= g_strdup (thedefault
);
1921 #ifdef WITH_BACKGROUND
1922 /* Did the user select to do a background operation? */
1926 v
= do_background (ctx
,
1927 g_strconcat (op_names
[operation
], ": ",
1930 message (1, MSG_ERROR
,
1931 _(" Sorry, I could not put the job in background "));
1934 /* If we are the parent */
1936 mc_setctl (panel
->cwd
, MCCTL_FORGET_ABOUT
, NULL
);
1937 mc_setctl (dest
, MCCTL_FORGET_ABOUT
, NULL
);
1938 /* file_op_context_destroy (ctx); */
1942 #endif /* WITH_BACKGROUND */
1944 /* Initialize things */
1945 /* We do not want to trash cache every time file is
1946 created/touched. However, this will make our cache contain
1949 if (mc_setctl (dest
, MCCTL_WANT_STALE_DATA
, NULL
))
1950 save_dest
= g_strdup (dest
);
1953 if (mc_setctl (panel
->cwd
, MCCTL_WANT_STALE_DATA
, NULL
))
1954 save_cwd
= g_strdup (panel
->cwd
);
1957 /* Now, let's do the job */
1962 file_op_context_create_ui (ctx
, operation
, 1);
1964 /* This code is only called by the tree and panel code */
1966 /* We now have ETA in all cases */
1968 /* One file: FIXME mc_chdir will take user out of any vfs */
1969 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1970 mc_chdir (PATH_SEP_STR
);
1972 /* The source and src_stat variables have been initialized before */
1973 #ifdef WITH_FULL_PATHS
1974 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1975 #endif /* WITH_FULL_PATHS */
1977 if (operation
== OP_DELETE
) {
1978 if (S_ISDIR (src_stat
.st_mode
))
1979 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1982 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1984 temp
= transform_source (ctx
, source_with_path
);
1987 value
= transform_error
;
1989 temp
= concat_dir_and_file (dest
, temp
);
1994 switch (operation
) {
1997 * we use file_mask_op_follow_links only with OP_COPY,
1999 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2001 if (S_ISDIR (src_stat
.st_mode
))
2003 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
2004 0, 0, 0, &count
, &bytes
);
2007 copy_file_file (ctx
, source_with_path
, dest
, 1,
2012 if (S_ISDIR (src_stat
.st_mode
))
2014 move_dir_dir (ctx
, source_with_path
, dest
,
2018 move_file_file (ctx
, source_with_path
, dest
,
2023 /* Unknown file operation */
2027 } /* Copy or move operation */
2029 if (value
== FILE_CONT
)
2030 unmark_files (panel
);
2033 /* Check destination for copy or move operation */
2034 if (operation
!= OP_DELETE
) {
2035 retry_many_dst_stat
:
2036 dst_result
= mc_stat (dest
, &dst_stat
);
2037 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
2039 (_(" Destination \"%s\" must be a directory \n %s "),
2040 dest
) == FILE_RETRY
)
2041 goto retry_many_dst_stat
;
2046 /* Initialize variables for progress bars */
2047 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
2048 panel_compute_totals (panel
, &ctx
->progress_count
,
2049 &ctx
->progress_bytes
);
2050 ctx
->progress_totals_computed
= 1;
2052 ctx
->progress_totals_computed
= 0;
2053 ctx
->progress_count
= panel
->marked
;
2054 ctx
->progress_bytes
= panel
->total
;
2057 /* Loop for every file, perform the actual copy operation */
2058 for (i
= 0; i
< panel
->count
; i
++) {
2059 if (!panel
->dir
.list
[i
].f
.marked
)
2060 continue; /* Skip the unmarked ones */
2062 source
= panel
->dir
.list
[i
].fname
;
2063 src_stat
= panel
->dir
.list
[i
].buf
;
2065 #ifdef WITH_FULL_PATHS
2066 if (source_with_path
)
2067 g_free (source_with_path
);
2068 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2069 #endif /* WITH_FULL_PATHS */
2071 if (operation
== OP_DELETE
) {
2072 if (S_ISDIR (src_stat
.st_mode
))
2074 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
2077 erase_file (ctx
, source_with_path
, &count
, &bytes
,
2083 temp
= transform_source (ctx
, source_with_path
);
2085 value
= transform_error
;
2087 temp
= concat_dir_and_file (dest
, temp
);
2089 switch (operation
) {
2092 * we use file_mask_op_follow_links only with OP_COPY,
2094 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2095 if (S_ISDIR (src_stat
.st_mode
))
2097 copy_dir_dir (ctx
, source_with_path
, temp
,
2098 1, 0, 0, 0, &count
, &bytes
);
2101 copy_file_file (ctx
, source_with_path
,
2102 temp
, 1, &count
, &bytes
,
2104 free_linklist (&dest_dirs
);
2108 if (S_ISDIR (src_stat
.st_mode
))
2110 move_dir_dir (ctx
, source_with_path
, temp
,
2114 move_file_file (ctx
, source_with_path
,
2115 temp
, &count
, &bytes
);
2119 /* Unknown file operation */
2123 } /* Copy or move operation */
2125 if (value
== FILE_ABORT
)
2128 if (value
== FILE_CONT
)
2129 do_file_mark (panel
, i
, 0);
2131 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2136 && file_progress_show_bytes (ctx
, bytes
,
2137 ctx
->progress_bytes
) ==
2141 if (operation
!= OP_DELETE
&& verbose
2142 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2146 } /* Loop for every file */
2152 mc_setctl (save_cwd
, MCCTL_NO_STALE_DATA
, NULL
);
2156 mc_setctl (save_dest
, MCCTL_NO_STALE_DATA
, NULL
);
2160 free_linklist (&linklist
);
2161 free_linklist (&dest_dirs
);
2162 #ifdef WITH_FULL_PATHS
2163 if (source_with_path
)
2164 g_free (source_with_path
);
2165 #endif /* WITH_FULL_PATHS */
2173 if (ctx
->rx
.buffer
) {
2174 g_free (ctx
->rx
.buffer
);
2175 ctx
->rx
.buffer
= NULL
;
2178 if (ctx
->dest_mask
) {
2179 g_free (ctx
->dest_mask
);
2180 ctx
->dest_mask
= NULL
;
2182 #ifdef WITH_BACKGROUND
2183 /* Let our parent know we are saying bye bye */
2184 if (we_are_background
) {
2186 tell_parent (MSG_CHILD_EXITING
);
2189 #endif /* WITH_BACKGROUND */
2191 file_op_context_destroy (ctx
);
2197 /* {{{ Query/status report routines */
2200 real_do_file_error (enum OperationMode mode
, char *error
)
2205 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2207 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2225 /* Report error with one file */
2227 file_error (char *format
, char *file
)
2229 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
,
2230 name_trunc (file
, 30), unix_error_string (errno
));
2232 return do_file_error (cmd_buf
);
2235 /* Report error with two files */
2237 files_error (char *format
, char *file1
, char *file2
)
2242 strcpy (nfile1
, name_trunc (file1
, 15));
2243 strcpy (nfile2
, name_trunc (file2
, 15));
2245 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
, nfile1
, nfile2
,
2246 unix_error_string (errno
));
2248 return do_file_error (cmd_buf
);
2252 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, char *s
)
2256 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2260 _("\n Directory not empty. \n"
2261 " Delete it recursively? ")
2262 : _("\n Background process: Directory not empty \n"
2263 " Delete it recursively? ");
2264 text
= g_strconcat (_(" Delete: "), name_trunc (s
, 30), " ", NULL
);
2268 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2269 _("&Yes"), _("&No"),
2270 _("A&ll"), _("Non&e"),
2273 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2278 switch (ctx
->recursive_result
) {
2280 case RECURSIVE_ALWAYS
:
2284 case RECURSIVE_NEVER
:
2287 case RECURSIVE_ABORT
:
2294 #ifdef WITH_BACKGROUND
2296 do_file_error (char *str
)
2298 if (we_are_background
)
2299 return parent_call (real_do_file_error
, NULL
, 1, strlen (str
),
2302 return real_do_file_error (Foreground
, str
);
2306 query_recursive (FileOpContext
*ctx
, char *s
)
2308 if (we_are_background
)
2309 return parent_call (real_query_recursive
, ctx
, 1, strlen (s
), 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 if (we_are_background
)
2319 return parent_call ((void *) file_progress_real_query_replace
,
2322 strlen (destname
), destname
,
2323 sizeof (struct stat
), _s_stat
,
2324 sizeof (struct stat
), _d_stat
);
2326 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2332 do_file_error (char *str
)
2334 return real_do_file_error (Foreground
, str
);
2338 query_recursive (FileOpContext
*ctx
, char *s
)
2340 return real_query_recursive (ctx
, Foreground
, s
);
2344 query_replace (FileOpContext
*ctx
, char *destname
, struct stat
*_s_stat
,
2345 struct stat
*_d_stat
)
2347 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2351 #endif /* !WITH_BACKGROUND */
2354 Cause emacs to enter folding mode for this file: