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
;
1057 gboolean old_ask_overwrite
;
1059 file_progress_show_source (ctx
, s
);
1060 file_progress_show_target (ctx
, d
);
1061 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1066 while (mc_lstat (s
, &src_stats
) != 0) {
1067 /* Source doesn't exist */
1069 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1070 if (return_status
!= FILE_RETRY
)
1071 return return_status
;
1074 if (mc_lstat (d
, &dst_stats
) == 0) {
1075 if (src_stats
.st_dev
== dst_stats
.st_dev
1076 && src_stats
.st_ino
== dst_stats
.st_ino
)
1077 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "), s
, d
);
1079 if (S_ISDIR (dst_stats
.st_mode
)) {
1080 message (D_ERROR
, MSG_ERROR
,
1081 _(" Cannot overwrite directory `%s' "), d
);
1086 if (confirm_overwrite
) {
1087 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1088 if (return_status
!= FILE_CONT
)
1089 return return_status
;
1091 /* Ok to overwrite */
1094 if (!ctx
->do_append
) {
1095 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1096 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1097 goto retry_src_remove
;
1099 return return_status
;
1102 if (mc_rename (s
, d
) == 0) {
1103 return progress_update_one (tctx
, ctx
, src_stats
.st_size
, TRUE
);
1107 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1108 one nfs to the same, but on the server it is on two different
1109 filesystems. Then nfs returns EIO instead of EXDEV.
1110 Hope it will not hurt if we always in case of error try to copy/delete. */
1112 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1114 if (errno
!= EXDEV
) {
1116 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1118 if (return_status
== FILE_RETRY
)
1120 return return_status
;
1124 /* Failed because filesystem boundary -> copy the file instead */
1125 old_ask_overwrite
= tctx
->ask_overwrite
;
1126 tctx
->ask_overwrite
= FALSE
;
1127 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
1128 tctx
->ask_overwrite
= old_ask_overwrite
;
1129 if (return_status
!= FILE_CONT
)
1130 return return_status
;
1134 file_progress_show_source (ctx
, NULL
);
1135 file_progress_show (ctx
, 0, 0, "", FALSE
);
1137 return_status
= check_progress_buttons (ctx
);
1138 if (return_status
!= FILE_CONT
)
1139 return return_status
;
1144 if (mc_unlink (s
)) {
1146 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1147 if (return_status
== FILE_RETRY
)
1148 goto retry_src_remove
;
1149 return return_status
;
1153 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
, TRUE
);
1156 return return_status
;
1160 move_dir_dir (FileOpTotalContext
*tctx
, FileOpContext
*ctx
, const char *s
, const char *d
)
1162 struct stat sbuf
, dbuf
, destbuf
;
1165 FileProgressStatus return_status
;
1166 gboolean move_over
= FALSE
;
1169 file_progress_show_source (ctx
, s
);
1170 file_progress_show_target (ctx
, d
);
1171 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1177 dstat_ok
= (mc_stat (d
, &dbuf
) == 0);
1179 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
1180 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same directory "), s
, d
);
1183 destdir
= g_strdup (d
); /* destination doesn't exist */
1184 else if (!ctx
->dive_into_subdirs
) {
1185 destdir
= g_strdup (d
);
1188 destdir
= concat_dir_and_file (d
, x_basename (s
));
1190 /* Check if the user inputted an existing dir */
1192 if (!mc_stat (destdir
, &destbuf
)) {
1194 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
1196 if (return_status
!= FILE_CONT
)
1200 if (S_ISDIR (destbuf
.st_mode
))
1203 (" Cannot overwrite directory \"%s\" %s "),
1207 file_error (_(" Cannot overwrite file \"%s\" %s "),
1209 if (return_status
== FILE_RETRY
)
1210 goto retry_dst_stat
;
1213 return return_status
;
1217 if (mc_rename (s
, destdir
) == 0) {
1218 return_status
= FILE_CONT
;
1222 if (errno
!= EXDEV
) {
1225 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1227 if (return_status
== FILE_RETRY
)
1231 /* Failed because of filesystem boundary -> copy dir instead */
1233 copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
1235 if (return_status
!= FILE_CONT
)
1238 file_progress_show_source (ctx
, NULL
);
1239 file_progress_show (ctx
, 0, 0, "", FALSE
);
1241 return_status
= check_progress_buttons (ctx
);
1242 if (return_status
!= FILE_CONT
)
1246 if (ctx
->erase_at_end
) {
1247 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1248 if (S_ISDIR (erase_list
->st_mode
)) {
1250 erase_dir_iff_empty (ctx
, erase_list
->name
);
1253 erase_file (tctx
, ctx
, erase_list
->name
, FALSE
);
1255 erase_list
= erase_list
->next
;
1259 erase_dir_iff_empty (ctx
, s
);
1263 while (erase_list
) {
1265 erase_list
= erase_list
->next
;
1268 return return_status
;
1273 /* {{{ Erase routines */
1274 /* Don't update progress status if progress_count==NULL */
1275 static FileProgressStatus
1276 erase_file (FileOpTotalContext
*tctx
, FileOpContext
*ctx
, const char *s
, gboolean is_toplevel_file
)
1281 file_progress_show_deleting (ctx
, s
);
1282 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1286 if (tctx
->progress_count
&& mc_lstat (s
, &buf
)) {
1287 /* ignore, most likely the mc_unlink fails, too */
1291 while (mc_unlink (s
)) {
1293 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1294 if (return_status
!= FILE_RETRY
)
1295 return return_status
;
1298 if (tctx
->progress_count
)
1299 return progress_update_one (tctx
, ctx
, buf
.st_size
, is_toplevel_file
);
1304 static FileProgressStatus
1305 recursive_erase (FileOpTotalContext
*tctx
, FileOpContext
*ctx
, const char *s
)
1307 struct dirent
*next
;
1311 FileProgressStatus return_status
= FILE_CONT
;
1313 if (!strcmp (s
, ".."))
1316 reading
= mc_opendir (s
);
1321 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1322 if (!strcmp (next
->d_name
, "."))
1324 if (!strcmp (next
->d_name
, ".."))
1326 path
= concat_dir_and_file (s
, next
->d_name
);
1327 if (mc_lstat (path
, &buf
)) {
1329 mc_closedir (reading
);
1332 if (S_ISDIR (buf
.st_mode
))
1334 (recursive_erase (tctx
, ctx
, path
) != FILE_CONT
) ? FILE_RETRY
: FILE_CONT
;
1337 erase_file (tctx
, ctx
, path
, 0);
1340 mc_closedir (reading
);
1341 if (return_status
!= FILE_CONT
)
1342 return return_status
;
1343 file_progress_show_deleting (ctx
, s
);
1344 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1348 while (my_rmdir (s
)) {
1350 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1351 if (return_status
!= FILE_RETRY
)
1352 return return_status
;
1358 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1359 in the directory path points to, 0 else. */
1361 check_dir_is_empty (const char *path
)
1367 dir
= mc_opendir (path
);
1371 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1372 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1373 (d
->d_name
[1] == '.'
1374 && d
->d_name
[2] == '\0')))
1375 continue; /* "." or ".." */
1385 erase_dir (FileOpTotalContext
*tctx
, FileOpContext
*ctx
, const char *s
)
1387 FileProgressStatus error
;
1389 if (strcmp (s
, "..") == 0)
1392 if (strcmp (s
, ".") == 0)
1395 file_progress_show_deleting (ctx
, s
);
1396 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1400 /* The old way to detect a non empty directory was:
1401 error = my_rmdir (s);
1402 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1403 For the linux user space nfs server (nfs-server-2.2beta29-2)
1404 we would have to check also for EIO. I hope the new way is
1405 fool proof. (Norbert)
1407 error
= check_dir_is_empty (s
);
1408 if (error
== 0) { /* not empty */
1409 error
= query_recursive (ctx
, s
);
1410 if (error
== FILE_CONT
)
1411 return recursive_erase (tctx
, ctx
, s
);
1416 while (my_rmdir (s
) == -1) {
1418 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1419 if (error
!= FILE_RETRY
)
1426 static FileProgressStatus
1427 erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
)
1429 FileProgressStatus error
;
1431 if (strcmp (s
, "..") == 0)
1434 if (strcmp (s
, ".") == 0)
1437 file_progress_show_deleting (ctx
, s
);
1438 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1442 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1445 while (my_rmdir (s
)) {
1447 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1448 if (error
!= FILE_RETRY
)
1457 /* {{{ Panel operate routines */
1460 * Return currently selected entry name or the name of the first marked
1461 * entry if there is one.
1464 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1468 if (get_current_type () == view_tree
) {
1469 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1470 char *tree_name
= tree_selected_name (tree
);
1472 mc_stat (tree_name
, stat_buf
);
1476 if (panel
->marked
) {
1477 for (i
= 0; i
< panel
->count
; i
++)
1478 if (panel
->dir
.list
[i
].f
.marked
) {
1479 *stat_buf
= panel
->dir
.list
[i
].st
;
1480 return panel
->dir
.list
[i
].fname
;
1483 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1484 return panel
->dir
.list
[panel
->selected
].fname
;
1486 g_assert_not_reached ();
1492 compute_dir_size_create_ui (void)
1494 ComputeDirSizeUI
*ui
;
1496 const char *b_name
= N_("&Abort");
1502 ui
= g_new (ComputeDirSizeUI
, 1);
1504 ui
->dlg
= create_dlg (0, 0, 8, COLS
/2, dialog_colors
, NULL
,
1505 NULL
, _("Directory scanning"), DLG_CENTER
);
1506 ui
->dirname
= label_new (3, 3, "");
1507 add_widget (ui
->dlg
, ui
->dirname
);
1509 add_widget (ui
->dlg
,
1510 button_new (5, (ui
->dlg
->cols
- strlen (b_name
))/2,
1511 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
1513 /* We will manage the dialog without any help,
1514 that's why we have to call init_dlg */
1521 compute_dir_size_destroy_ui (ComputeDirSizeUI
*ui
)
1524 /* schedule to update passive panel */
1525 other_panel
->dirty
= 1;
1527 /* close and destroy dialog */
1528 dlg_run_done (ui
->dlg
);
1529 destroy_dlg (ui
->dlg
);
1535 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
1537 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
1544 label_set_text (this->dirname
, name_trunc (dirname
, this->dlg
->cols
- 6));
1546 event
.x
= -1; /* Don't show the GPM cursor */
1547 c
= tty_get_event (&event
, FALSE
, FALSE
);
1551 /* Reinitialize to avoid old values after events other than
1552 selecting a button */
1553 this->dlg
->ret_value
= FILE_CONT
;
1555 dlg_process_event (this->dlg
, c
, &event
);
1557 switch (this->dlg
->ret_value
) {
1569 * Computes the number of bytes used by the files in a directory
1572 compute_dir_size (const char *dirname
, const void *ui
,
1573 compute_dir_size_callback cback
,
1574 off_t
*ret_marked
, double *ret_total
,
1575 gboolean compute_symlinks
)
1580 struct dirent
*dirent
;
1581 FileProgressStatus ret
= FILE_CONT
;
1583 if (!compute_symlinks
)
1585 res
= mc_lstat (dirname
, &s
);
1589 /* don't scan symlink to directory */
1590 if (S_ISLNK (s
.st_mode
)) {
1592 *ret_total
+= s
.st_size
;
1597 dir
= mc_opendir (dirname
);
1602 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1605 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
1607 if (ret
!= FILE_CONT
)
1610 if (strcmp (dirent
->d_name
, ".") == 0)
1612 if (strcmp (dirent
->d_name
, "..") == 0)
1615 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1616 res
= mc_lstat (fullname
, &s
);
1623 if (S_ISDIR (s
.st_mode
)) {
1624 off_t subdir_count
= 0;
1625 double subdir_bytes
= 0;
1627 ret
= compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
, compute_symlinks
);
1629 if (ret
!= FILE_CONT
) {
1634 *ret_marked
+= subdir_count
;
1635 *ret_total
+= subdir_bytes
;
1638 *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.
1657 static FileProgressStatus
1658 panel_compute_totals (const WPanel
*panel
, const void *ui
,
1659 compute_dir_size_callback cback
,
1660 off_t
*ret_marked
, double *ret_total
, gboolean compute_symlinks
)
1667 for (i
= 0; i
< panel
->count
; i
++) {
1670 if (!panel
->dir
.list
[i
].f
.marked
)
1673 s
= &panel
->dir
.list
[i
].st
;
1675 if (S_ISDIR (s
->st_mode
)) {
1677 off_t subdir_count
= 0;
1678 double subdir_bytes
= 0;
1679 FileProgressStatus status
;
1682 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1684 status
= compute_dir_size (dir_name
, ui
, cback
,
1685 &subdir_count
, &subdir_bytes
, compute_symlinks
);
1688 if (status
!= FILE_CONT
)
1691 *ret_marked
+= subdir_count
;
1692 *ret_total
+= subdir_bytes
;
1695 *ret_total
+= s
->st_size
;
1702 /* Initialize variables for progress bars */
1703 static FileProgressStatus
1704 panel_operate_init_totals (FileOperation operation
,
1705 const WPanel
*panel
, const char *source
,
1708 FileProgressStatus status
;
1710 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
1711 ComputeDirSizeUI
*ui
;
1713 ui
= compute_dir_size_create_ui ();
1716 status
= compute_dir_size (source
, ui
, compute_dir_size_update_ui
,
1717 &ctx
->progress_count
, &ctx
->progress_bytes
, ctx
->follow_links
);
1719 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1720 &ctx
->progress_count
, &ctx
->progress_bytes
, ctx
->follow_links
);
1722 compute_dir_size_destroy_ui (ui
);
1724 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1727 ctx
->progress_count
= panel
->marked
;
1728 ctx
->progress_bytes
= panel
->total
;
1729 ctx
->progress_totals_computed
= FALSE
;
1736 * This array introduced to avoid translation problems. The former (op_names)
1737 * is assumed to be nouns, suitable in dialog box titles; this one should
1738 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1739 * (I don't use spaces around the words, because someday they could be
1740 * dropped, when widgets get smarter)
1743 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
1744 static const char *op_names1
[] = {
1745 N_("FileOperation|Copy"),
1746 N_("FileOperation|Move"),
1747 N_("FileOperation|Delete")
1751 * These are formats for building a prompt. Parts encoded as follows:
1752 * %o - operation from op_names1
1753 * %f - file/files or files/directories, as appropriate
1754 * %m - "with source mask" or question mark for delete
1755 * %s - source name (truncated)
1756 * %d - number of marked files
1757 * %e - "to:" or question mark for delete
1759 * xgettext:no-c-format */
1760 static const char *one_format
= N_("%o %f \"%s\"%m");
1761 /* xgettext:no-c-format */
1762 static const char *many_format
= N_("%o %d %f%m");
1764 static const char *prompt_parts
[] = {
1769 N_("files/directories"),
1770 N_(" with source mask:"),
1774 static const char *question_format
= N_("%s?");
1777 * Generate user prompt for panel operation.
1778 * single_source is the name if the source entry or NULL for multiple
1780 * src_stat is only used when single_source is not NULL.
1783 panel_operate_generate_prompt (const WPanel
*panel
, FileOperation operation
,
1784 gboolean single_source
,
1785 const struct stat
*src_stat
)
1787 const char *sp
, *cp
;
1788 char format_string
[BUF_MEDIUM
];
1789 char *dp
= format_string
;
1790 gboolean build_question
= FALSE
;
1793 static gboolean i18n_flag
= FALSE
;
1797 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1798 op_names1
[i
] = Q_(op_names1
[i
]);
1800 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1801 prompt_parts
[i
] = _(prompt_parts
[i
]);
1803 one_format
= _(one_format
);
1804 many_format
= _(many_format
);
1805 question_format
= _(question_format
);
1808 #endif /* ENABLE_NLS */
1810 sp
= single_source
? one_format
: many_format
;
1812 while (*sp
!= '\0') {
1818 cp
= op_names1
[operation
];
1821 if (operation
== OP_DELETE
) {
1823 build_question
= TRUE
;
1825 cp
= prompt_parts
[5];
1828 if (operation
== OP_DELETE
) {
1830 build_question
= TRUE
;
1832 cp
= prompt_parts
[6];
1835 if (single_source
) {
1836 cp
= S_ISDIR (src_stat
->
1837 st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1839 cp
= (panel
->marked
== panel
->dirs_marked
)
1841 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1859 if (build_question
) {
1860 char tmp
[BUF_MEDIUM
];
1862 memmove (tmp
, format_string
, sizeof (tmp
));
1863 g_snprintf (format_string
, sizeof (format_string
),
1864 question_format
, tmp
);
1867 return g_strdup (format_string
);
1870 #ifdef WITH_BACKGROUND
1872 end_bg_process (FileOpContext
*ctx
, enum OperationMode mode
) {
1878 unregister_task_with_pid(pid
);
1879 // file_op_context_destroy(ctx);
1887 * Performs one of the operations on the selection on the source_panel
1888 * (copy, delete, move).
1890 * Returns TRUE if did change the directory
1891 * structure, Returns FALSE if user aborted
1893 * force_single forces operation on the current entry and affects
1894 * default destination. Current filename is used as default.
1897 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
1899 WPanel
*panel
= (WPanel
*) source_panel
;
1900 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
1901 || (get_current_type () == view_tree
);
1903 char *source
= NULL
;
1904 #ifdef WITH_FULL_PATHS
1905 char *source_with_path
= NULL
;
1907 # define source_with_path source
1908 #endif /* !WITH_FULL_PATHS */
1911 char *save_cwd
= NULL
, *save_dest
= NULL
;
1912 struct stat src_stat
;
1914 FileProgressStatus value
;
1916 FileOpTotalContext
*tctx
;
1918 gboolean do_bg
= FALSE
; /* do background operation? */
1921 static gboolean i18n_flag
= FALSE
;
1923 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1924 op_names
[i
] = Q_(op_names
[i
]);
1927 #endif /* ENABLE_NLS */
1929 free_linklist (&linklist
);
1930 free_linklist (&dest_dirs
);
1932 /* Update panel contents to avoid actions on deleted files */
1933 if (!panel
->is_panelized
) {
1934 update_panels (UP_RELOAD
, UP_KEEPSEL
);
1940 source
= selection (panel
)->fname
;
1941 src_stat
= selection (panel
)->st
;
1943 source
= panel_get_file (panel
, &src_stat
);
1946 if (!strcmp (source
, "..")) {
1947 message (D_ERROR
, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1952 ctx
= file_op_context_new (operation
);
1953 tctx
= file_op_total_context_new ();
1954 gettimeofday (&(tctx
->transfer_start
), (struct timezone
*) NULL
);
1956 /* Show confirmation dialog */
1957 if (operation
!= OP_DELETE
) {
1962 /* Forced single operations default to the original name */
1965 else if (get_other_type () == view_listing
)
1966 dest_dir
= other_panel
->cwd
;
1968 dest_dir
= panel
->cwd
;
1970 * Add trailing backslash only when do non-local ops.
1971 * It saves user from occasional file renames (when destination
1975 && dest_dir
[0] != '\0'
1976 && dest_dir
[strlen (dest_dir
) - 1] != PATH_SEP
) {
1977 /* add trailing separator */
1978 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char *) NULL
);
1981 dest_dir_
= g_strdup (dest_dir
);
1983 if (dest_dir_
== NULL
) {
1984 file_op_total_context_destroy (tctx
);
1985 file_op_context_destroy (ctx
);
1989 /* Generate confirmation prompt */
1990 format
= panel_operate_generate_prompt (panel
, operation
,
1991 source
!= NULL
, &src_stat
);
1993 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
1994 source
!= NULL
? (void *) source
1995 : (void *) &panel
->marked
,
2001 if (dest
== NULL
|| dest
[0] == '\0') {
2002 file_op_total_context_destroy (tctx
);
2003 file_op_context_destroy (ctx
);
2007 } else if (confirm_delete
) {
2009 char fmd_buf
[BUF_MEDIUM
];
2011 /* Generate confirmation prompt */
2012 format
= panel_operate_generate_prompt (panel
, OP_DELETE
,
2013 source
!= NULL
, &src_stat
);
2016 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2018 const int fmd_xlen
= 64;
2019 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2020 g_snprintf (fmd_buf
, sizeof (fmd_buf
),
2021 format
, str_trunc (source
, i
));
2029 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2,
2030 _("&Yes"), _("&No"));
2033 file_op_total_context_destroy (tctx
);
2034 file_op_context_destroy (ctx
);
2040 filegui_dialog_type_t dialog_type
;
2042 if (operation
== OP_DELETE
) {
2043 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2045 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2046 ? FILEGUI_DIALOG_MULTI_ITEM
2047 : FILEGUI_DIALOG_ONE_ITEM
;
2049 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2050 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2053 /* Background also need ctx->ui, but not full */
2055 file_op_context_create_ui_without_init (ctx
, 1, dialog_type
);
2057 file_op_context_create_ui (ctx
, 1, dialog_type
);
2060 #ifdef WITH_BACKGROUND
2061 /* Did the user select to do a background operation? */
2065 v
= do_background (ctx
,
2066 g_strconcat (op_names
[operation
], ": ",
2067 panel
->cwd
, (char *) NULL
));
2069 message (D_ERROR
, MSG_ERROR
,
2070 _(" Sorry, I could not put the job in background "));
2073 /* If we are the parent */
2075 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
2076 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
2077 /* file_op_context_destroy (ctx); */
2081 #endif /* WITH_BACKGROUND */
2083 /* Initialize things */
2084 /* We do not want to trash cache every time file is
2085 created/touched. However, this will make our cache contain
2088 && (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2089 save_dest
= g_strdup (dest
);
2091 if ((panel
->cwd
[0] != '\0')
2092 && (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2093 save_cwd
= g_strdup (panel
->cwd
);
2095 /* Now, let's do the job */
2097 /* This code is only called by the tree and panel code */
2099 /* We now have ETA in all cases */
2101 /* One file: FIXME mc_chdir will take user out of any vfs */
2102 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
2103 mc_chdir (PATH_SEP_STR
);
2105 /* The source and src_stat variables have been initialized before */
2106 #ifdef WITH_FULL_PATHS
2107 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2108 #endif /* WITH_FULL_PATHS */
2110 if (panel_operate_init_totals (operation
, panel
,
2111 source_with_path
, ctx
) == FILE_CONT
) {
2112 if (operation
== OP_DELETE
) {
2113 if (S_ISDIR (src_stat
.st_mode
))
2114 value
= erase_dir (tctx
, ctx
, source_with_path
);
2116 value
= erase_file (tctx
, ctx
, source_with_path
, 1);
2118 temp
= transform_source (ctx
, source_with_path
);
2120 value
= transform_error
;
2122 char *repl_dest
, *temp2
;
2124 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2125 temp2
= concat_dir_and_file (repl_dest
, temp
);
2131 switch (operation
) {
2133 /* we use file_mask_op_follow_links only with OP_COPY */
2134 ctx
->stat_func (source_with_path
, &src_stat
);
2136 if (S_ISDIR (src_stat
.st_mode
))
2137 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, dest
,
2138 TRUE
, FALSE
, FALSE
, NULL
);
2140 value
= copy_file_file (tctx
, ctx
, source_with_path
, dest
);
2144 if (S_ISDIR (src_stat
.st_mode
))
2145 value
= move_dir_dir (tctx
, ctx
, source_with_path
, dest
);
2147 value
= move_file_file (tctx
, ctx
, source_with_path
, dest
);
2151 /* Unknown file operation */
2155 } /* Copy or move operation */
2157 if ((value
== FILE_CONT
) && !force_single
)
2158 unmark_files (panel
);
2163 /* Check destination for copy or move operation */
2164 while (operation
!= OP_DELETE
) {
2166 struct stat dst_stat
;
2168 dst_result
= mc_stat (dest
, &dst_stat
);
2170 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2173 if (file_error (_(" Destination \"%s\" must be a directory \n %s "),
2174 dest
) != FILE_RETRY
)
2178 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
) {
2179 /* Loop for every file, perform the actual copy operation */
2180 for (i
= 0; i
< panel
->count
; i
++) {
2181 if (!panel
->dir
.list
[i
].f
.marked
)
2182 continue; /* Skip the unmarked ones */
2184 source
= panel
->dir
.list
[i
].fname
;
2185 src_stat
= panel
->dir
.list
[i
].st
;
2187 #ifdef WITH_FULL_PATHS
2188 g_free (source_with_path
);
2189 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2190 #endif /* WITH_FULL_PATHS */
2192 if (operation
== OP_DELETE
) {
2193 if (S_ISDIR (src_stat
.st_mode
))
2194 value
= erase_dir (tctx
, ctx
, source_with_path
);
2196 value
= erase_file (tctx
, ctx
, source_with_path
, 1);
2198 temp
= transform_source (ctx
, source_with_path
);
2201 value
= transform_error
;
2203 char *temp2
, *temp3
, *repl_dest
;
2205 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2206 temp2
= concat_dir_and_file (repl_dest
, temp
);
2209 temp3
= source_with_path
;
2210 source_with_path
= strutils_shell_unescape (source_with_path
);
2213 temp2
= strutils_shell_unescape (temp2
);
2216 switch (operation
) {
2218 /* we use file_mask_op_follow_links only with OP_COPY */
2219 ctx
->stat_func (source_with_path
, &src_stat
);
2220 if (S_ISDIR (src_stat
.st_mode
))
2221 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, temp2
,
2222 TRUE
, FALSE
, FALSE
, NULL
);
2224 value
= copy_file_file (tctx
, ctx
, source_with_path
, temp2
);
2225 free_linklist (&dest_dirs
);
2229 if (S_ISDIR (src_stat
.st_mode
))
2230 value
= move_dir_dir (tctx
, ctx
, source_with_path
, temp2
);
2232 value
= move_file_file (tctx
, ctx
, source_with_path
, temp2
);
2236 /* Unknown file operation */
2242 } /* Copy or move operation */
2244 if (value
== FILE_ABORT
)
2247 if (value
== FILE_CONT
)
2248 do_file_mark (panel
, i
, 0);
2250 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2253 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
2255 if (operation
!= OP_DELETE
)
2256 file_progress_show (ctx
, 0, 0, "", FALSE
);
2259 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2263 } /* Loop for every file */
2265 } /* Many entries */
2269 if (save_cwd
!= NULL
) {
2270 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2274 if (save_dest
!= NULL
) {
2275 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2279 free_linklist (&linklist
);
2280 free_linklist (&dest_dirs
);
2281 #ifdef WITH_FULL_PATHS
2282 g_free (source_with_path
);
2283 #endif /* WITH_FULL_PATHS */
2285 g_free (ctx
->dest_mask
);
2286 ctx
->dest_mask
= NULL
;
2288 #ifdef WITH_BACKGROUND
2289 /* Let our parent know we are saying bye bye */
2290 if (we_are_background
) {
2291 int cur_pid
= getpid();
2292 /* Send pid to parent with child context, it is fork and
2293 don't modify real parent ctx */
2295 parent_call ((void *) end_bg_process
, ctx
, 0);
2300 #endif /* WITH_BACKGROUND */
2302 file_op_context_destroy (ctx
);
2308 /* {{{ Query/status report routines */
2310 static FileProgressStatus
2311 real_do_file_error (enum OperationMode mode
, const char *error
)
2316 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2318 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2336 /* Report error with one file */
2338 file_error (const char *format
, const char *file
)
2340 char buf
[BUF_MEDIUM
];
2342 g_snprintf (buf
, sizeof (buf
), format
,
2343 path_trunc (file
, 30), unix_error_string (errno
));
2345 return do_file_error (buf
);
2348 /* Report error with two files */
2349 static FileProgressStatus
2350 files_error (const char *format
, const char *file1
, const char *file2
)
2352 char buf
[BUF_MEDIUM
];
2353 char *nfile1
= g_strdup (path_trunc (file1
, 15));
2354 char *nfile2
= g_strdup (path_trunc (file2
, 15));
2356 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
,
2357 unix_error_string (errno
));
2362 return do_file_error (buf
);
2365 static FileProgressStatus
2366 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, const char *s
)
2370 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2371 const char *msg
= mode
== Foreground
2372 ? _("\n Directory not empty. \n"
2373 " Delete it recursively? ")
2374 : _("\n Background process: Directory not empty \n"
2375 " Delete it recursively? ");
2376 text
= g_strconcat (_(" Delete: "), path_trunc (s
, 30), " ", (char *) NULL
);
2381 ctx
->recursive_result
=
2382 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
2383 _("&Yes"), _("&No"),
2384 _("A&ll"), _("Non&e"),
2387 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2392 switch (ctx
->recursive_result
) {
2394 case RECURSIVE_ALWAYS
:
2398 case RECURSIVE_NEVER
:
2401 case RECURSIVE_ABORT
:
2407 #ifdef WITH_BACKGROUND
2408 static FileProgressStatus
2409 do_file_error (const char *str
)
2413 FileProgressStatus (*f
) (enum OperationMode
, const char *);
2415 pntr
.f
= real_do_file_error
;
2417 if (we_are_background
)
2418 return parent_call (pntr
.p
, NULL
, 1, strlen (str
),
2421 return real_do_file_error (Foreground
, str
);
2424 static FileProgressStatus
2425 query_recursive (FileOpContext
*ctx
, const char *s
)
2429 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
2431 pntr
.f
= real_query_recursive
;
2433 if (we_are_background
)
2434 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
2436 return real_query_recursive (ctx
, Foreground
, s
);
2439 static FileProgressStatus
2440 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2441 struct stat
*_d_stat
)
2445 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
2446 struct stat
*, struct stat
*);
2448 pntr
.f
= file_progress_real_query_replace
;
2450 if (we_are_background
)
2451 return parent_call (pntr
.p
,
2454 strlen (destname
), destname
,
2455 sizeof (struct stat
), _s_stat
,
2456 sizeof (struct stat
), _d_stat
);
2458 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2463 static FileProgressStatus
2464 do_file_error (const char *str
)
2466 return real_do_file_error (Foreground
, str
);
2469 static FileProgressStatus
2470 query_recursive (FileOpContext
*ctx
, const char *s
)
2472 return real_query_recursive (ctx
, Foreground
, s
);
2475 static FileProgressStatus
2476 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2477 struct stat
*_d_stat
)
2479 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2483 #endif /* !WITH_BACKGROUND */
2486 Cause emacs to enter folding mode for this file: