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 */
104 struct vfs_class
*vfs
;
112 /* the hard link cache */
113 static struct link
*linklist
= NULL
;
115 /* the files-to-be-erased list */
116 static struct link
*erase_list
;
119 * In copy_dir_dir we use two additional single linked lists: The first -
120 * variable name `parent_dirs' - holds information about already copied
121 * directories and is used to detect cyclic symbolic links.
122 * The second (`dest_dirs' below) holds information about just created
123 * target directories and is used to detect when an directory is copied
124 * into itself (we don't want to copy infinitly).
125 * Both lists don't use the linkcount and name structure members of struct
128 static struct link
*dest_dirs
= NULL
;
130 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
131 const char *op_names
[3] = {
132 N_("DialogTitle|Copy"),
133 N_("DialogTitle|Move"),
134 N_("DialogTitle|Delete")
139 static FileProgressStatus
query_replace (FileOpContext
* ctx
, const char *destname
,
140 struct stat
*_s_stat
, struct stat
*_d_stat
);
141 static FileProgressStatus
query_recursive (FileOpContext
* ctx
, const char *s
);
142 static FileProgressStatus
do_file_error (const char *str
);
143 static FileProgressStatus
erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
);
144 static FileProgressStatus
erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
145 const char *s
, gboolean is_toplevel_file
);
146 static FileProgressStatus
files_error (const char *format
, const char *file1
, const char *file2
);
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
);
171 transform_error
= FILE_SKIP
;
179 free_linklist (struct link
**lc_linklist
)
181 struct link
*lp
, *lp2
;
183 for (lp
= *lc_linklist
; lp
!= NULL
; lp
= lp2
)
192 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
194 ino_t ino
= sb
->st_ino
;
195 dev_t dev
= sb
->st_dev
;
197 struct vfs_class
*vfs
= vfs_get_class (path
);
198 #endif /* ENABLE_VFS */
206 #endif /* ENABLE_VFS */
207 if (lp
->ino
== ino
&& lp
->dev
== dev
)
215 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
216 * and a hardlink was succesfully made
219 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
222 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
223 ino_t ino
= pstat
->st_ino
;
224 dev_t dev
= pstat
->st_dev
;
225 struct stat link_stat
;
228 if (vfs_file_class_flags (src_name
) & VFSF_NOLINKS
)
231 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
232 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
)
234 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
235 && link_stat
.st_dev
== dev
&& vfs_get_class (lp
->name
) == my_vfs
)
237 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
239 if (vfs_get_class (dst_name
) == vfs_get_class (p
))
241 if (!mc_stat (p
, &link_stat
))
243 if (!mc_link (p
, dst_name
))
248 message (D_ERROR
, MSG_ERROR
, _(" Cannot make the hardlink "));
251 lp
= (struct link
*) g_try_malloc (sizeof (struct link
) + strlen (src_name
)
252 + strlen (dst_name
) + 1);
259 strcpy (lp
->name
, src_name
);
260 lpdstname
= lp
->name
+ strlen (lp
->name
) + 1;
261 strcpy (lpdstname
, dst_name
);
269 * Duplicate the contents of the symbolic link src_path in dst_path.
270 * Try to make a stable symlink if the option "stable symlink" was
271 * set in the file mask dialog.
272 * If dst_path is an existing symlink it will be deleted silently
273 * (upper levels take already care of existing files at dst_path).
275 static FileProgressStatus
276 make_symlink (FileOpContext
* ctx
, const char *src_path
, const char *dst_path
)
278 char link_target
[MC_MAXPATHLEN
];
280 FileProgressStatus return_status
;
282 gboolean dst_is_symlink
;
284 dst_is_symlink
= (mc_lstat (dst_path
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
287 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
290 return_status
= file_error (_(" Cannot read source link \"%s\" \n %s "), src_path
);
291 if (return_status
== FILE_RETRY
)
292 goto retry_src_readlink
;
293 return return_status
;
295 link_target
[len
] = 0;
297 if (ctx
->stable_symlinks
)
298 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
))
300 message (D_ERROR
, MSG_ERROR
,
301 _(" Cannot make stable symlinks across "
302 "non-local filesystems: \n\n" " Option Stable Symlinks will be disabled "));
303 ctx
->stable_symlinks
= FALSE
;
306 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
310 const char *r
= strrchr (src_path
, PATH_SEP
);
314 p
= g_strndup (src_path
, r
- src_path
+ 1);
315 if (g_path_is_absolute (dst_path
))
316 q
= g_strdup (dst_path
);
318 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
319 s
= strrchr (q
, PATH_SEP
);
323 s
= g_strconcat (p
, link_target
, (char *) NULL
);
325 g_strlcpy (link_target
, s
, sizeof (link_target
));
327 s
= diff_two_paths (q
, link_target
);
330 g_strlcpy (link_target
, s
, sizeof (link_target
));
340 if (mc_symlink (link_target
, dst_path
) == 0)
344 * if dst_exists, it is obvious that this had failed.
345 * We can delete the old symlink and try again...
349 if (!mc_unlink (dst_path
))
350 if (mc_symlink (link_target
, dst_path
) == 0)
354 return_status
= file_error (_(" Cannot create target symlink \"%s\" \n %s "), dst_path
);
355 if (return_status
== FILE_RETRY
)
356 goto retry_dst_symlink
;
357 return return_status
;
360 static FileProgressStatus
361 progress_update_one (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, off_t add
,
362 gboolean is_toplevel_file
)
364 struct timeval tv_current
;
365 static struct timeval tv_start
= { };
367 if (is_toplevel_file
|| ctx
->progress_totals_computed
)
369 tctx
->progress_count
++;
370 tctx
->progress_bytes
+= add
;
372 if (tv_start
.tv_sec
== 0)
374 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
376 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
377 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
379 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
380 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
381 tv_start
.tv_sec
= tv_current
.tv_sec
;
384 return check_progress_buttons (ctx
);
387 /* Status of the destination file */
390 DEST_NONE
= 0, /* Not created */
391 DEST_SHORT
= 1, /* Created, not fully copied */
392 DEST_FULL
= 2 /* Created, fully copied */
395 static FileProgressStatus
396 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
400 const char *head_msg
;
402 head_msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
404 msg
= g_strdup_printf (fmt
, a
, b
);
405 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
409 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
412 #ifdef WITH_BACKGROUND
413 static FileProgressStatus
414 warn_same_file (const char *fmt
, const char *a
, const char *b
)
419 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
420 const char *a
, const char *b
);
422 pntr
.f
= real_warn_same_file
;
424 if (we_are_background
)
425 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
427 return real_warn_same_file (Foreground
, fmt
, a
, b
);
430 static FileProgressStatus
431 warn_same_file (const char *fmt
, const char *a
, const char *b
)
433 return real_warn_same_file (Foreground
, fmt
, a
, b
);
438 copy_file_file_display_progress (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
439 struct timeval tv_current
, struct timeval tv_transfer_start
,
440 off_t file_size
, off_t n_read_total
)
444 /* 1. Update rotating dash after some time */
448 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
452 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
453 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
458 /* 4. Compute BPS rate */
459 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
460 if (ctx
->bps_time
< 1)
462 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
464 /* 5. Compute total ETA and BPS */
465 if (ctx
->progress_bytes
!= 0)
468 tctx
->copyed_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
469 remain_bytes
= ctx
->progress_bytes
- tctx
->copyed_bytes
;
472 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
476 tctx
->bps
= tctx
->copyed_bytes
/ total_secs
;
477 tctx
->eta_secs
= remain_bytes
/ tctx
->bps
;
480 /* broken on lot of little files */
482 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
483 tctx
->eta_secs
= remain_bytes
/ tctx
->bps
;
489 copy_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
490 const char *src_path
, const char *dst_path
)
492 uid_t src_uid
= (uid_t
) - 1;
493 gid_t src_gid
= (gid_t
) - 1;
495 int src_desc
, dest_desc
= -1;
496 int n_read
, n_written
;
497 mode_t src_mode
= 0; /* The mode of the source file */
500 gboolean dst_exists
= FALSE
, appending
= FALSE
;
501 off_t n_read_total
= 0, file_size
= -1;
502 FileProgressStatus return_status
, temp_status
;
503 struct timeval tv_transfer_start
;
504 dest_status_t dst_status
= DEST_NONE
;
506 gboolean is_first_time
= TRUE
;
508 /* FIXME: We should not be using global variables! */
510 return_status
= FILE_RETRY
;
512 file_progress_show_source (ctx
, src_path
);
513 file_progress_show_target (ctx
, dst_path
);
514 if (check_progress_buttons (ctx
) == FILE_ABORT
)
519 while (mc_stat (dst_path
, &sb2
) == 0)
521 if (S_ISDIR (sb2
.st_mode
))
523 return_status
= file_error (_(" Cannot overwrite directory \"%s\" \n %s "), dst_path
);
524 if (return_status
== FILE_RETRY
)
526 return return_status
;
532 while ((*ctx
->stat_func
) (src_path
, &sb
))
534 return_status
= file_error (_(" Cannot stat source file \"%s\" \n %s "), src_path
);
535 if (return_status
!= FILE_RETRY
)
536 return return_status
;
541 /* Destination already exists */
542 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
543 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "),
545 /* Should we replace destination? */
546 if (tctx
->ask_overwrite
)
549 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
550 if (return_status
!= FILE_CONT
)
551 return return_status
;
557 /* Check the hardlinks */
558 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_path
, dst_path
, &sb
) == 1)
560 /* We have made a hardlink - no more processing is necessary */
564 if (S_ISLNK (sb
.st_mode
))
565 return make_symlink (ctx
, src_path
, dst_path
);
567 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
568 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
570 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0)
573 file_error (_(" Cannot create special file \"%s\" \n %s "), dst_path
);
574 if (return_status
== FILE_RETRY
)
576 return return_status
;
580 while (ctx
->preserve_uidgid
&& mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
))
582 temp_status
= file_error (_(" Cannot chown target file \"%s\" \n %s "), dst_path
);
583 if (temp_status
== FILE_RETRY
)
587 while (ctx
->preserve
&& mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
))
589 temp_status
= file_error (_(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
590 if (temp_status
== FILE_RETRY
)
598 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
600 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0)
602 return_status
= file_error (_(" Cannot open source file \"%s\" \n %s "), src_path
);
603 if (return_status
== FILE_RETRY
)
606 return return_status
;
609 if (ctx
->do_reget
!= 0)
611 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
613 message (D_ERROR
, _("Warning"), _(" Reget failed, about to overwrite file "));
615 ctx
->do_append
= FALSE
;
619 while (mc_fstat (src_desc
, &sb
))
621 return_status
= file_error (_(" Cannot fstat source file \"%s\" \n %s "), src_path
);
622 if (return_status
== FILE_RETRY
)
624 ctx
->do_append
= FALSE
;
627 src_mode
= sb
.st_mode
;
630 utb
.actime
= sb
.st_atime
;
631 utb
.modtime
= sb
.st_mtime
;
632 file_size
= sb
.st_size
;
634 open_flags
= O_WRONLY
;
637 if (ctx
->do_append
!= 0)
638 open_flags
|= O_APPEND
;
640 open_flags
|= O_CREAT
| O_TRUNC
;
644 open_flags
|= O_CREAT
| O_EXCL
;
647 while ((dest_desc
= mc_open (dst_path
, open_flags
, src_mode
)) < 0)
652 return_status
= file_error (_(" Cannot create target file \"%s\" \n %s "), dst_path
);
653 if (return_status
== FILE_RETRY
)
655 ctx
->do_append
= FALSE
;
658 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
660 appending
= ctx
->do_append
;
661 ctx
->do_append
= FALSE
;
663 /* Find out the optimal buffer size. */
664 while (mc_fstat (dest_desc
, &sb
))
666 return_status
= file_error (_(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
667 if (return_status
== FILE_RETRY
)
675 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
676 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
678 file_progress_show (ctx
, 1, 1, "", TRUE
);
679 return_status
= check_progress_buttons (ctx
);
682 if (return_status
!= FILE_CONT
)
686 struct timeval tv_current
, tv_last_update
, tv_last_input
;
687 int secs
, update_secs
;
688 const char *stalled_msg
= "";
690 tv_last_update
= tv_transfer_start
;
697 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
700 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0)
703 file_error (_(" Cannot read source file \"%s\" \n %s "), src_path
);
704 if (return_status
== FILE_RETRY
)
711 gettimeofday (&tv_current
, NULL
);
716 n_read_total
+= n_read
;
718 /* Windows NT ftp servers report that files have no
719 * permissions: -------, so if we happen to have actually
720 * read something, we should fix the permissions.
722 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
723 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
724 gettimeofday (&tv_last_input
, NULL
);
727 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
)
736 file_error (_(" Cannot write target file \"%s\" \n %s "), dst_path
);
737 if (return_status
!= FILE_RETRY
)
741 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
742 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
744 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
746 copy_file_file_display_progress (tctx
, ctx
,
748 tv_transfer_start
, file_size
, n_read_total
);
749 tv_last_update
= tv_current
;
751 is_first_time
= FALSE
;
753 if (update_secs
> FILEOP_STALLING_INTERVAL
)
755 stalled_msg
= _("(stalled)");
759 gboolean force_update
=
760 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
761 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
762 file_progress_show_total (tctx
, ctx
,
763 tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
,
767 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
772 return_status
= check_progress_buttons (ctx
);
774 if (return_status
!= FILE_CONT
)
782 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
785 while (src_desc
!= -1 && mc_close (src_desc
) < 0)
787 temp_status
= file_error (_(" Cannot close source file \"%s\" \n %s "), src_path
);
788 if (temp_status
== FILE_RETRY
)
790 if (temp_status
== FILE_ABORT
)
791 return_status
= temp_status
;
795 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0)
797 temp_status
= file_error (_(" Cannot close target file \"%s\" \n %s "), dst_path
);
798 if (temp_status
== FILE_RETRY
)
800 return_status
= temp_status
;
804 if (dst_status
== DEST_SHORT
)
806 /* Remove short file */
808 result
= query_dialog (Q_ ("DialogTitle|Copy"),
809 _("Incomplete file was retrieved. Keep it?"),
810 D_ERROR
, 2, _("&Delete"), _("&Keep"));
812 mc_unlink (dst_path
);
814 else if (dst_status
== DEST_FULL
)
816 /* Copy has succeeded */
817 if (!appending
&& ctx
->preserve_uidgid
)
819 while (mc_chown (dst_path
, src_uid
, src_gid
))
821 temp_status
= file_error (_(" Cannot chown target file \"%s\" \n %s "), dst_path
);
822 if (temp_status
== FILE_RETRY
)
824 return_status
= temp_status
;
833 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
)))
836 file_error (_(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
837 if (temp_status
!= FILE_RETRY
)
839 return_status
= temp_status
;
846 src_mode
= umask (-1);
848 src_mode
= 0100666 & ~src_mode
;
849 mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
));
851 mc_utime (dst_path
, &utb
);
855 if (return_status
== FILE_CONT
)
856 return_status
= progress_update_one (tctx
, ctx
, file_size
, tctx
->is_toplevel_file
);
858 return return_status
;
862 * I think these copy_*_* functions should have a return type.
863 * anyway, this function *must* have two directories as arguments.
865 /* FIXME: This function needs to check the return values of the
868 copy_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *_d
,
869 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, struct link
* parent_dirs
)
872 struct stat buf
, cbuf
;
874 char *dest_dir
= NULL
;
875 FileProgressStatus return_status
= FILE_CONT
;
880 d
= strutils_shell_unescape (_d
);
882 /* First get the mode of the source dir */
884 if ((*ctx
->stat_func
) (s
, &cbuf
))
886 return_status
= file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
887 if (return_status
== FILE_RETRY
)
892 if (is_in_linklist (dest_dirs
, s
, &cbuf
))
894 /* Don't copy a directory we created before (we don't want to copy
895 infinitely if a directory is copied into itself) */
896 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
897 return_status
= FILE_CONT
;
901 /* Hmm, hardlink to directory??? - Norbert */
902 /* FIXME: In this step we should do something
903 in case the destination already exist */
904 /* Check the hardlinks */
905 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (s
, d
, &cbuf
) == 1)
907 /* We have made a hardlink - no more processing is necessary */
911 if (!S_ISDIR (cbuf
.st_mode
))
913 return_status
= file_error (_(" Source \"%s\" is not a directory \n %s "), s
);
914 if (return_status
== FILE_RETRY
)
919 if (is_in_linklist (parent_dirs
, s
, &cbuf
))
921 /* we found a cyclic symbolic link */
922 message (D_ERROR
, MSG_ERROR
, _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
923 return_status
= FILE_SKIP
;
927 lp
= g_new (struct link
, 1);
928 lp
->vfs
= vfs_get_class (s
);
929 lp
->ino
= cbuf
.st_ino
;
930 lp
->dev
= cbuf
.st_dev
;
931 lp
->next
= parent_dirs
;
935 /* Now, check if the dest dir exists, if not, create it. */
936 if (mc_stat (d
, &buf
))
938 /* Here the dir doesn't exist : make it ! */
941 if (mc_rename (s
, d
) == 0)
943 return_status
= FILE_CONT
;
953 * If the destination directory exists, we want to copy the whole
954 * directory, but we only want this to happen once.
956 * Escape sequences added to the * to compiler warnings.
957 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
958 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
960 if (!S_ISDIR (buf
.st_mode
))
962 return_status
= file_error (_(" Destination \"%s\" must be a directory \n %s "), d
);
963 if (return_status
== FILE_RETRY
)
967 /* Dive into subdir if exists */
968 if (toplevel
&& ctx
->dive_into_subdirs
)
970 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
979 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
))
981 return_status
= file_error (_(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
982 if (return_status
!= FILE_RETRY
)
986 lp
= g_new (struct link
, 1);
987 mc_stat (dest_dir
, &buf
);
988 lp
->vfs
= vfs_get_class (dest_dir
);
989 lp
->ino
= buf
.st_ino
;
990 lp
->dev
= buf
.st_dev
;
991 lp
->next
= dest_dirs
;
994 if (ctx
->preserve_uidgid
)
996 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
))
999 file_error (_(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
1000 if (return_status
!= FILE_RETRY
)
1006 /* open the source dir for reading */
1007 reading
= mc_opendir (s
);
1008 if (reading
== NULL
)
1011 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1015 * Now, we don't want '.' and '..' to be created / copied at any time
1017 if (!strcmp (next
->d_name
, "."))
1019 if (!strcmp (next
->d_name
, ".."))
1022 /* get the filename and add it to the src directory */
1023 path
= concat_dir_and_file (s
, next
->d_name
);
1025 (*ctx
->stat_func
) (path
, &buf
);
1026 if (S_ISDIR (buf
.st_mode
))
1030 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
1032 * From here, we just intend to recursively copy subdirs, not
1033 * the double functionality of copying different when the target
1034 * dir already exists. So, we give the recursive call the flag 0
1035 * meaning no toplevel.
1038 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
1045 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
1046 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
1049 if (do_delete
&& return_status
== FILE_CONT
)
1051 if (ctx
->erase_at_end
)
1053 static struct link
*tail
;
1054 size_t len
= strlen (path
);
1055 lp
= g_malloc (sizeof (struct link
) + len
);
1056 strncpy (lp
->name
, path
, len
+ 1);
1057 lp
->st_mode
= buf
.st_mode
;
1059 if (erase_list
!= NULL
)
1065 erase_list
= tail
= lp
;
1069 if (S_ISDIR (buf
.st_mode
))
1071 return_status
= erase_dir_iff_empty (ctx
, path
);
1074 return_status
= erase_file (tctx
, ctx
, path
, FALSE
);
1079 mc_closedir (reading
);
1083 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1084 utb
.actime
= cbuf
.st_atime
;
1085 utb
.modtime
= cbuf
.st_mtime
;
1086 mc_utime (dest_dir
, &utb
);
1090 cbuf
.st_mode
= umask (-1);
1091 umask (cbuf
.st_mode
);
1092 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
1093 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1098 g_free (parent_dirs
);
1101 return return_status
;
1106 /* {{{ Move routines */
1108 static FileProgressStatus
1109 move_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
1111 struct stat src_stats
, dst_stats
;
1112 FileProgressStatus return_status
= FILE_CONT
;
1113 gboolean copy_done
= FALSE
;
1114 gboolean old_ask_overwrite
;
1116 file_progress_show_source (ctx
, s
);
1117 file_progress_show_target (ctx
, d
);
1118 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1123 while (mc_lstat (s
, &src_stats
) != 0)
1125 /* Source doesn't exist */
1126 return_status
= file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1127 if (return_status
!= FILE_RETRY
)
1128 return return_status
;
1131 if (mc_lstat (d
, &dst_stats
) == 0)
1133 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
1134 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "), s
, d
);
1136 if (S_ISDIR (dst_stats
.st_mode
))
1138 message (D_ERROR
, MSG_ERROR
, _(" Cannot overwrite directory `%s' "), d
);
1143 if (confirm_overwrite
)
1145 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1146 if (return_status
!= FILE_CONT
)
1147 return return_status
;
1149 /* Ok to overwrite */
1152 if (!ctx
->do_append
)
1154 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
1156 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
)
1157 goto retry_src_remove
;
1159 return return_status
;
1162 if (mc_rename (s
, d
) == 0)
1163 return progress_update_one (tctx
, ctx
, src_stats
.st_size
, TRUE
);
1166 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1167 one nfs to the same, but on the server it is on two different
1168 filesystems. Then nfs returns EIO instead of EXDEV.
1169 Hope it will not hurt if we always in case of error try to copy/delete. */
1171 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1175 return_status
= files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
, d
);
1176 if (return_status
== FILE_RETRY
)
1178 return return_status
;
1182 /* Failed because filesystem boundary -> copy the file instead */
1183 old_ask_overwrite
= tctx
->ask_overwrite
;
1184 tctx
->ask_overwrite
= FALSE
;
1185 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
1186 tctx
->ask_overwrite
= old_ask_overwrite
;
1187 if (return_status
!= FILE_CONT
)
1188 return return_status
;
1192 file_progress_show_source (ctx
, NULL
);
1193 file_progress_show (ctx
, 0, 0, "", FALSE
);
1195 return_status
= check_progress_buttons (ctx
);
1196 if (return_status
!= FILE_CONT
)
1197 return return_status
;
1204 return_status
= file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1205 if (return_status
== FILE_RETRY
)
1206 goto retry_src_remove
;
1207 return return_status
;
1211 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
, TRUE
);
1213 return return_status
;
1217 move_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
1219 struct stat sbuf
, dbuf
, destbuf
;
1222 FileProgressStatus return_status
;
1223 gboolean move_over
= FALSE
;
1226 file_progress_show_source (ctx
, s
);
1227 file_progress_show_target (ctx
, d
);
1228 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1234 dstat_ok
= (mc_stat (d
, &dbuf
) == 0);
1236 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
1237 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same directory "), s
, d
);
1240 destdir
= g_strdup (d
); /* destination doesn't exist */
1241 else if (!ctx
->dive_into_subdirs
)
1243 destdir
= g_strdup (d
);
1247 destdir
= concat_dir_and_file (d
, x_basename (s
));
1249 /* Check if the user inputted an existing dir */
1251 if (!mc_stat (destdir
, &destbuf
))
1255 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
1257 if (return_status
!= FILE_CONT
)
1263 if (S_ISDIR (destbuf
.st_mode
))
1264 return_status
= file_error (_(" Cannot overwrite directory \"%s\" %s "), destdir
);
1266 return_status
= file_error (_(" Cannot overwrite file \"%s\" %s "), destdir
);
1267 if (return_status
== FILE_RETRY
)
1268 goto retry_dst_stat
;
1271 return return_status
;
1275 if (mc_rename (s
, destdir
) == 0)
1277 return_status
= FILE_CONT
;
1283 return_status
= files_error (_(" Cannot move directory \"%s\" to \"%s\" \n %s "), s
, d
);
1284 if (return_status
== FILE_RETRY
)
1288 /* Failed because of filesystem boundary -> copy dir instead */
1289 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
1291 if (return_status
!= FILE_CONT
)
1294 file_progress_show_source (ctx
, NULL
);
1295 file_progress_show (ctx
, 0, 0, "", FALSE
);
1297 return_status
= check_progress_buttons (ctx
);
1298 if (return_status
!= FILE_CONT
)
1302 if (ctx
->erase_at_end
)
1304 for (; erase_list
&& return_status
!= FILE_ABORT
;)
1306 if (S_ISDIR (erase_list
->st_mode
))
1308 return_status
= erase_dir_iff_empty (ctx
, erase_list
->name
);
1311 return_status
= erase_file (tctx
, ctx
, erase_list
->name
, FALSE
);
1313 erase_list
= erase_list
->next
;
1317 erase_dir_iff_empty (ctx
, s
);
1324 erase_list
= erase_list
->next
;
1327 return return_status
;
1332 /* {{{ Erase routines */
1333 /* Don't update progress status if progress_count==NULL */
1334 static FileProgressStatus
1335 erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
,
1336 gboolean is_toplevel_file
)
1341 file_progress_show_deleting (ctx
, s
);
1342 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1346 if (tctx
->progress_count
&& mc_lstat (s
, &buf
))
1348 /* ignore, most likely the mc_unlink fails, too */
1352 while (mc_unlink (s
))
1354 return_status
= file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1355 if (return_status
!= FILE_RETRY
)
1356 return return_status
;
1359 if (tctx
->progress_count
)
1360 return progress_update_one (tctx
, ctx
, buf
.st_size
, is_toplevel_file
);
1365 static FileProgressStatus
1366 recursive_erase (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
1368 struct dirent
*next
;
1372 FileProgressStatus return_status
= FILE_CONT
;
1374 if (!strcmp (s
, ".."))
1377 reading
= mc_opendir (s
);
1382 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
)
1384 if (!strcmp (next
->d_name
, "."))
1386 if (!strcmp (next
->d_name
, ".."))
1388 path
= concat_dir_and_file (s
, next
->d_name
);
1389 if (mc_lstat (path
, &buf
))
1392 mc_closedir (reading
);
1395 if (S_ISDIR (buf
.st_mode
))
1397 (recursive_erase (tctx
, ctx
, path
) != FILE_CONT
) ? FILE_RETRY
: FILE_CONT
;
1399 return_status
= erase_file (tctx
, ctx
, path
, 0);
1402 mc_closedir (reading
);
1403 if (return_status
!= FILE_CONT
)
1404 return return_status
;
1405 file_progress_show_deleting (ctx
, s
);
1406 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1410 while (my_rmdir (s
))
1412 return_status
= file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1413 if (return_status
!= FILE_RETRY
)
1414 return return_status
;
1420 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1421 in the directory path points to, 0 else. */
1423 check_dir_is_empty (const char *path
)
1429 dir
= mc_opendir (path
);
1433 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
))
1435 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1436 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1437 continue; /* "." or ".." */
1447 erase_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
1449 FileProgressStatus error
;
1451 if (strcmp (s
, "..") == 0)
1454 if (strcmp (s
, ".") == 0)
1457 file_progress_show_deleting (ctx
, s
);
1458 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1462 /* The old way to detect a non empty directory was:
1463 error = my_rmdir (s);
1464 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1465 For the linux user space nfs server (nfs-server-2.2beta29-2)
1466 we would have to check also for EIO. I hope the new way is
1467 fool proof. (Norbert)
1469 error
= check_dir_is_empty (s
);
1472 error
= query_recursive (ctx
, s
);
1473 if (error
== FILE_CONT
)
1474 return recursive_erase (tctx
, ctx
, s
);
1479 while (my_rmdir (s
) == -1)
1481 error
= file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1482 if (error
!= FILE_RETRY
)
1489 static FileProgressStatus
1490 erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
)
1492 FileProgressStatus error
;
1494 if (strcmp (s
, "..") == 0)
1497 if (strcmp (s
, ".") == 0)
1500 file_progress_show_deleting (ctx
, s
);
1501 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1505 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1508 while (my_rmdir (s
))
1510 error
= file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1511 if (error
!= FILE_RETRY
)
1520 /* {{{ Panel operate routines */
1523 * Return currently selected entry name or the name of the first marked
1524 * entry if there is one.
1527 panel_get_file (WPanel
* panel
, struct stat
*stat_buf
)
1531 if (get_current_type () == view_tree
)
1533 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1534 char *tree_name
= tree_selected_name (tree
);
1536 mc_stat (tree_name
, stat_buf
);
1542 for (i
= 0; i
< panel
->count
; i
++)
1543 if (panel
->dir
.list
[i
].f
.marked
)
1545 *stat_buf
= panel
->dir
.list
[i
].st
;
1546 return panel
->dir
.list
[i
].fname
;
1551 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1552 return panel
->dir
.list
[panel
->selected
].fname
;
1554 g_assert_not_reached ();
1560 compute_dir_size_create_ui (void)
1562 ComputeDirSizeUI
*ui
;
1564 const char *b_name
= N_("&Abort");
1570 ui
= g_new (ComputeDirSizeUI
, 1);
1572 ui
->dlg
= create_dlg (0, 0, 8, COLS
/ 2, dialog_colors
, NULL
,
1573 NULL
, _("Directory scanning"), DLG_CENTER
);
1574 ui
->dirname
= label_new (3, 3, "");
1575 add_widget (ui
->dlg
, ui
->dirname
);
1577 add_widget (ui
->dlg
,
1578 button_new (5, (ui
->dlg
->cols
- strlen (b_name
)) / 2,
1579 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
1581 /* We will manage the dialog without any help,
1582 that's why we have to call init_dlg */
1589 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
1593 /* schedule to update passive panel */
1594 other_panel
->dirty
= 1;
1596 /* close and destroy dialog */
1597 dlg_run_done (ui
->dlg
);
1598 destroy_dlg (ui
->dlg
);
1604 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
1606 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
1613 label_set_text (this->dirname
, name_trunc (dirname
, this->dlg
->cols
- 6));
1615 event
.x
= -1; /* Don't show the GPM cursor */
1616 c
= tty_get_event (&event
, FALSE
, FALSE
);
1620 /* Reinitialize to avoid old values after events other than
1621 selecting a button */
1622 this->dlg
->ret_value
= FILE_CONT
;
1624 dlg_process_event (this->dlg
, c
, &event
);
1626 switch (this->dlg
->ret_value
)
1639 * Computes the number of bytes used by the files in a directory
1642 compute_dir_size (const char *dirname
, const void *ui
,
1643 compute_dir_size_callback cback
,
1644 off_t
* ret_marked
, double *ret_total
, gboolean compute_symlinks
)
1649 struct dirent
*dirent
;
1650 FileProgressStatus ret
= FILE_CONT
;
1652 if (!compute_symlinks
)
1654 res
= mc_lstat (dirname
, &s
);
1658 /* don't scan symlink to directory */
1659 if (S_ISLNK (s
.st_mode
))
1662 *ret_total
+= s
.st_size
;
1667 dir
= mc_opendir (dirname
);
1672 while ((dirent
= mc_readdir (dir
)) != NULL
)
1676 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
1678 if (ret
!= FILE_CONT
)
1681 if (strcmp (dirent
->d_name
, ".") == 0)
1683 if (strcmp (dirent
->d_name
, "..") == 0)
1686 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1687 res
= mc_lstat (fullname
, &s
);
1695 if (S_ISDIR (s
.st_mode
))
1697 off_t subdir_count
= 0;
1698 double subdir_bytes
= 0;
1701 compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
,
1704 if (ret
!= FILE_CONT
)
1710 *ret_marked
+= subdir_count
;
1711 *ret_total
+= subdir_bytes
;
1716 *ret_total
+= s
.st_size
;
1728 * panel_compute_totals:
1730 * compute the number of files and the number of bytes
1731 * used up by the whole selection, recursing directories
1732 * as required. In addition, it checks to see if it will
1733 * overwrite any files by doing the copy.
1735 static FileProgressStatus
1736 panel_compute_totals (const WPanel
* panel
, const void *ui
,
1737 compute_dir_size_callback cback
,
1738 off_t
* ret_marked
, double *ret_total
, gboolean compute_symlinks
)
1745 for (i
= 0; i
< panel
->count
; i
++)
1749 if (!panel
->dir
.list
[i
].f
.marked
)
1752 s
= &panel
->dir
.list
[i
].st
;
1754 if (S_ISDIR (s
->st_mode
))
1757 off_t subdir_count
= 0;
1758 double subdir_bytes
= 0;
1759 FileProgressStatus status
;
1761 dir_name
= concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1763 status
= compute_dir_size (dir_name
, ui
, cback
,
1764 &subdir_count
, &subdir_bytes
, compute_symlinks
);
1767 if (status
!= FILE_CONT
)
1770 *ret_marked
+= subdir_count
;
1771 *ret_total
+= subdir_bytes
;
1776 *ret_total
+= s
->st_size
;
1783 /* Initialize variables for progress bars */
1784 static FileProgressStatus
1785 panel_operate_init_totals (FileOperation operation
,
1786 const WPanel
* panel
, const char *source
, FileOpContext
* ctx
)
1788 FileProgressStatus status
;
1790 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
)
1792 ComputeDirSizeUI
*ui
;
1794 ui
= compute_dir_size_create_ui ();
1797 status
= compute_dir_size (source
, ui
, compute_dir_size_update_ui
,
1798 &ctx
->progress_count
, &ctx
->progress_bytes
,
1801 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1802 &ctx
->progress_count
, &ctx
->progress_bytes
,
1805 compute_dir_size_destroy_ui (ui
);
1807 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1812 ctx
->progress_count
= panel
->marked
;
1813 ctx
->progress_bytes
= panel
->total
;
1814 ctx
->progress_totals_computed
= FALSE
;
1821 * This array introduced to avoid translation problems. The former (op_names)
1822 * is assumed to be nouns, suitable in dialog box titles; this one should
1823 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1824 * (I don't use spaces around the words, because someday they could be
1825 * dropped, when widgets get smarter)
1828 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
1829 static const char *op_names1
[] = {
1830 N_("FileOperation|Copy"),
1831 N_("FileOperation|Move"),
1832 N_("FileOperation|Delete")
1836 * These are formats for building a prompt. Parts encoded as follows:
1837 * %o - operation from op_names1
1838 * %f - file/files or files/directories, as appropriate
1839 * %m - "with source mask" or question mark for delete
1840 * %s - source name (truncated)
1841 * %d - number of marked files
1842 * %e - "to:" or question mark for delete
1844 * xgettext:no-c-format */
1845 static const char *one_format
= N_("%o %f \"%s\"%m");
1846 /* xgettext:no-c-format */
1847 static const char *many_format
= N_("%o %d %f%m");
1849 static const char *prompt_parts
[] = {
1854 N_("files/directories"),
1855 N_(" with source mask:"),
1859 static const char *question_format
= N_("%s?");
1862 * Generate user prompt for panel operation.
1863 * single_source is the name if the source entry or NULL for multiple
1865 * src_stat is only used when single_source is not NULL.
1868 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1869 gboolean single_source
, const struct stat
*src_stat
)
1871 const char *sp
, *cp
;
1872 char format_string
[BUF_MEDIUM
];
1873 char *dp
= format_string
;
1874 gboolean build_question
= FALSE
;
1877 static gboolean i18n_flag
= FALSE
;
1882 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1883 op_names1
[i
] = Q_ (op_names1
[i
]);
1885 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1886 prompt_parts
[i
] = _(prompt_parts
[i
]);
1888 one_format
= _(one_format
);
1889 many_format
= _(many_format
);
1890 question_format
= _(question_format
);
1893 #endif /* ENABLE_NLS */
1895 sp
= single_source
? one_format
: many_format
;
1906 cp
= op_names1
[operation
];
1909 if (operation
== OP_DELETE
)
1912 build_question
= TRUE
;
1915 cp
= prompt_parts
[5];
1918 if (operation
== OP_DELETE
)
1921 build_question
= TRUE
;
1924 cp
= prompt_parts
[6];
1928 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1930 cp
= (panel
->marked
== panel
->dirs_marked
)
1932 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1953 char tmp
[BUF_MEDIUM
];
1955 memmove (tmp
, format_string
, sizeof (tmp
));
1956 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1959 return g_strdup (format_string
);
1962 #ifdef WITH_BACKGROUND
1964 end_bg_process (FileOpContext
* ctx
, enum OperationMode mode
)
1971 unregister_task_with_pid (pid
);
1972 /* file_op_context_destroy(ctx); */
1980 * Performs one of the operations on the selection on the source_panel
1981 * (copy, delete, move).
1983 * Returns TRUE if did change the directory
1984 * structure, Returns FALSE if user aborted
1986 * force_single forces operation on the current entry and affects
1987 * default destination. Current filename is used as default.
1990 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
1992 WPanel
*panel
= (WPanel
*) source_panel
;
1993 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
1994 || (get_current_type () == view_tree
);
1996 char *source
= NULL
;
1997 #ifdef WITH_FULL_PATHS
1998 char *source_with_path
= NULL
;
2000 # define source_with_path source
2001 #endif /* !WITH_FULL_PATHS */
2004 char *save_cwd
= NULL
, *save_dest
= NULL
;
2005 struct stat src_stat
;
2006 gboolean ret_val
= TRUE
;
2008 FileProgressStatus value
;
2010 FileOpTotalContext
*tctx
;
2012 gboolean do_bg
= FALSE
; /* do background operation? */
2015 static gboolean i18n_flag
= FALSE
;
2018 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
2019 op_names
[i
] = Q_ (op_names
[i
]);
2022 #endif /* ENABLE_NLS */
2024 free_linklist (&linklist
);
2025 free_linklist (&dest_dirs
);
2027 /* Update panel contents to avoid actions on deleted files */
2028 if (!panel
->is_panelized
)
2030 update_panels (UP_RELOAD
, UP_KEEPSEL
);
2038 source
= selection (panel
)->fname
;
2039 src_stat
= selection (panel
)->st
;
2042 source
= panel_get_file (panel
, &src_stat
);
2044 if (!strcmp (source
, ".."))
2046 message (D_ERROR
, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
2051 ctx
= file_op_context_new (operation
);
2052 tctx
= file_op_total_context_new ();
2053 gettimeofday (&(tctx
->transfer_start
), (struct timezone
*) NULL
);
2055 /* Show confirmation dialog */
2056 if (operation
!= OP_DELETE
)
2062 /* Forced single operations default to the original name */
2065 else if (get_other_type () == view_listing
)
2066 dest_dir
= other_panel
->cwd
;
2068 dest_dir
= panel
->cwd
;
2070 * Add trailing backslash only when do non-local ops.
2071 * It saves user from occasional file renames (when destination
2074 if (!force_single
&& dest_dir
[0] != '\0' && dest_dir
[strlen (dest_dir
) - 1] != PATH_SEP
)
2076 /* add trailing separator */
2077 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2082 dest_dir_
= g_strdup (dest_dir
);
2084 if (dest_dir_
== NULL
)
2086 file_op_total_context_destroy (tctx
);
2087 file_op_context_destroy (ctx
);
2091 /* Generate confirmation prompt */
2092 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2094 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2095 source
!= NULL
? (void *) source
2096 : (void *) &panel
->marked
, dest_dir_
, &do_bg
);
2101 if (dest
== NULL
|| dest
[0] == '\0')
2103 file_op_total_context_destroy (tctx
);
2104 file_op_context_destroy (ctx
);
2109 else if (confirm_delete
)
2112 char fmd_buf
[BUF_MEDIUM
];
2114 /* Generate confirmation prompt */
2115 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2118 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2121 const int fmd_xlen
= 64;
2122 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2123 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2131 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2135 file_op_total_context_destroy (tctx
);
2136 file_op_context_destroy (ctx
);
2142 filegui_dialog_type_t dialog_type
;
2144 if (operation
== OP_DELETE
)
2145 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2148 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2149 ? FILEGUI_DIALOG_MULTI_ITEM
: FILEGUI_DIALOG_ONE_ITEM
;
2151 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2152 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2155 /* Background also need ctx->ui, but not full */
2157 file_op_context_create_ui_without_init (ctx
, 1, dialog_type
);
2159 file_op_context_create_ui (ctx
, 1, dialog_type
);
2162 #ifdef WITH_BACKGROUND
2163 /* Did the user select to do a background operation? */
2168 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", panel
->cwd
, (char *) NULL
));
2170 message (D_ERROR
, MSG_ERROR
, _(" Sorry, I could not put the job in background "));
2172 /* If we are the parent */
2175 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
2176 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
2177 /* file_op_context_destroy (ctx); */
2181 #endif /* WITH_BACKGROUND */
2183 /* Initialize things */
2184 /* We do not want to trash cache every time file is
2185 created/touched. However, this will make our cache contain
2187 if ((dest
!= NULL
) && (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2188 save_dest
= g_strdup (dest
);
2190 if ((panel
->cwd
[0] != '\0') && (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2191 save_cwd
= g_strdup (panel
->cwd
);
2193 /* Now, let's do the job */
2195 /* This code is only called by the tree and panel code */
2198 /* We now have ETA in all cases */
2200 /* One file: FIXME mc_chdir will take user out of any vfs */
2201 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
) &&
2202 (mc_chdir (PATH_SEP_STR
) < 0))
2208 /* The source and src_stat variables have been initialized before */
2209 #ifdef WITH_FULL_PATHS
2210 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2211 #endif /* WITH_FULL_PATHS */
2213 if (panel_operate_init_totals (operation
, panel
, source_with_path
, ctx
) == FILE_CONT
)
2215 if (operation
== OP_DELETE
)
2217 if (S_ISDIR (src_stat
.st_mode
))
2218 value
= erase_dir (tctx
, ctx
, source_with_path
);
2220 value
= erase_file (tctx
, ctx
, source_with_path
, 1);
2224 temp
= transform_source (ctx
, source_with_path
);
2226 value
= transform_error
;
2229 char *repl_dest
, *temp2
;
2231 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2232 temp2
= concat_dir_and_file (repl_dest
, temp
);
2241 /* we use file_mask_op_follow_links only with OP_COPY */
2242 ctx
->stat_func (source_with_path
, &src_stat
);
2244 if (S_ISDIR (src_stat
.st_mode
))
2245 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, dest
,
2246 TRUE
, FALSE
, FALSE
, NULL
);
2248 value
= copy_file_file (tctx
, ctx
, source_with_path
, dest
);
2252 if (S_ISDIR (src_stat
.st_mode
))
2253 value
= move_dir_dir (tctx
, ctx
, source_with_path
, dest
);
2255 value
= move_file_file (tctx
, ctx
, source_with_path
, dest
);
2259 /* Unknown file operation */
2263 } /* Copy or move operation */
2265 if ((value
== FILE_CONT
) && !force_single
)
2266 unmark_files (panel
);
2273 /* Check destination for copy or move operation */
2274 while (operation
!= OP_DELETE
)
2277 struct stat dst_stat
;
2279 dst_result
= mc_stat (dest
, &dst_stat
);
2281 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2284 if (file_error (_(" Destination \"%s\" must be a directory \n %s "),
2285 dest
) != FILE_RETRY
)
2289 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
)
2291 /* Loop for every file, perform the actual copy operation */
2292 for (i
= 0; i
< panel
->count
; i
++)
2294 if (!panel
->dir
.list
[i
].f
.marked
)
2295 continue; /* Skip the unmarked ones */
2297 source
= panel
->dir
.list
[i
].fname
;
2298 src_stat
= panel
->dir
.list
[i
].st
;
2300 #ifdef WITH_FULL_PATHS
2301 g_free (source_with_path
);
2302 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2303 #endif /* WITH_FULL_PATHS */
2305 if (operation
== OP_DELETE
)
2307 if (S_ISDIR (src_stat
.st_mode
))
2308 value
= erase_dir (tctx
, ctx
, source_with_path
);
2310 value
= erase_file (tctx
, ctx
, source_with_path
, 1);
2314 temp
= transform_source (ctx
, source_with_path
);
2317 value
= transform_error
;
2320 char *temp2
, *temp3
, *repl_dest
;
2322 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2323 temp2
= concat_dir_and_file (repl_dest
, temp
);
2326 temp3
= source_with_path
;
2327 source_with_path
= strutils_shell_unescape (source_with_path
);
2330 temp2
= strutils_shell_unescape (temp2
);
2336 /* we use file_mask_op_follow_links only with OP_COPY */
2337 ctx
->stat_func (source_with_path
, &src_stat
);
2338 if (S_ISDIR (src_stat
.st_mode
))
2339 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, temp2
,
2340 TRUE
, FALSE
, FALSE
, NULL
);
2342 value
= copy_file_file (tctx
, ctx
, source_with_path
, temp2
);
2343 free_linklist (&dest_dirs
);
2347 if (S_ISDIR (src_stat
.st_mode
))
2348 value
= move_dir_dir (tctx
, ctx
, source_with_path
, temp2
);
2350 value
= move_file_file (tctx
, ctx
, source_with_path
, temp2
);
2354 /* Unknown file operation */
2360 } /* Copy or move operation */
2362 if (value
== FILE_ABORT
)
2365 if (value
== FILE_CONT
)
2366 do_file_mark (panel
, i
, 0);
2368 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2372 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
2374 if (operation
!= OP_DELETE
)
2375 file_progress_show (ctx
, 0, 0, "", FALSE
);
2378 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2382 } /* Loop for every file */
2384 } /* Many entries */
2388 if (save_cwd
!= NULL
)
2390 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2394 if (save_dest
!= NULL
)
2396 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2400 free_linklist (&linklist
);
2401 free_linklist (&dest_dirs
);
2402 #ifdef WITH_FULL_PATHS
2403 g_free (source_with_path
);
2404 #endif /* WITH_FULL_PATHS */
2406 g_free (ctx
->dest_mask
);
2407 ctx
->dest_mask
= NULL
;
2409 #ifdef WITH_BACKGROUND
2410 /* Let our parent know we are saying bye bye */
2411 if (we_are_background
)
2413 int cur_pid
= getpid ();
2414 /* Send pid to parent with child context, it is fork and
2415 don't modify real parent ctx */
2417 parent_call ((void *) end_bg_process
, ctx
, 0);
2422 #endif /* WITH_BACKGROUND */
2424 file_op_context_destroy (ctx
);
2425 file_op_total_context_destroy (tctx
);
2432 /* {{{ Query/status report routines */
2434 static FileProgressStatus
2435 real_do_file_error (enum OperationMode mode
, const char *error
)
2440 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2441 result
= query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"), _("&Abort"));
2459 /* Report error with one file */
2461 file_error (const char *format
, const char *file
)
2463 char buf
[BUF_MEDIUM
];
2465 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
2467 return do_file_error (buf
);
2470 /* Report error with two files */
2471 static FileProgressStatus
2472 files_error (const char *format
, const char *file1
, const char *file2
)
2474 char buf
[BUF_MEDIUM
];
2475 char *nfile1
= g_strdup (path_trunc (file1
, 15));
2476 char *nfile2
= g_strdup (path_trunc (file2
, 15));
2478 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
2483 return do_file_error (buf
);
2486 static FileProgressStatus
2487 real_query_recursive (FileOpContext
* ctx
, enum OperationMode mode
, const char *s
)
2491 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
2493 const char *msg
= mode
== Foreground
2494 ? _("\n Directory not empty. \n"
2495 " Delete it recursively? ")
2496 : _("\n Background process: Directory not empty \n" " Delete it recursively? ");
2497 text
= g_strconcat (_(" Delete: "), path_trunc (s
, 30), " ", (char *) NULL
);
2502 ctx
->recursive_result
=
2503 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
2504 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
2506 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2511 switch (ctx
->recursive_result
)
2514 case RECURSIVE_ALWAYS
:
2518 case RECURSIVE_NEVER
:
2521 case RECURSIVE_ABORT
:
2527 #ifdef WITH_BACKGROUND
2528 static FileProgressStatus
2529 do_file_error (const char *str
)
2534 FileProgressStatus (*f
) (enum OperationMode
, const char *);
2536 pntr
.f
= real_do_file_error
;
2538 if (we_are_background
)
2539 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
2541 return real_do_file_error (Foreground
, str
);
2544 static FileProgressStatus
2545 query_recursive (FileOpContext
* ctx
, const char *s
)
2550 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
2552 pntr
.f
= real_query_recursive
;
2554 if (we_are_background
)
2555 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
2557 return real_query_recursive (ctx
, Foreground
, s
);
2560 static FileProgressStatus
2561 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
2562 struct stat
*_d_stat
)
2567 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
2568 struct stat
*, struct stat
*);
2570 pntr
.f
= file_progress_real_query_replace
;
2572 if (we_are_background
)
2573 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
2574 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
2576 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
2580 static FileProgressStatus
2581 do_file_error (const char *str
)
2583 return real_do_file_error (Foreground
, str
);
2586 static FileProgressStatus
2587 query_recursive (FileOpContext
* ctx
, const char *s
)
2589 return real_query_recursive (ctx
, Foreground
, s
);
2592 static FileProgressStatus
2593 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
2594 struct stat
*_d_stat
)
2596 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
2599 #endif /* !WITH_BACKGROUND */
2602 Cause emacs to enter folding mode for this file: