2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1994, 1995 Janne Kukonlehto
6 1994, 1995 Fred Leeflang
7 1994, 1995, 1996 Miguel de Icaza
8 1995, 1996 Jakub Jelinek
12 The copy code was based in GNU's cp, and was written by:
13 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
15 The move code was based in GNU's mv, and was written by:
16 Mike Parker and David MacKenzie.
18 Janne Kukonlehto added much error recovery to them for being used
19 in an interactive program.
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
36 * Please note that all dialogs used here must be safe for background
41 * \brief Source: file management
44 /* {{{ Include files */
53 #include <sys/types.h>
58 #include "lib/global.h"
59 #include "lib/tty/tty.h"
60 #include "lib/tty/key.h"
61 #include "lib/search.h"
62 #include "lib/vfs/mc-vfs/vfs-impl.h"
63 #include "lib/vfs/mc-vfs/vfs.h"
64 #include "lib/strescape.h"
65 #include "lib/strutil.h"
74 #include "background.h" /* we_are_background */
76 /* Needed for current_panel, other_panel and WTree */
85 /* Hack: the vfs code should not rely on this */
86 #define WITH_FULL_PATHS 1
88 #define FILEOP_UPDATE_INTERVAL 2
89 #define FILEOP_STALLING_INTERVAL 4
94 * Whether the Midnight Commander tries to provide more
95 * information about copy/move sizes and bytes transfered
96 * at the expense of some speed
98 int file_op_compute_totals
= 1;
100 /* This is a hard link cache */
103 struct vfs_class
*vfs
;
111 /* the hard link cache */
112 static struct link
*linklist
= NULL
;
114 /* the files-to-be-erased list */
115 static struct link
*erase_list
;
118 * In copy_dir_dir we use two additional single linked lists: The first -
119 * variable name `parent_dirs' - holds information about already copied
120 * directories and is used to detect cyclic symbolic links.
121 * The second (`dest_dirs' below) holds information about just created
122 * target directories and is used to detect when an directory is copied
123 * into itself (we don't want to copy infinitly).
124 * Both lists don't use the linkcount and name structure members of struct
127 static struct link
*dest_dirs
= NULL
;
129 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
130 const char *op_names
[3] = {
131 N_("DialogTitle|Copy"),
132 N_("DialogTitle|Move"),
133 N_("DialogTitle|Delete")
138 static FileProgressStatus
query_replace (FileOpContext
* ctx
, const char *destname
,
139 struct stat
*_s_stat
, struct stat
*_d_stat
);
140 static FileProgressStatus
query_recursive (FileOpContext
* ctx
, const char *s
);
141 static FileProgressStatus
do_file_error (const char *str
);
142 static FileProgressStatus
erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
);
143 static FileProgressStatus
erase_file (FileOpTotalContext
*tctx
, FileOpContext
*ctx
,
144 const char *s
, gboolean is_toplevel_file
);
145 static FileProgressStatus
files_error (const char *format
, const char *file1
,
148 static FileProgressStatus transform_error
= FILE_CONT
;
151 transform_source (FileOpContext
*ctx
, const char *source
)
156 s
= g_strdup (source
);
158 /* We remove \n from the filename since regex routines would use \n as an anchor */
159 /* this is just to be allowed to maniupulate file names with \n on it */
160 for (q
= s
; *q
!= '\0'; q
++)
164 fnsource
= (char *) x_basename (s
);
166 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
167 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
170 transform_error
= FILE_SKIP
;
178 free_linklist (struct link
**lc_linklist
)
180 struct link
*lp
, *lp2
;
182 for (lp
= *lc_linklist
; lp
!= NULL
; lp
= lp2
) {
190 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
192 ino_t ino
= sb
->st_ino
;
193 dev_t dev
= sb
->st_dev
;
195 struct vfs_class
*vfs
= vfs_get_class (path
);
196 #endif /* ENABLE_VFS */
203 #endif /* ENABLE_VFS */
204 if (lp
->ino
== ino
&& lp
->dev
== dev
)
212 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
213 * and a hardlink was succesfully made
216 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
219 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
220 ino_t ino
= pstat
->st_ino
;
221 dev_t dev
= pstat
->st_dev
;
222 struct stat link_stat
;
225 if (vfs_file_class_flags (src_name
) & VFSF_NOLINKS
)
228 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
229 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
) {
230 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
231 && link_stat
.st_dev
== dev
232 && vfs_get_class (lp
->name
) == my_vfs
) {
233 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
235 if (vfs_get_class (dst_name
) == vfs_get_class (p
)) {
236 if (!mc_stat (p
, &link_stat
)) {
237 if (!mc_link (p
, dst_name
))
242 message (D_ERROR
, MSG_ERROR
, _(" Cannot make the hardlink "));
245 lp
= (struct link
*) g_try_malloc (sizeof (struct link
) + strlen (src_name
)
246 + strlen (dst_name
) + 1);
252 strcpy (lp
->name
, src_name
);
253 lpdstname
= lp
->name
+ strlen(lp
->name
) + 1;
254 strcpy (lpdstname
, dst_name
);
262 * Duplicate the contents of the symbolic link src_path in dst_path.
263 * Try to make a stable symlink if the option "stable symlink" was
264 * set in the file mask dialog.
265 * If dst_path is an existing symlink it will be deleted silently
266 * (upper levels take already care of existing files at dst_path).
268 static FileProgressStatus
269 make_symlink (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
)
271 char link_target
[MC_MAXPATHLEN
];
273 FileProgressStatus return_status
;
275 gboolean dst_is_symlink
;
277 dst_is_symlink
= (mc_lstat (dst_path
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
280 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
283 file_error (_(" Cannot read source link \"%s\" \n %s "),
285 if (return_status
== FILE_RETRY
)
286 goto retry_src_readlink
;
287 return return_status
;
289 link_target
[len
] = 0;
291 if (ctx
->stable_symlinks
)
292 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
293 message (D_ERROR
, MSG_ERROR
,
294 _(" Cannot make stable symlinks across "
295 "non-local filesystems: \n\n"
296 " Option Stable Symlinks will be disabled "));
297 ctx
->stable_symlinks
= FALSE
;
300 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
)) {
303 const char *r
= strrchr (src_path
, PATH_SEP
);
306 p
= g_strndup (src_path
, r
- src_path
+ 1);
307 if (g_path_is_absolute (dst_path
))
308 q
= g_strdup (dst_path
);
310 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
311 s
= strrchr (q
, PATH_SEP
);
314 s
= g_strconcat (p
, link_target
, (char *) NULL
);
316 g_strlcpy (link_target
, s
, sizeof (link_target
));
318 s
= diff_two_paths (q
, link_target
);
320 g_strlcpy (link_target
, s
, sizeof (link_target
));
329 if (mc_symlink (link_target
, dst_path
) == 0)
333 * if dst_exists, it is obvious that this had failed.
334 * We can delete the old symlink and try again...
336 if (dst_is_symlink
) {
337 if (!mc_unlink (dst_path
))
338 if (mc_symlink (link_target
, dst_path
) == 0)
343 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
345 if (return_status
== FILE_RETRY
)
346 goto retry_dst_symlink
;
347 return return_status
;
350 static FileProgressStatus
351 progress_update_one (FileOpTotalContext
*tctx
, FileOpContext
*ctx
, off_t add
, gboolean is_toplevel_file
)
353 struct timeval tv_current
;
354 static struct timeval tv_start
= {};
356 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
357 tctx
->progress_count
++;
358 tctx
->progress_bytes
+= add
;
360 if (tv_start
.tv_sec
== 0) {
361 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
363 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
364 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
366 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
367 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
368 tv_start
.tv_sec
= tv_current
.tv_sec
;
371 return check_progress_buttons (ctx
);
374 /* Status of the destination file */
376 DEST_NONE
= 0, /* Not created */
377 DEST_SHORT
= 1, /* Created, not fully copied */
378 DEST_FULL
= 2 /* Created, fully copied */
381 static FileProgressStatus
382 real_warn_same_file (enum OperationMode mode
, const char *fmt
,
383 const char *a
, const char *b
)
387 const char *head_msg
;
389 head_msg
= mode
== Foreground
? MSG_ERROR
:
390 _(" Background process error ");
392 msg
= g_strdup_printf (fmt
, a
, b
);
393 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
396 if ( result
) { /* 1 == Abort */
403 #ifdef WITH_BACKGROUND
404 static FileProgressStatus
405 warn_same_file (const char *fmt
, const char *a
, const char *b
)
409 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
410 const char *a
, const char *b
);
412 pntr
.f
= real_warn_same_file
;
414 if (we_are_background
)
415 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
),
416 fmt
, strlen(a
), a
, strlen(b
), b
);
418 return real_warn_same_file (Foreground
, fmt
, a
, b
);
421 static FileProgressStatus
422 warn_same_file (const char *fmt
, const char *a
, const char *b
)
424 return real_warn_same_file (Foreground
, fmt
, a
, b
);
429 copy_file_file_display_progress (FileOpTotalContext
*tctx
, FileOpContext
*ctx
,
430 struct timeval tv_current
, struct timeval tv_transfer_start
,
431 off_t file_size
, off_t n_read_total
)
435 /* 1. Update rotating dash after some time */
439 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
442 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
443 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
447 /* 4. Compute BPS rate */
448 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
449 if (ctx
->bps_time
< 1)
451 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
453 /* 5. Compute total ETA and BPS*/
454 if (ctx
->progress_bytes
!= 0) {
456 tctx
->copyed_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
457 remain_bytes
= ctx
->progress_bytes
- tctx
->copyed_bytes
;
460 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
464 tctx
->bps
= tctx
->copyed_bytes
/ total_secs
;
465 tctx
->eta_secs
= remain_bytes
/ tctx
->bps
;
468 /* broken on lot of little files */
470 tctx
->bps
= ( tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
471 tctx
->eta_secs
= remain_bytes
/ tctx
->bps
;
477 copy_file_file (FileOpTotalContext
*tctx
, FileOpContext
*ctx
,
478 const char *src_path
, const char *dst_path
)
480 uid_t src_uid
= (uid_t
) -1;
481 gid_t src_gid
= (gid_t
) -1;
483 int src_desc
, dest_desc
= -1;
484 int n_read
, n_written
;
485 mode_t src_mode
= 0; /* The mode of the source file */
488 gboolean dst_exists
= FALSE
, appending
= FALSE
;
489 off_t n_read_total
= 0, file_size
= -1;
490 FileProgressStatus return_status
, temp_status
;
491 struct timeval tv_transfer_start
;
492 dest_status_t dst_status
= DEST_NONE
;
494 gboolean is_first_time
=TRUE
;
496 /* FIXME: We should not be using global variables! */
498 return_status
= FILE_RETRY
;
500 file_progress_show_source (ctx
, src_path
);
501 file_progress_show_target (ctx
, dst_path
);
502 if (check_progress_buttons (ctx
) == FILE_ABORT
)
507 while (mc_stat (dst_path
, &sb2
) == 0) {
508 if (S_ISDIR (sb2
.st_mode
)) {
510 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
512 if (return_status
== FILE_RETRY
)
514 return return_status
;
520 while ((*ctx
->stat_func
) (src_path
, &sb
)) {
522 file_error (_(" Cannot stat source file \"%s\" \n %s "),
524 if (return_status
!= FILE_RETRY
)
525 return return_status
;
529 /* Destination already exists */
530 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
531 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "),
533 /* Should we replace destination? */
534 if (tctx
->ask_overwrite
) {
536 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
537 if (return_status
!= FILE_CONT
)
538 return return_status
;
542 if (!ctx
->do_append
) {
543 /* Check the hardlinks */
544 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
545 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
546 /* We have made a hardlink - no more processing is necessary */
550 if (S_ISLNK (sb
.st_mode
))
551 return make_symlink (ctx
, src_path
, dst_path
);
553 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
554 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) ||
555 S_ISSOCK (sb
.st_mode
)) {
556 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
558 return_status
= file_error (
559 _(" Cannot create special file \"%s\" \n %s "), dst_path
);
560 if (return_status
== FILE_RETRY
)
562 return return_status
;
566 while (ctx
->preserve_uidgid
567 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
568 temp_status
= file_error (
569 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
570 if (temp_status
== FILE_RETRY
)
574 while (ctx
->preserve
&&
575 mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
)) {
576 temp_status
= file_error (
577 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
578 if (temp_status
== FILE_RETRY
)
586 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
588 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
589 return_status
= file_error (
590 _(" Cannot open source file \"%s\" \n %s "), src_path
);
591 if (return_status
== FILE_RETRY
)
594 return return_status
;
597 if (ctx
->do_reget
!= 0) {
598 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
) {
599 message (D_ERROR
, _("Warning"),
600 _(" Reget failed, about to overwrite file "));
602 ctx
->do_append
= FALSE
;
606 while (mc_fstat (src_desc
, &sb
)) {
607 return_status
= file_error (
608 _(" Cannot fstat source file \"%s\" \n %s "), src_path
);
609 if (return_status
== FILE_RETRY
)
611 ctx
->do_append
= FALSE
;
614 src_mode
= sb
.st_mode
;
617 utb
.actime
= sb
.st_atime
;
618 utb
.modtime
= sb
.st_mtime
;
619 file_size
= sb
.st_size
;
621 open_flags
= O_WRONLY
;
623 if (ctx
->do_append
!= 0)
624 open_flags
|= O_APPEND
;
626 open_flags
|= O_CREAT
| O_TRUNC
;
628 open_flags
|= O_CREAT
| O_EXCL
;
631 while ((dest_desc
= mc_open (dst_path
, open_flags
, src_mode
)) < 0) {
632 if (errno
== EEXIST
) {
635 return_status
= file_error (
636 _(" Cannot create target file \"%s\" \n %s "), dst_path
);
637 if (return_status
== FILE_RETRY
)
639 ctx
->do_append
= FALSE
;
642 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
644 appending
= ctx
->do_append
;
645 ctx
->do_append
= FALSE
;
647 /* Find out the optimal buffer size. */
648 while (mc_fstat (dest_desc
, &sb
)) {
649 return_status
= file_error (
650 _(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
651 if (return_status
== FILE_RETRY
)
659 if (tctx
->bps
== 0 || (file_size
/(tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
) {
660 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
662 file_progress_show (ctx
, 1, 1, "", TRUE
);
664 return_status
= check_progress_buttons (ctx
);
667 if (return_status
!= FILE_CONT
)
671 struct timeval tv_current
, tv_last_update
, tv_last_input
;
672 int secs
, update_secs
;
673 const char *stalled_msg
="";
675 tv_last_update
= tv_transfer_start
;
681 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
684 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0) {
685 return_status
= file_error (
686 _(" Cannot read source file \"%s\" \n %s "), src_path
);
687 if (return_status
== FILE_RETRY
)
694 gettimeofday (&tv_current
, NULL
);
698 n_read_total
+= n_read
;
700 /* Windows NT ftp servers report that files have no
701 * permissions: -------, so if we happen to have actually
702 * read something, we should fix the permissions.
704 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
705 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
706 gettimeofday (&tv_last_input
, NULL
);
710 mc_write (dest_desc
, t
, n_read
)) < n_read
) {
717 file_error (_(" Cannot write target file \"%s\" \n %s "),
719 if (return_status
!= FILE_RETRY
)
723 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
724 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
726 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
728 copy_file_file_display_progress(tctx
, ctx
,
733 tv_last_update
= tv_current
;
735 is_first_time
= FALSE
;
737 if (update_secs
> FILEOP_STALLING_INTERVAL
) {
738 stalled_msg
= _("(stalled)");
741 gboolean force_update
=
742 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
743 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
744 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
,
748 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
753 return_status
= check_progress_buttons (ctx
);
755 if (return_status
!= FILE_CONT
) {
762 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
765 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
766 temp_status
= file_error (
767 _(" Cannot close source file \"%s\" \n %s "), src_path
);
768 if (temp_status
== FILE_RETRY
)
770 if (temp_status
== FILE_ABORT
)
771 return_status
= temp_status
;
775 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
776 temp_status
= file_error (
777 _(" Cannot close target file \"%s\" \n %s "), dst_path
);
778 if (temp_status
== FILE_RETRY
)
780 return_status
= temp_status
;
784 if (dst_status
== DEST_SHORT
) {
785 /* Remove short file */
787 result
= query_dialog (Q_("DialogTitle|Copy"),
788 _("Incomplete file was retrieved. Keep it?"),
789 D_ERROR
, 2, _("&Delete"), _("&Keep"));
791 mc_unlink (dst_path
);
792 } else if (dst_status
== DEST_FULL
) {
793 /* Copy has succeeded */
794 if (!appending
&& ctx
->preserve_uidgid
) {
795 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
796 temp_status
= file_error (
797 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
798 if (temp_status
== FILE_RETRY
)
800 return_status
= temp_status
;
807 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
))) {
808 temp_status
= file_error (
809 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
810 if (temp_status
!= FILE_RETRY
) {
811 return_status
= temp_status
;
816 src_mode
= umask(-1);
818 src_mode
= 0100666 & ~src_mode
;
819 mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
));
821 mc_utime (dst_path
, &utb
);
825 if (return_status
== FILE_CONT
)
826 return_status
= progress_update_one (tctx
, ctx
, file_size
, tctx
->is_toplevel_file
);
828 return return_status
;
831 * I think these copy_*_* functions should have a return type.
832 * anyway, this function *must* have two directories as arguments.
834 /* FIXME: This function needs to check the return values of the
837 copy_dir_dir (FileOpTotalContext
*tctx
, FileOpContext
*ctx
, const char *s
, const char *_d
,
838 gboolean toplevel
, gboolean move_over
, gboolean do_delete
,
839 struct link
*parent_dirs
)
842 struct stat buf
, cbuf
;
844 char *dest_dir
= NULL
;
845 FileProgressStatus return_status
= FILE_CONT
;
850 d
= strutils_shell_unescape (_d
);
852 /* First get the mode of the source dir */
854 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
856 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
857 if (return_status
== FILE_RETRY
)
862 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
863 /* Don't copy a directory we created before (we don't want to copy
864 infinitely if a directory is copied into itself) */
865 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
866 return_status
= FILE_CONT
;
870 /* Hmm, hardlink to directory??? - Norbert */
871 /* FIXME: In this step we should do something
872 in case the destination already exist */
873 /* Check the hardlinks */
874 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
875 && check_hardlinks (s
, d
, &cbuf
) == 1) {
876 /* We have made a hardlink - no more processing is necessary */
880 if (!S_ISDIR (cbuf
.st_mode
)) {
882 file_error (_(" Source \"%s\" is not a directory \n %s "), s
);
883 if (return_status
== FILE_RETRY
)
888 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
889 /* we found a cyclic symbolic link */
890 message (D_ERROR
, MSG_ERROR
,
891 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
892 return_status
= FILE_SKIP
;
896 lp
= g_new (struct link
, 1);
897 lp
->vfs
= vfs_get_class (s
);
898 lp
->ino
= cbuf
.st_ino
;
899 lp
->dev
= cbuf
.st_dev
;
900 lp
->next
= parent_dirs
;
904 /* Now, check if the dest dir exists, if not, create it. */
905 if (mc_stat (d
, &buf
)) {
906 /* Here the dir doesn't exist : make it ! */
908 if (mc_rename (s
, d
) == 0) {
909 return_status
= FILE_CONT
;
917 * If the destination directory exists, we want to copy the whole
918 * directory, but we only want this to happen once.
920 * Escape sequences added to the * to compiler warnings.
921 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
922 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
924 if (!S_ISDIR (buf
.st_mode
)) {
925 return_status
= file_error(
926 _(" Destination \"%s\" must be a directory \n %s "), d
);
927 if (return_status
== FILE_RETRY
)
931 /* Dive into subdir if exists */
932 if (toplevel
&& ctx
->dive_into_subdirs
) {
933 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
940 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
941 return_status
= file_error (
942 _(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
943 if (return_status
!= FILE_RETRY
)
947 lp
= g_new (struct link
, 1);
948 mc_stat (dest_dir
, &buf
);
949 lp
->vfs
= vfs_get_class (dest_dir
);
950 lp
->ino
= buf
.st_ino
;
951 lp
->dev
= buf
.st_dev
;
952 lp
->next
= dest_dirs
;
955 if (ctx
->preserve_uidgid
) {
956 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
957 return_status
= file_error (
958 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
959 if (return_status
!= FILE_RETRY
)
965 /* open the source dir for reading */
966 reading
= mc_opendir (s
);
970 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
973 * Now, we don't want '.' and '..' to be created / copied at any time
975 if (!strcmp (next
->d_name
, "."))
977 if (!strcmp (next
->d_name
, ".."))
980 /* get the filename and add it to the src directory */
981 path
= concat_dir_and_file (s
, next
->d_name
);
983 (*ctx
->stat_func
) (path
, &buf
);
984 if (S_ISDIR (buf
.st_mode
)) {
987 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
989 * From here, we just intend to recursively copy subdirs, not
990 * the double functionality of copying different when the target
991 * dir already exists. So, we give the recursive call the flag 0
992 * meaning no toplevel.
994 return_status
= copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
999 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
1000 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
1003 if (do_delete
&& return_status
== FILE_CONT
) {
1004 if (ctx
->erase_at_end
) {
1005 static struct link
*tail
;
1006 size_t len
= strlen (path
);
1007 lp
= g_malloc (sizeof (struct link
) + len
);
1008 strncpy (lp
->name
, path
, len
+ 1);
1009 lp
->st_mode
= buf
.st_mode
;
1011 if (erase_list
!= NULL
) {
1015 erase_list
= tail
= lp
;
1017 if (S_ISDIR (buf
.st_mode
)) {
1018 return_status
= erase_dir_iff_empty (ctx
, path
);
1020 return_status
= erase_file (tctx
, ctx
, path
, FALSE
);
1025 mc_closedir (reading
);
1027 if (ctx
->preserve
) {
1028 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1029 utb
.actime
= cbuf
.st_atime
;
1030 utb
.modtime
= cbuf
.st_mtime
;
1031 mc_utime (dest_dir
, &utb
);
1033 cbuf
.st_mode
= umask(-1);
1034 umask(cbuf
.st_mode
);
1035 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
1036 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1041 g_free (parent_dirs
);
1044 return return_status
;
1049 /* {{{ Move routines */
1051 static FileProgressStatus
1052 move_file_file (FileOpTotalContext
*tctx
, FileOpContext
*ctx
, const char *s
, const char *d
)
1054 struct stat src_stats
, dst_stats
;
1055 FileProgressStatus return_status
= FILE_CONT
;
1056 gboolean copy_done
= FALSE
;
1058 file_progress_show_source (ctx
, s
);
1059 file_progress_show_target (ctx
, d
);
1060 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1065 while (mc_lstat (s
, &src_stats
) != 0) {
1066 /* Source doesn't exist */
1068 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1069 if (return_status
!= FILE_RETRY
)
1070 return return_status
;
1073 if (mc_lstat (d
, &dst_stats
) == 0) {
1074 if (src_stats
.st_dev
== dst_stats
.st_dev
1075 && src_stats
.st_ino
== dst_stats
.st_ino
)
1076 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "), s
, d
);
1078 if (S_ISDIR (dst_stats
.st_mode
)) {
1079 message (D_ERROR
, MSG_ERROR
,
1080 _(" Cannot overwrite directory `%s' "), d
);
1085 if (confirm_overwrite
) {
1086 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1087 if (return_status
!= FILE_CONT
)
1088 return return_status
;
1090 /* Ok to overwrite */
1093 if (!ctx
->do_append
) {
1094 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1095 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1096 goto retry_src_remove
;
1098 return return_status
;
1101 if (mc_rename (s
, d
) == 0) {
1102 return progress_update_one (tctx
, ctx
, src_stats
.st_size
, TRUE
);
1106 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1107 one nfs to the same, but on the server it is on two different
1108 filesystems. Then nfs returns EIO instead of EXDEV.
1109 Hope it will not hurt if we always in case of error try to copy/delete. */
1111 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1113 if (errno
!= EXDEV
) {
1115 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1117 if (return_status
== FILE_RETRY
)
1119 return return_status
;
1123 /* Failed because filesystem boundary -> copy the file instead */
1124 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
1125 if (return_status
!= FILE_CONT
)
1126 return return_status
;
1130 file_progress_show_source (ctx
, NULL
);
1131 file_progress_show (ctx
, 0, 0, "", FALSE
);
1133 return_status
= check_progress_buttons (ctx
);
1134 if (return_status
!= FILE_CONT
)
1135 return return_status
;
1140 if (mc_unlink (s
)) {
1142 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1143 if (return_status
== FILE_RETRY
)
1144 goto retry_src_remove
;
1145 return return_status
;
1149 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
, TRUE
);
1152 return return_status
;
1156 move_dir_dir (FileOpTotalContext
*tctx
, FileOpContext
*ctx
, const char *s
, const char *d
)
1158 struct stat sbuf
, dbuf
, destbuf
;
1161 FileProgressStatus return_status
;
1162 gboolean move_over
= FALSE
;
1165 file_progress_show_source (ctx
, s
);
1166 file_progress_show_target (ctx
, d
);
1167 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1173 dstat_ok
= (mc_stat (d
, &dbuf
) == 0);
1175 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
1176 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same directory "), s
, d
);
1179 destdir
= g_strdup (d
); /* destination doesn't exist */
1180 else if (!ctx
->dive_into_subdirs
) {
1181 destdir
= g_strdup (d
);
1184 destdir
= concat_dir_and_file (d
, x_basename (s
));
1186 /* Check if the user inputted an existing dir */
1188 if (!mc_stat (destdir
, &destbuf
)) {
1190 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
1192 if (return_status
!= FILE_CONT
)
1196 if (S_ISDIR (destbuf
.st_mode
))
1199 (" Cannot overwrite directory \"%s\" %s "),
1203 file_error (_(" Cannot overwrite file \"%s\" %s "),
1205 if (return_status
== FILE_RETRY
)
1206 goto retry_dst_stat
;
1209 return return_status
;
1213 if (mc_rename (s
, destdir
) == 0) {
1214 return_status
= FILE_CONT
;
1218 if (errno
!= EXDEV
) {
1221 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1223 if (return_status
== FILE_RETRY
)
1227 /* Failed because of filesystem boundary -> copy dir instead */
1229 copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
1231 if (return_status
!= FILE_CONT
)
1234 file_progress_show_source (ctx
, NULL
);
1235 file_progress_show (ctx
, 0, 0, "", FALSE
);
1237 return_status
= check_progress_buttons (ctx
);
1238 if (return_status
!= FILE_CONT
)
1242 if (ctx
->erase_at_end
) {
1243 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1244 if (S_ISDIR (erase_list
->st_mode
)) {
1246 erase_dir_iff_empty (ctx
, erase_list
->name
);
1249 erase_file (tctx
, ctx
, erase_list
->name
, FALSE
);
1251 erase_list
= erase_list
->next
;
1255 erase_dir_iff_empty (ctx
, s
);
1259 while (erase_list
) {
1261 erase_list
= erase_list
->next
;
1264 return return_status
;
1269 /* {{{ Erase routines */
1270 /* Don't update progress status if progress_count==NULL */
1271 static FileProgressStatus
1272 erase_file (FileOpTotalContext
*tctx
, FileOpContext
*ctx
, const char *s
, gboolean is_toplevel_file
)
1277 file_progress_show_deleting (ctx
, s
);
1278 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1282 if (tctx
->progress_count
&& mc_lstat (s
, &buf
)) {
1283 /* ignore, most likely the mc_unlink fails, too */
1287 while (mc_unlink (s
)) {
1289 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1290 if (return_status
!= FILE_RETRY
)
1291 return return_status
;
1294 if (tctx
->progress_count
)
1295 return progress_update_one (tctx
, ctx
, buf
.st_size
, is_toplevel_file
);
1300 static FileProgressStatus
1301 recursive_erase (FileOpTotalContext
*tctx
, FileOpContext
*ctx
, const char *s
)
1303 struct dirent
*next
;
1307 FileProgressStatus return_status
= FILE_CONT
;
1309 if (!strcmp (s
, ".."))
1312 reading
= mc_opendir (s
);
1317 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1318 if (!strcmp (next
->d_name
, "."))
1320 if (!strcmp (next
->d_name
, ".."))
1322 path
= concat_dir_and_file (s
, next
->d_name
);
1323 if (mc_lstat (path
, &buf
)) {
1325 mc_closedir (reading
);
1328 if (S_ISDIR (buf
.st_mode
))
1330 (recursive_erase (tctx
, ctx
, path
) != FILE_CONT
) ? FILE_RETRY
: FILE_CONT
;
1333 erase_file (tctx
, ctx
, path
, 0);
1336 mc_closedir (reading
);
1337 if (return_status
!= FILE_CONT
)
1338 return return_status
;
1339 file_progress_show_deleting (ctx
, s
);
1340 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1344 while (my_rmdir (s
)) {
1346 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1347 if (return_status
!= FILE_RETRY
)
1348 return return_status
;
1354 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1355 in the directory path points to, 0 else. */
1357 check_dir_is_empty (const char *path
)
1363 dir
= mc_opendir (path
);
1367 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1368 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1369 (d
->d_name
[1] == '.'
1370 && d
->d_name
[2] == '\0')))
1371 continue; /* "." or ".." */
1381 erase_dir (FileOpTotalContext
*tctx
, FileOpContext
*ctx
, const char *s
)
1383 FileProgressStatus error
;
1385 if (strcmp (s
, "..") == 0)
1388 if (strcmp (s
, ".") == 0)
1391 file_progress_show_deleting (ctx
, s
);
1392 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1396 /* The old way to detect a non empty directory was:
1397 error = my_rmdir (s);
1398 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1399 For the linux user space nfs server (nfs-server-2.2beta29-2)
1400 we would have to check also for EIO. I hope the new way is
1401 fool proof. (Norbert)
1403 error
= check_dir_is_empty (s
);
1404 if (error
== 0) { /* not empty */
1405 error
= query_recursive (ctx
, s
);
1406 if (error
== FILE_CONT
)
1407 return recursive_erase (tctx
, ctx
, s
);
1412 while (my_rmdir (s
) == -1) {
1414 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1415 if (error
!= FILE_RETRY
)
1422 static FileProgressStatus
1423 erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
)
1425 FileProgressStatus error
;
1427 if (strcmp (s
, "..") == 0)
1430 if (strcmp (s
, ".") == 0)
1433 file_progress_show_deleting (ctx
, s
);
1434 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1438 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1441 while (my_rmdir (s
)) {
1443 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1444 if (error
!= FILE_RETRY
)
1453 /* {{{ Panel operate routines */
1456 * Return currently selected entry name or the name of the first marked
1457 * entry if there is one.
1460 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1464 if (get_current_type () == view_tree
) {
1465 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1466 char *tree_name
= tree_selected_name (tree
);
1468 mc_stat (tree_name
, stat_buf
);
1472 if (panel
->marked
) {
1473 for (i
= 0; i
< panel
->count
; i
++)
1474 if (panel
->dir
.list
[i
].f
.marked
) {
1475 *stat_buf
= panel
->dir
.list
[i
].st
;
1476 return panel
->dir
.list
[i
].fname
;
1479 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1480 return panel
->dir
.list
[panel
->selected
].fname
;
1482 g_assert_not_reached ();
1488 compute_dir_size_create_ui (void)
1490 ComputeDirSizeUI
*ui
;
1492 const char *b_name
= N_("&Abort");
1498 ui
= g_new (ComputeDirSizeUI
, 1);
1500 ui
->dlg
= create_dlg (0, 0, 8, COLS
/2, dialog_colors
, NULL
,
1501 NULL
, _("Directory scanning"), DLG_CENTER
);
1502 ui
->dirname
= label_new (3, 3, "");
1503 add_widget (ui
->dlg
, ui
->dirname
);
1505 add_widget (ui
->dlg
,
1506 button_new (5, (ui
->dlg
->cols
- strlen (b_name
))/2,
1507 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
1509 /* We will manage the dialog without any help,
1510 that's why we have to call init_dlg */
1517 compute_dir_size_destroy_ui (ComputeDirSizeUI
*ui
)
1520 /* schedule to update passive panel */
1521 other_panel
->dirty
= 1;
1523 /* close and destroy dialog */
1524 dlg_run_done (ui
->dlg
);
1525 destroy_dlg (ui
->dlg
);
1531 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
1533 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
1540 label_set_text (this->dirname
, name_trunc (dirname
, this->dlg
->cols
- 6));
1542 event
.x
= -1; /* Don't show the GPM cursor */
1543 c
= tty_get_event (&event
, FALSE
, FALSE
);
1547 /* Reinitialize to avoid old values after events other than
1548 selecting a button */
1549 this->dlg
->ret_value
= FILE_CONT
;
1551 dlg_process_event (this->dlg
, c
, &event
);
1553 switch (this->dlg
->ret_value
) {
1565 * Computes the number of bytes used by the files in a directory
1568 compute_dir_size (const char *dirname
, const void *ui
,
1569 compute_dir_size_callback cback
,
1570 off_t
*ret_marked
, double *ret_total
)
1573 struct dirent
*dirent
;
1574 FileProgressStatus ret
= FILE_CONT
;
1576 dir
= mc_opendir (dirname
);
1581 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1586 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
1588 if (ret
!= FILE_CONT
)
1591 if (strcmp (dirent
->d_name
, ".") == 0)
1593 if (strcmp (dirent
->d_name
, "..") == 0)
1596 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1597 res
= mc_lstat (fullname
, &s
);
1604 if (S_ISDIR (s
.st_mode
)) {
1605 off_t subdir_count
= 0;
1606 double subdir_bytes
= 0;
1608 ret
= compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
);
1610 if (ret
!= FILE_CONT
) {
1615 *ret_marked
+= subdir_count
;
1616 *ret_total
+= subdir_bytes
;
1619 *ret_total
+= s
.st_size
;
1631 * panel_compute_totals:
1633 * compute the number of files and the number of bytes
1634 * used up by the whole selection, recursing directories
1635 * as required. In addition, it checks to see if it will
1636 * overwrite any files by doing the copy.
1638 static FileProgressStatus
1639 panel_compute_totals (const WPanel
*panel
, const void *ui
,
1640 compute_dir_size_callback cback
,
1641 off_t
*ret_marked
, double *ret_total
)
1648 for (i
= 0; i
< panel
->count
; i
++) {
1651 if (!panel
->dir
.list
[i
].f
.marked
)
1654 s
= &panel
->dir
.list
[i
].st
;
1656 if (S_ISDIR (s
->st_mode
)) {
1658 off_t subdir_count
= 0;
1659 double subdir_bytes
= 0;
1660 FileProgressStatus status
;
1663 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1665 status
= compute_dir_size (dir_name
, ui
, cback
,
1666 &subdir_count
, &subdir_bytes
);
1669 if (status
!= FILE_CONT
)
1672 *ret_marked
+= subdir_count
;
1673 *ret_total
+= subdir_bytes
;
1676 *ret_total
+= s
->st_size
;
1683 /* Initialize variables for progress bars */
1684 static FileProgressStatus
1685 panel_operate_init_totals (FileOperation operation
,
1686 const WPanel
*panel
, const char *source
,
1689 FileProgressStatus status
;
1691 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
1692 ComputeDirSizeUI
*ui
;
1694 ui
= compute_dir_size_create_ui ();
1697 status
= compute_dir_size (source
, ui
, compute_dir_size_update_ui
,
1698 &ctx
->progress_count
, &ctx
->progress_bytes
);
1700 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1701 &ctx
->progress_count
, &ctx
->progress_bytes
);
1703 compute_dir_size_destroy_ui (ui
);
1705 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1708 ctx
->progress_count
= panel
->marked
;
1709 ctx
->progress_bytes
= panel
->total
;
1710 ctx
->progress_totals_computed
= FALSE
;
1717 * This array introduced to avoid translation problems. The former (op_names)
1718 * is assumed to be nouns, suitable in dialog box titles; this one should
1719 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1720 * (I don't use spaces around the words, because someday they could be
1721 * dropped, when widgets get smarter)
1724 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
1725 static const char *op_names1
[] = {
1726 N_("FileOperation|Copy"),
1727 N_("FileOperation|Move"),
1728 N_("FileOperation|Delete")
1732 * These are formats for building a prompt. Parts encoded as follows:
1733 * %o - operation from op_names1
1734 * %f - file/files or files/directories, as appropriate
1735 * %m - "with source mask" or question mark for delete
1736 * %s - source name (truncated)
1737 * %d - number of marked files
1738 * %e - "to:" or question mark for delete
1740 * xgettext:no-c-format */
1741 static const char *one_format
= N_("%o %f \"%s\"%m");
1742 /* xgettext:no-c-format */
1743 static const char *many_format
= N_("%o %d %f%m");
1745 static const char *prompt_parts
[] = {
1750 N_("files/directories"),
1751 N_(" with source mask:"),
1755 static const char *question_format
= N_("%s?");
1758 * Generate user prompt for panel operation.
1759 * single_source is the name if the source entry or NULL for multiple
1761 * src_stat is only used when single_source is not NULL.
1764 panel_operate_generate_prompt (const WPanel
*panel
, FileOperation operation
,
1765 gboolean single_source
,
1766 const struct stat
*src_stat
)
1768 const char *sp
, *cp
;
1769 char format_string
[BUF_MEDIUM
];
1770 char *dp
= format_string
;
1771 gboolean build_question
= FALSE
;
1774 static gboolean i18n_flag
= FALSE
;
1778 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1779 op_names1
[i
] = Q_(op_names1
[i
]);
1781 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1782 prompt_parts
[i
] = _(prompt_parts
[i
]);
1784 one_format
= _(one_format
);
1785 many_format
= _(many_format
);
1786 question_format
= _(question_format
);
1789 #endif /* ENABLE_NLS */
1791 sp
= single_source
? one_format
: many_format
;
1793 while (*sp
!= '\0') {
1799 cp
= op_names1
[operation
];
1802 if (operation
== OP_DELETE
) {
1804 build_question
= TRUE
;
1806 cp
= prompt_parts
[5];
1809 if (operation
== OP_DELETE
) {
1811 build_question
= TRUE
;
1813 cp
= prompt_parts
[6];
1816 if (single_source
) {
1817 cp
= S_ISDIR (src_stat
->
1818 st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1820 cp
= (panel
->marked
== panel
->dirs_marked
)
1822 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1840 if (build_question
) {
1841 char tmp
[BUF_MEDIUM
];
1843 memmove (tmp
, format_string
, sizeof (tmp
));
1844 g_snprintf (format_string
, sizeof (format_string
),
1845 question_format
, tmp
);
1848 return g_strdup (format_string
);
1851 #ifdef WITH_BACKGROUND
1853 end_bg_process (FileOpContext
*ctx
, enum OperationMode mode
) {
1859 unregister_task_with_pid(pid
);
1860 // file_op_context_destroy(ctx);
1868 * Performs one of the operations on the selection on the source_panel
1869 * (copy, delete, move).
1871 * Returns TRUE if did change the directory
1872 * structure, Returns FALSE if user aborted
1874 * force_single forces operation on the current entry and affects
1875 * default destination. Current filename is used as default.
1878 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
1880 WPanel
*panel
= (WPanel
*) source_panel
;
1881 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
1882 || (get_current_type () == view_tree
);
1884 char *source
= NULL
;
1885 #ifdef WITH_FULL_PATHS
1886 char *source_with_path
= NULL
;
1888 # define source_with_path source
1889 #endif /* !WITH_FULL_PATHS */
1892 char *save_cwd
= NULL
, *save_dest
= NULL
;
1893 struct stat src_stat
;
1895 FileProgressStatus value
;
1897 FileOpTotalContext
*tctx
;
1899 gboolean do_bg
= FALSE
; /* do background operation? */
1902 static gboolean i18n_flag
= FALSE
;
1904 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1905 op_names
[i
] = Q_(op_names
[i
]);
1908 #endif /* ENABLE_NLS */
1910 free_linklist (&linklist
);
1911 free_linklist (&dest_dirs
);
1913 /* Update panel contents to avoid actions on deleted files */
1914 if (!panel
->is_panelized
) {
1915 update_panels (UP_RELOAD
, UP_KEEPSEL
);
1921 source
= selection (panel
)->fname
;
1922 src_stat
= selection (panel
)->st
;
1924 source
= panel_get_file (panel
, &src_stat
);
1927 if (!strcmp (source
, "..")) {
1928 message (D_ERROR
, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1933 ctx
= file_op_context_new (operation
);
1934 tctx
= file_op_total_context_new ();
1935 gettimeofday (&(tctx
->transfer_start
), (struct timezone
*) NULL
);
1937 /* Show confirmation dialog */
1938 if (operation
!= OP_DELETE
) {
1943 /* Forced single operations default to the original name */
1946 else if (get_other_type () == view_listing
)
1947 dest_dir
= other_panel
->cwd
;
1949 dest_dir
= panel
->cwd
;
1951 * Add trailing backslash only when do non-local ops.
1952 * It saves user from occasional file renames (when destination
1956 && dest_dir
[0] != '\0'
1957 && dest_dir
[strlen (dest_dir
) - 1] != PATH_SEP
) {
1958 /* add trailing separator */
1959 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char *) NULL
);
1962 dest_dir_
= g_strdup (dest_dir
);
1964 if (dest_dir_
== NULL
) {
1965 file_op_total_context_destroy (tctx
);
1966 file_op_context_destroy (ctx
);
1970 /* Generate confirmation prompt */
1971 format
= panel_operate_generate_prompt (panel
, operation
,
1972 source
!= NULL
, &src_stat
);
1974 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
1975 source
!= NULL
? (void *) source
1976 : (void *) &panel
->marked
,
1982 if (dest
== NULL
|| dest
[0] == '\0') {
1983 file_op_total_context_destroy (tctx
);
1984 file_op_context_destroy (ctx
);
1988 } else if (confirm_delete
) {
1990 char fmd_buf
[BUF_MEDIUM
];
1992 /* Generate confirmation prompt */
1993 format
= panel_operate_generate_prompt (panel
, OP_DELETE
,
1994 source
!= NULL
, &src_stat
);
1997 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
1999 const int fmd_xlen
= 64;
2000 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2001 g_snprintf (fmd_buf
, sizeof (fmd_buf
),
2002 format
, str_trunc (source
, i
));
2010 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2,
2011 _("&Yes"), _("&No"));
2014 file_op_total_context_destroy (tctx
);
2015 file_op_context_destroy (ctx
);
2021 filegui_dialog_type_t dialog_type
;
2023 if (operation
== OP_DELETE
) {
2024 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2026 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2027 ? FILEGUI_DIALOG_MULTI_ITEM
2028 : FILEGUI_DIALOG_ONE_ITEM
;
2030 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2031 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2034 /* Background also need ctx->ui, but not full */
2036 file_op_context_create_ui_without_init (ctx
, 1, dialog_type
);
2038 file_op_context_create_ui (ctx
, 1, dialog_type
);
2041 #ifdef WITH_BACKGROUND
2042 /* Did the user select to do a background operation? */
2046 v
= do_background (ctx
,
2047 g_strconcat (op_names
[operation
], ": ",
2048 panel
->cwd
, (char *) NULL
));
2050 message (D_ERROR
, MSG_ERROR
,
2051 _(" Sorry, I could not put the job in background "));
2054 /* If we are the parent */
2056 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
2057 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
2058 /* file_op_context_destroy (ctx); */
2062 #endif /* WITH_BACKGROUND */
2064 /* Initialize things */
2065 /* We do not want to trash cache every time file is
2066 created/touched. However, this will make our cache contain
2069 && (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2070 save_dest
= g_strdup (dest
);
2072 if ((panel
->cwd
[0] != '\0')
2073 && (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2074 save_cwd
= g_strdup (panel
->cwd
);
2076 /* Now, let's do the job */
2078 /* This code is only called by the tree and panel code */
2080 /* We now have ETA in all cases */
2082 /* One file: FIXME mc_chdir will take user out of any vfs */
2083 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
2084 mc_chdir (PATH_SEP_STR
);
2086 /* The source and src_stat variables have been initialized before */
2087 #ifdef WITH_FULL_PATHS
2088 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2089 #endif /* WITH_FULL_PATHS */
2091 if (panel_operate_init_totals (operation
, panel
,
2092 source_with_path
, ctx
) == FILE_CONT
) {
2093 if (operation
== OP_DELETE
) {
2094 if (S_ISDIR (src_stat
.st_mode
))
2095 value
= erase_dir (tctx
, ctx
, source_with_path
);
2097 value
= erase_file (tctx
, ctx
, source_with_path
, 1);
2099 temp
= transform_source (ctx
, source_with_path
);
2101 value
= transform_error
;
2103 char *repl_dest
, *temp2
;
2105 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2106 temp2
= concat_dir_and_file (repl_dest
, temp
);
2112 switch (operation
) {
2114 /* we use file_mask_op_follow_links only with OP_COPY */
2115 ctx
->stat_func (source_with_path
, &src_stat
);
2117 if (S_ISDIR (src_stat
.st_mode
))
2118 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, dest
,
2119 TRUE
, FALSE
, FALSE
, NULL
);
2121 value
= copy_file_file (tctx
, ctx
, source_with_path
, dest
);
2125 if (S_ISDIR (src_stat
.st_mode
))
2126 value
= move_dir_dir (tctx
, ctx
, source_with_path
, dest
);
2128 value
= move_file_file (tctx
, ctx
, source_with_path
, dest
);
2132 /* Unknown file operation */
2136 } /* Copy or move operation */
2138 if ((value
== FILE_CONT
) && !force_single
)
2139 unmark_files (panel
);
2144 /* Check destination for copy or move operation */
2145 while (operation
!= OP_DELETE
) {
2147 struct stat dst_stat
;
2149 dst_result
= mc_stat (dest
, &dst_stat
);
2151 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2154 if (file_error (_(" Destination \"%s\" must be a directory \n %s "),
2155 dest
) != FILE_RETRY
)
2159 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
) {
2160 /* Loop for every file, perform the actual copy operation */
2161 for (i
= 0; i
< panel
->count
; i
++) {
2162 if (!panel
->dir
.list
[i
].f
.marked
)
2163 continue; /* Skip the unmarked ones */
2165 source
= panel
->dir
.list
[i
].fname
;
2166 src_stat
= panel
->dir
.list
[i
].st
;
2168 #ifdef WITH_FULL_PATHS
2169 g_free (source_with_path
);
2170 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2171 #endif /* WITH_FULL_PATHS */
2173 if (operation
== OP_DELETE
) {
2174 if (S_ISDIR (src_stat
.st_mode
))
2175 value
= erase_dir (tctx
, ctx
, source_with_path
);
2177 value
= erase_file (tctx
, ctx
, source_with_path
, 1);
2179 temp
= transform_source (ctx
, source_with_path
);
2182 value
= transform_error
;
2184 char *temp2
, *temp3
, *repl_dest
;
2186 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2187 temp2
= concat_dir_and_file (repl_dest
, temp
);
2190 temp3
= source_with_path
;
2191 source_with_path
= strutils_shell_unescape (source_with_path
);
2194 temp2
= strutils_shell_unescape (temp2
);
2197 switch (operation
) {
2199 /* we use file_mask_op_follow_links only with OP_COPY */
2200 ctx
->stat_func (source_with_path
, &src_stat
);
2201 if (S_ISDIR (src_stat
.st_mode
))
2202 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, temp2
,
2203 TRUE
, FALSE
, FALSE
, NULL
);
2205 value
= copy_file_file (tctx
, ctx
, source_with_path
, temp2
);
2206 free_linklist (&dest_dirs
);
2210 if (S_ISDIR (src_stat
.st_mode
))
2211 value
= move_dir_dir (tctx
, ctx
, source_with_path
, temp2
);
2213 value
= move_file_file (tctx
, ctx
, source_with_path
, temp2
);
2217 /* Unknown file operation */
2223 } /* Copy or move operation */
2225 if (value
== FILE_ABORT
)
2228 if (value
== FILE_CONT
)
2229 do_file_mark (panel
, i
, 0);
2231 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2234 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
2236 if (operation
!= OP_DELETE
)
2237 file_progress_show (ctx
, 0, 0, "", FALSE
);
2240 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2244 } /* Loop for every file */
2246 } /* Many entries */
2250 if (save_cwd
!= NULL
) {
2251 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2255 if (save_dest
!= NULL
) {
2256 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2260 free_linklist (&linklist
);
2261 free_linklist (&dest_dirs
);
2262 #ifdef WITH_FULL_PATHS
2263 g_free (source_with_path
);
2264 #endif /* WITH_FULL_PATHS */
2266 g_free (ctx
->dest_mask
);
2267 ctx
->dest_mask
= NULL
;
2269 #ifdef WITH_BACKGROUND
2270 /* Let our parent know we are saying bye bye */
2271 if (we_are_background
) {
2272 int cur_pid
= getpid();
2273 /* Send pid to parent with child context, it is fork and
2274 don't modify real parent ctx */
2276 parent_call ((void *) end_bg_process
, ctx
, 0);
2281 #endif /* WITH_BACKGROUND */
2283 file_op_context_destroy (ctx
);
2289 /* {{{ Query/status report routines */
2291 static FileProgressStatus
2292 real_do_file_error (enum OperationMode mode
, const char *error
)
2297 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2299 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2317 /* Report error with one file */
2319 file_error (const char *format
, const char *file
)
2321 char buf
[BUF_MEDIUM
];
2323 g_snprintf (buf
, sizeof (buf
), format
,
2324 path_trunc (file
, 30), unix_error_string (errno
));
2326 return do_file_error (buf
);
2329 /* Report error with two files */
2330 static FileProgressStatus
2331 files_error (const char *format
, const char *file1
, const char *file2
)
2333 char buf
[BUF_MEDIUM
];
2334 char *nfile1
= g_strdup (path_trunc (file1
, 15));
2335 char *nfile2
= g_strdup (path_trunc (file2
, 15));
2337 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
,
2338 unix_error_string (errno
));
2343 return do_file_error (buf
);
2346 static FileProgressStatus
2347 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, const char *s
)
2351 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2352 const char *msg
= mode
== Foreground
2353 ? _("\n Directory not empty. \n"
2354 " Delete it recursively? ")
2355 : _("\n Background process: Directory not empty \n"
2356 " Delete it recursively? ");
2357 text
= g_strconcat (_(" Delete: "), path_trunc (s
, 30), " ", (char *) NULL
);
2362 ctx
->recursive_result
=
2363 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
2364 _("&Yes"), _("&No"),
2365 _("A&ll"), _("Non&e"),
2368 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2373 switch (ctx
->recursive_result
) {
2375 case RECURSIVE_ALWAYS
:
2379 case RECURSIVE_NEVER
:
2382 case RECURSIVE_ABORT
:
2388 #ifdef WITH_BACKGROUND
2389 static FileProgressStatus
2390 do_file_error (const char *str
)
2394 FileProgressStatus (*f
) (enum OperationMode
, const char *);
2396 pntr
.f
= real_do_file_error
;
2398 if (we_are_background
)
2399 return parent_call (pntr
.p
, NULL
, 1, strlen (str
),
2402 return real_do_file_error (Foreground
, str
);
2405 static FileProgressStatus
2406 query_recursive (FileOpContext
*ctx
, const char *s
)
2410 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
2412 pntr
.f
= real_query_recursive
;
2414 if (we_are_background
)
2415 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
2417 return real_query_recursive (ctx
, Foreground
, s
);
2420 static FileProgressStatus
2421 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2422 struct stat
*_d_stat
)
2426 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
2427 struct stat
*, struct stat
*);
2429 pntr
.f
= file_progress_real_query_replace
;
2431 if (we_are_background
)
2432 return parent_call (pntr
.p
,
2435 strlen (destname
), destname
,
2436 sizeof (struct stat
), _s_stat
,
2437 sizeof (struct stat
), _d_stat
);
2439 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2444 static FileProgressStatus
2445 do_file_error (const char *str
)
2447 return real_do_file_error (Foreground
, str
);
2450 static FileProgressStatus
2451 query_recursive (FileOpContext
*ctx
, const char *s
)
2453 return real_query_recursive (ctx
, Foreground
, s
);
2456 static FileProgressStatus
2457 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2458 struct stat
*_d_stat
)
2460 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2464 #endif /* !WITH_BACKGROUND */
2467 Cause emacs to enter folding mode for this file: