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/strescape.h"
63 #include "lib/strutil.h"
64 #include "lib/vfs/mc-vfs/vfs.h"
70 #include "layout.h" /* rotate_dash() */
73 #include "background.h" /* we_are_background */
75 /* Needed for current_panel, other_panel and WTree */
84 /* Hack: the vfs code should not rely on this */
85 #define WITH_FULL_PATHS 1
87 #define FILEOP_UPDATE_INTERVAL 2
88 #define FILEOP_STALLING_INTERVAL 4
93 * Whether the Midnight Commander tries to provide more
94 * information about copy/move sizes and bytes transfered
95 * at the expense of some speed
97 int file_op_compute_totals
= 1;
99 /* 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
, const char *file2
);
147 static FileProgressStatus transform_error
= FILE_CONT
;
150 transform_source (FileOpContext
* ctx
, const char *source
)
155 s
= g_strdup (source
);
157 /* We remove \n from the filename since regex routines would use \n as an anchor */
158 /* this is just to be allowed to maniupulate file names with \n on it */
159 for (q
= s
; *q
!= '\0'; q
++)
163 fnsource
= (char *) x_basename (s
);
165 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
166 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
)
191 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
193 ino_t ino
= sb
->st_ino
;
194 dev_t dev
= sb
->st_dev
;
195 struct vfs_class
*vfs
= vfs_get_class (path
);
200 if (lp
->ino
== ino
&& lp
->dev
== dev
)
208 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
209 * and a hardlink was succesfully made
212 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
215 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
216 ino_t ino
= pstat
->st_ino
;
217 dev_t dev
= pstat
->st_dev
;
218 struct stat link_stat
;
221 if ((vfs_file_class_flags (src_name
) & VFSF_NOLINKS
) != 0)
224 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
225 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
)
227 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
228 && link_stat
.st_dev
== dev
&& vfs_get_class (lp
->name
) == my_vfs
)
230 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
232 if (vfs_get_class (dst_name
) == vfs_get_class (p
))
234 if (!mc_stat (p
, &link_stat
))
236 if (!mc_link (p
, dst_name
))
241 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
244 lp
= (struct link
*) g_try_malloc (sizeof (struct link
) + strlen (src_name
)
245 + 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 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
284 if (return_status
== FILE_RETRY
)
285 goto retry_src_readlink
;
286 return return_status
;
288 link_target
[len
] = 0;
290 if (ctx
->stable_symlinks
)
291 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\nOption Stable Symlinks will be disabled"));
296 ctx
->stable_symlinks
= FALSE
;
299 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
303 const char *r
= strrchr (src_path
, PATH_SEP
);
307 p
= g_strndup (src_path
, r
- src_path
+ 1);
308 if (g_path_is_absolute (dst_path
))
309 q
= g_strdup (dst_path
);
311 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
312 s
= strrchr (q
, PATH_SEP
);
316 s
= g_strconcat (p
, link_target
, (char *) NULL
);
318 g_strlcpy (link_target
, s
, sizeof (link_target
));
320 s
= diff_two_paths (q
, link_target
);
323 g_strlcpy (link_target
, s
, sizeof (link_target
));
333 if (mc_symlink (link_target
, dst_path
) == 0)
337 * if dst_exists, it is obvious that this had failed.
338 * We can delete the old symlink and try again...
342 if (!mc_unlink (dst_path
))
343 if (mc_symlink (link_target
, dst_path
) == 0)
347 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
348 if (return_status
== FILE_RETRY
)
349 goto retry_dst_symlink
;
350 return return_status
;
353 static FileProgressStatus
354 progress_update_one (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, off_t add
,
355 gboolean is_toplevel_file
)
357 struct timeval tv_current
;
358 static struct timeval tv_start
= { };
360 if (is_toplevel_file
|| ctx
->progress_totals_computed
)
362 tctx
->progress_count
++;
363 tctx
->progress_bytes
+= add
;
365 if (tv_start
.tv_sec
== 0)
367 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
369 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
370 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
372 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
373 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
374 tv_start
.tv_sec
= tv_current
.tv_sec
;
377 return check_progress_buttons (ctx
);
380 /* Status of the destination file */
383 DEST_NONE
= 0, /* Not created */
384 DEST_SHORT
= 1, /* Created, not fully copied */
385 DEST_FULL
= 2 /* Created, fully copied */
388 static FileProgressStatus
389 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
393 const char *head_msg
;
395 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
397 msg
= g_strdup_printf (fmt
, a
, b
);
398 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
402 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
405 #ifdef WITH_BACKGROUND
406 static FileProgressStatus
407 warn_same_file (const char *fmt
, const char *a
, const char *b
)
412 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
413 const char *a
, const char *b
);
415 pntr
.f
= real_warn_same_file
;
417 if (we_are_background
)
418 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
420 return real_warn_same_file (Foreground
, fmt
, a
, b
);
423 static FileProgressStatus
424 warn_same_file (const char *fmt
, const char *a
, const char *b
)
426 return real_warn_same_file (Foreground
, fmt
, a
, b
);
431 copy_file_file_display_progress (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
432 struct timeval tv_current
, struct timeval tv_transfer_start
,
433 off_t file_size
, off_t n_read_total
)
437 /* 1. Update rotating dash after some time */
441 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
445 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
446 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
451 /* 4. Compute BPS rate */
452 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
453 if (ctx
->bps_time
< 1)
455 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
457 /* 5. Compute total ETA and BPS */
458 if (ctx
->progress_bytes
!= 0)
461 tctx
->copyed_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
462 remain_bytes
= ctx
->progress_bytes
- tctx
->copyed_bytes
;
465 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
469 tctx
->bps
= tctx
->copyed_bytes
/ total_secs
;
470 tctx
->eta_secs
= remain_bytes
/ tctx
->bps
;
473 /* broken on lot of little files */
475 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
476 tctx
->eta_secs
= remain_bytes
/ tctx
->bps
;
482 copy_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
483 const char *src_path
, const char *dst_path
)
485 uid_t src_uid
= (uid_t
) - 1;
486 gid_t src_gid
= (gid_t
) - 1;
488 int src_desc
, dest_desc
= -1;
489 int n_read
, n_written
;
490 mode_t src_mode
= 0; /* The mode of the source file */
493 gboolean dst_exists
= FALSE
, appending
= FALSE
;
494 off_t n_read_total
= 0, file_size
= -1;
495 FileProgressStatus return_status
, temp_status
;
496 struct timeval tv_transfer_start
;
497 dest_status_t dst_status
= DEST_NONE
;
499 gboolean is_first_time
= TRUE
;
501 /* FIXME: We should not be using global variables! */
503 return_status
= FILE_RETRY
;
505 file_progress_show_source (ctx
, src_path
);
506 file_progress_show_target (ctx
, dst_path
);
507 if (check_progress_buttons (ctx
) == FILE_ABORT
)
512 while (mc_stat (dst_path
, &sb2
) == 0)
514 if (S_ISDIR (sb2
.st_mode
))
516 return_status
= file_error (_("Cannot overwrite directory\"%s\"\n%s"), dst_path
);
517 if (return_status
== FILE_RETRY
)
519 return return_status
;
525 while ((*ctx
->stat_func
) (src_path
, &sb
))
527 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
528 if (return_status
!= FILE_RETRY
)
529 return return_status
;
534 /* Destination already exists */
535 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
536 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
538 /* Should we replace destination? */
539 if (tctx
->ask_overwrite
)
542 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
543 if (return_status
!= FILE_CONT
)
544 return return_status
;
550 /* Check the hardlinks */
551 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_path
, dst_path
, &sb
) == 1)
553 /* We have made a hardlink - no more processing is necessary */
557 if (S_ISLNK (sb
.st_mode
))
558 return make_symlink (ctx
, src_path
, dst_path
);
560 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
561 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
563 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0)
566 file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
567 if (return_status
== FILE_RETRY
)
569 return return_status
;
573 while (ctx
->preserve_uidgid
&& mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
))
575 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
576 if (temp_status
== FILE_RETRY
)
580 while (ctx
->preserve
&& mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
))
582 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
583 if (temp_status
== FILE_RETRY
)
591 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
593 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0)
595 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
596 if (return_status
== FILE_RETRY
)
599 return return_status
;
602 if (ctx
->do_reget
!= 0)
604 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
606 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
608 ctx
->do_append
= FALSE
;
612 while (mc_fstat (src_desc
, &sb
))
614 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
615 if (return_status
== FILE_RETRY
)
617 ctx
->do_append
= FALSE
;
620 src_mode
= sb
.st_mode
;
623 utb
.actime
= sb
.st_atime
;
624 utb
.modtime
= sb
.st_mtime
;
625 file_size
= sb
.st_size
;
627 open_flags
= O_WRONLY
;
630 if (ctx
->do_append
!= 0)
631 open_flags
|= O_APPEND
;
633 open_flags
|= O_CREAT
| O_TRUNC
;
637 open_flags
|= O_CREAT
| O_EXCL
;
640 while ((dest_desc
= mc_open (dst_path
, open_flags
, src_mode
)) < 0)
645 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
646 if (return_status
== FILE_RETRY
)
648 ctx
->do_append
= FALSE
;
651 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
653 appending
= ctx
->do_append
;
654 ctx
->do_append
= FALSE
;
656 /* Find out the optimal buffer size. */
657 while (mc_fstat (dest_desc
, &sb
))
659 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
660 if (return_status
== FILE_RETRY
)
668 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
669 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
671 file_progress_show (ctx
, 1, 1, "", TRUE
);
672 return_status
= check_progress_buttons (ctx
);
675 if (return_status
!= FILE_CONT
)
679 struct timeval tv_current
, tv_last_update
, tv_last_input
;
680 int secs
, update_secs
;
681 const char *stalled_msg
= "";
683 tv_last_update
= tv_transfer_start
;
690 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
693 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0)
696 file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
697 if (return_status
== FILE_RETRY
)
704 gettimeofday (&tv_current
, NULL
);
709 n_read_total
+= n_read
;
711 /* Windows NT ftp servers report that files have no
712 * permissions: -------, so if we happen to have actually
713 * read something, we should fix the permissions.
715 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
716 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
717 gettimeofday (&tv_last_input
, NULL
);
720 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
)
729 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
730 if (return_status
!= FILE_RETRY
)
734 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
735 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
737 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
739 copy_file_file_display_progress (tctx
, ctx
,
741 tv_transfer_start
, file_size
, n_read_total
);
742 tv_last_update
= tv_current
;
744 is_first_time
= FALSE
;
746 if (update_secs
> FILEOP_STALLING_INTERVAL
)
748 stalled_msg
= _("(stalled)");
752 gboolean force_update
=
753 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
754 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
755 file_progress_show_total (tctx
, ctx
,
756 tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
,
760 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
765 return_status
= check_progress_buttons (ctx
);
767 if (return_status
!= FILE_CONT
)
775 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
778 while (src_desc
!= -1 && mc_close (src_desc
) < 0)
780 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
781 if (temp_status
== FILE_RETRY
)
783 if (temp_status
== FILE_ABORT
)
784 return_status
= temp_status
;
788 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0)
790 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
791 if (temp_status
== FILE_RETRY
)
793 return_status
= temp_status
;
797 if (dst_status
== DEST_SHORT
)
799 /* Remove short file */
801 result
= query_dialog (Q_ ("DialogTitle|Copy"),
802 _("Incomplete file was retrieved. Keep it?"),
803 D_ERROR
, 2, _("&Delete"), _("&Keep"));
805 mc_unlink (dst_path
);
807 else if (dst_status
== DEST_FULL
)
809 /* Copy has succeeded */
810 if (!appending
&& ctx
->preserve_uidgid
)
812 while (mc_chown (dst_path
, src_uid
, src_gid
))
814 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
815 if (temp_status
== FILE_RETRY
)
817 return_status
= temp_status
;
826 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
)))
829 file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
830 if (temp_status
!= FILE_RETRY
)
832 return_status
= temp_status
;
839 src_mode
= umask (-1);
841 src_mode
= 0100666 & ~src_mode
;
842 mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
));
844 mc_utime (dst_path
, &utb
);
848 if (return_status
== FILE_CONT
)
849 return_status
= progress_update_one (tctx
, ctx
, file_size
, tctx
->is_toplevel_file
);
851 return return_status
;
855 * I think these copy_*_* functions should have a return type.
856 * anyway, this function *must* have two directories as arguments.
858 /* FIXME: This function needs to check the return values of the
861 copy_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *_d
,
862 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, struct link
* parent_dirs
)
865 struct stat buf
, cbuf
;
867 char *dest_dir
= NULL
;
868 FileProgressStatus return_status
= FILE_CONT
;
875 /* First get the mode of the source dir */
877 if ((*ctx
->stat_func
) (s
, &cbuf
))
879 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
880 if (return_status
== FILE_RETRY
)
885 if (is_in_linklist (dest_dirs
, s
, &cbuf
))
887 /* Don't copy a directory we created before (we don't want to copy
888 infinitely if a directory is copied into itself) */
889 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
890 return_status
= FILE_CONT
;
894 /* Hmm, hardlink to directory??? - Norbert */
895 /* FIXME: In this step we should do something
896 in case the destination already exist */
897 /* Check the hardlinks */
898 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (s
, d
, &cbuf
) == 1)
900 /* We have made a hardlink - no more processing is necessary */
904 if (!S_ISDIR (cbuf
.st_mode
))
906 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
907 if (return_status
== FILE_RETRY
)
912 if (is_in_linklist (parent_dirs
, s
, &cbuf
))
914 /* we found a cyclic symbolic link */
915 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
916 return_status
= FILE_SKIP
;
920 lp
= g_new (struct link
, 1);
921 lp
->vfs
= vfs_get_class (s
);
922 lp
->ino
= cbuf
.st_ino
;
923 lp
->dev
= cbuf
.st_dev
;
924 lp
->next
= parent_dirs
;
928 /* Now, check if the dest dir exists, if not, create it. */
929 if (mc_stat (d
, &buf
))
931 /* Here the dir doesn't exist : make it ! */
934 if (mc_rename (s
, d
) == 0)
936 return_status
= FILE_CONT
;
946 * If the destination directory exists, we want to copy the whole
947 * directory, but we only want this to happen once.
949 * Escape sequences added to the * to compiler warnings.
950 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
951 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
953 if (!S_ISDIR (buf
.st_mode
))
955 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
956 if (return_status
== FILE_RETRY
)
960 /* Dive into subdir if exists */
961 if (toplevel
&& ctx
->dive_into_subdirs
)
963 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
972 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
))
974 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir
);
975 if (return_status
!= FILE_RETRY
)
979 lp
= g_new (struct link
, 1);
980 mc_stat (dest_dir
, &buf
);
981 lp
->vfs
= vfs_get_class (dest_dir
);
982 lp
->ino
= buf
.st_ino
;
983 lp
->dev
= buf
.st_dev
;
984 lp
->next
= dest_dirs
;
987 if (ctx
->preserve_uidgid
)
989 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
))
992 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir
);
993 if (return_status
!= FILE_RETRY
)
999 /* open the source dir for reading */
1000 reading
= mc_opendir (s
);
1001 if (reading
== NULL
)
1004 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1008 * Now, we don't want '.' and '..' to be created / copied at any time
1010 if (!strcmp (next
->d_name
, "."))
1012 if (!strcmp (next
->d_name
, ".."))
1015 /* get the filename and add it to the src directory */
1016 path
= concat_dir_and_file (s
, next
->d_name
);
1018 (*ctx
->stat_func
) (path
, &buf
);
1019 if (S_ISDIR (buf
.st_mode
))
1023 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
1025 * From here, we just intend to recursively copy subdirs, not
1026 * the double functionality of copying different when the target
1027 * dir already exists. So, we give the recursive call the flag 0
1028 * meaning no toplevel.
1031 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
1038 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
1039 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
1042 if (do_delete
&& return_status
== FILE_CONT
)
1044 if (ctx
->erase_at_end
)
1046 static struct link
*tail
;
1047 size_t len
= strlen (path
);
1048 lp
= g_malloc (sizeof (struct link
) + len
);
1049 strncpy (lp
->name
, path
, len
+ 1);
1050 lp
->st_mode
= buf
.st_mode
;
1052 if (erase_list
!= NULL
)
1058 erase_list
= tail
= lp
;
1062 if (S_ISDIR (buf
.st_mode
))
1064 return_status
= erase_dir_iff_empty (ctx
, path
);
1067 return_status
= erase_file (tctx
, ctx
, path
, FALSE
);
1072 mc_closedir (reading
);
1076 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1077 utb
.actime
= cbuf
.st_atime
;
1078 utb
.modtime
= cbuf
.st_mtime
;
1079 mc_utime (dest_dir
, &utb
);
1083 cbuf
.st_mode
= umask (-1);
1084 umask (cbuf
.st_mode
);
1085 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
1086 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1091 g_free (parent_dirs
);
1094 return return_status
;
1099 /* {{{ Move routines */
1101 static FileProgressStatus
1102 move_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
1104 struct stat src_stats
, dst_stats
;
1105 FileProgressStatus return_status
= FILE_CONT
;
1106 gboolean copy_done
= FALSE
;
1107 gboolean old_ask_overwrite
;
1109 file_progress_show_source (ctx
, s
);
1110 file_progress_show_target (ctx
, d
);
1111 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1116 while (mc_lstat (s
, &src_stats
) != 0)
1118 /* Source doesn't exist */
1119 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
1120 if (return_status
!= FILE_RETRY
)
1121 return return_status
;
1124 if (mc_lstat (d
, &dst_stats
) == 0)
1126 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
1127 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
1129 if (S_ISDIR (dst_stats
.st_mode
))
1131 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
1136 if (confirm_overwrite
)
1138 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1139 if (return_status
!= FILE_CONT
)
1140 return return_status
;
1142 /* Ok to overwrite */
1145 if (!ctx
->do_append
)
1147 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
1149 return_status
= make_symlink (ctx
, s
, d
);
1150 if (return_status
== FILE_CONT
)
1151 goto retry_src_remove
;
1153 return return_status
;
1156 if (mc_rename (s
, d
) == 0)
1157 return progress_update_one (tctx
, ctx
, src_stats
.st_size
, TRUE
);
1160 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1161 one nfs to the same, but on the server it is on two different
1162 filesystems. Then nfs returns EIO instead of EXDEV.
1163 Hope it will not hurt if we always in case of error try to copy/delete. */
1165 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1169 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
1170 if (return_status
== FILE_RETRY
)
1172 return return_status
;
1176 /* Failed because filesystem boundary -> copy the file instead */
1177 old_ask_overwrite
= tctx
->ask_overwrite
;
1178 tctx
->ask_overwrite
= FALSE
;
1179 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
1180 tctx
->ask_overwrite
= old_ask_overwrite
;
1181 if (return_status
!= FILE_CONT
)
1182 return return_status
;
1186 file_progress_show_source (ctx
, NULL
);
1187 file_progress_show (ctx
, 0, 0, "", FALSE
);
1189 return_status
= check_progress_buttons (ctx
);
1190 if (return_status
!= FILE_CONT
)
1191 return return_status
;
1198 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
1199 if (return_status
== FILE_RETRY
)
1200 goto retry_src_remove
;
1201 return return_status
;
1205 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
, TRUE
);
1207 return return_status
;
1211 move_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
1213 struct stat sbuf
, dbuf
, destbuf
;
1216 FileProgressStatus return_status
;
1217 gboolean move_over
= FALSE
;
1220 file_progress_show_source (ctx
, s
);
1221 file_progress_show_target (ctx
, d
);
1222 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1228 dstat_ok
= (mc_stat (d
, &dbuf
) == 0);
1230 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
1231 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
1234 destdir
= g_strdup (d
); /* destination doesn't exist */
1235 else if (!ctx
->dive_into_subdirs
)
1237 destdir
= g_strdup (d
);
1241 destdir
= concat_dir_and_file (d
, x_basename (s
));
1243 /* Check if the user inputted an existing dir */
1245 if (!mc_stat (destdir
, &destbuf
))
1249 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
1251 if (return_status
!= FILE_CONT
)
1257 if (S_ISDIR (destbuf
.st_mode
))
1258 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir
);
1260 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir
);
1261 if (return_status
== FILE_RETRY
)
1262 goto retry_dst_stat
;
1265 return return_status
;
1269 if (mc_rename (s
, destdir
) == 0)
1271 return_status
= FILE_CONT
;
1277 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
1278 if (return_status
== FILE_RETRY
)
1282 /* Failed because of filesystem boundary -> copy dir instead */
1283 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
1285 if (return_status
!= FILE_CONT
)
1288 file_progress_show_source (ctx
, NULL
);
1289 file_progress_show (ctx
, 0, 0, "", FALSE
);
1291 return_status
= check_progress_buttons (ctx
);
1292 if (return_status
!= FILE_CONT
)
1296 if (ctx
->erase_at_end
)
1298 for (; erase_list
&& return_status
!= FILE_ABORT
;)
1300 if (S_ISDIR (erase_list
->st_mode
))
1302 return_status
= erase_dir_iff_empty (ctx
, erase_list
->name
);
1305 return_status
= erase_file (tctx
, ctx
, erase_list
->name
, FALSE
);
1307 erase_list
= erase_list
->next
;
1311 erase_dir_iff_empty (ctx
, s
);
1318 erase_list
= erase_list
->next
;
1321 return return_status
;
1326 /* {{{ Erase routines */
1327 /* Don't update progress status if progress_count==NULL */
1328 static FileProgressStatus
1329 erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
,
1330 gboolean is_toplevel_file
)
1335 file_progress_show_deleting (ctx
, s
);
1336 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1340 if (tctx
->progress_count
&& mc_lstat (s
, &buf
))
1342 /* ignore, most likely the mc_unlink fails, too */
1346 while (mc_unlink (s
))
1348 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), s
);
1349 if (return_status
!= FILE_RETRY
)
1350 return return_status
;
1353 if (tctx
->progress_count
)
1354 return progress_update_one (tctx
, ctx
, buf
.st_size
, is_toplevel_file
);
1359 static FileProgressStatus
1360 recursive_erase (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
1362 struct dirent
*next
;
1366 FileProgressStatus return_status
= FILE_CONT
;
1368 if (!strcmp (s
, ".."))
1371 reading
= mc_opendir (s
);
1376 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
)
1378 if (!strcmp (next
->d_name
, "."))
1380 if (!strcmp (next
->d_name
, ".."))
1382 path
= concat_dir_and_file (s
, next
->d_name
);
1383 if (mc_lstat (path
, &buf
))
1386 mc_closedir (reading
);
1389 if (S_ISDIR (buf
.st_mode
))
1391 (recursive_erase (tctx
, ctx
, path
) != FILE_CONT
) ? FILE_RETRY
: FILE_CONT
;
1393 return_status
= erase_file (tctx
, ctx
, path
, 0);
1396 mc_closedir (reading
);
1397 if (return_status
!= FILE_CONT
)
1398 return return_status
;
1399 file_progress_show_deleting (ctx
, s
);
1400 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1404 while (my_rmdir (s
))
1406 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1407 if (return_status
!= FILE_RETRY
)
1408 return return_status
;
1414 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1415 in the directory path points to, 0 else. */
1417 check_dir_is_empty (const char *path
)
1423 dir
= mc_opendir (path
);
1427 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
))
1429 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1430 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1431 continue; /* "." or ".." */
1441 erase_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
1443 FileProgressStatus error
;
1445 if (strcmp (s
, "..") == 0)
1448 if (strcmp (s
, ".") == 0)
1451 file_progress_show_deleting (ctx
, s
);
1452 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1456 /* The old way to detect a non empty directory was:
1457 error = my_rmdir (s);
1458 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1459 For the linux user space nfs server (nfs-server-2.2beta29-2)
1460 we would have to check also for EIO. I hope the new way is
1461 fool proof. (Norbert)
1463 error
= check_dir_is_empty (s
);
1466 error
= query_recursive (ctx
, s
);
1467 if (error
== FILE_CONT
)
1468 return recursive_erase (tctx
, ctx
, s
);
1473 while (my_rmdir (s
) == -1)
1475 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1476 if (error
!= FILE_RETRY
)
1483 static FileProgressStatus
1484 erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
)
1486 FileProgressStatus error
;
1488 if (strcmp (s
, "..") == 0)
1491 if (strcmp (s
, ".") == 0)
1494 file_progress_show_deleting (ctx
, s
);
1495 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1499 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1502 while (my_rmdir (s
))
1504 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1505 if (error
!= FILE_RETRY
)
1514 /* {{{ Panel operate routines */
1517 * Return currently selected entry name or the name of the first marked
1518 * entry if there is one.
1521 panel_get_file (WPanel
* panel
, struct stat
*stat_buf
)
1525 if (get_current_type () == view_tree
)
1527 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1528 char *tree_name
= tree_selected_name (tree
);
1530 mc_stat (tree_name
, stat_buf
);
1536 for (i
= 0; i
< panel
->count
; i
++)
1537 if (panel
->dir
.list
[i
].f
.marked
)
1539 *stat_buf
= panel
->dir
.list
[i
].st
;
1540 return panel
->dir
.list
[i
].fname
;
1545 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1546 return panel
->dir
.list
[panel
->selected
].fname
;
1548 g_assert_not_reached ();
1554 compute_dir_size_create_ui (void)
1556 ComputeDirSizeUI
*ui
;
1558 const char *b_name
= N_("&Abort");
1564 ui
= g_new (ComputeDirSizeUI
, 1);
1566 ui
->dlg
= create_dlg (TRUE
, 0, 0, 8, COLS
/ 2, dialog_colors
, NULL
,
1567 NULL
, _("Directory scanning"), DLG_CENTER
);
1568 ui
->dirname
= label_new (3, 3, "");
1569 add_widget (ui
->dlg
, ui
->dirname
);
1571 add_widget (ui
->dlg
,
1572 button_new (5, (ui
->dlg
->cols
- strlen (b_name
)) / 2,
1573 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
1575 /* We will manage the dialog without any help,
1576 that's why we have to call init_dlg */
1583 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
1587 /* schedule to update passive panel */
1588 other_panel
->dirty
= 1;
1590 /* close and destroy dialog */
1591 dlg_run_done (ui
->dlg
);
1592 destroy_dlg (ui
->dlg
);
1598 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
1600 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
1607 label_set_text (this->dirname
, name_trunc (dirname
, this->dlg
->cols
- 6));
1609 event
.x
= -1; /* Don't show the GPM cursor */
1610 c
= tty_get_event (&event
, FALSE
, FALSE
);
1614 /* Reinitialize to avoid old values after events other than
1615 selecting a button */
1616 this->dlg
->ret_value
= FILE_CONT
;
1618 dlg_process_event (this->dlg
, c
, &event
);
1620 switch (this->dlg
->ret_value
)
1633 * Computes the number of bytes used by the files in a directory
1636 compute_dir_size (const char *dirname
, const void *ui
,
1637 compute_dir_size_callback cback
,
1638 off_t
* ret_marked
, double *ret_total
, gboolean compute_symlinks
)
1643 struct dirent
*dirent
;
1644 FileProgressStatus ret
= FILE_CONT
;
1646 if (!compute_symlinks
)
1648 res
= mc_lstat (dirname
, &s
);
1652 /* don't scan symlink to directory */
1653 if (S_ISLNK (s
.st_mode
))
1656 *ret_total
+= s
.st_size
;
1661 dir
= mc_opendir (dirname
);
1666 while ((dirent
= mc_readdir (dir
)) != NULL
)
1670 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
1672 if (ret
!= FILE_CONT
)
1675 if (strcmp (dirent
->d_name
, ".") == 0)
1677 if (strcmp (dirent
->d_name
, "..") == 0)
1680 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1681 res
= mc_lstat (fullname
, &s
);
1689 if (S_ISDIR (s
.st_mode
))
1691 off_t subdir_count
= 0;
1692 double subdir_bytes
= 0;
1695 compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
,
1698 if (ret
!= FILE_CONT
)
1704 *ret_marked
+= subdir_count
;
1705 *ret_total
+= subdir_bytes
;
1710 *ret_total
+= s
.st_size
;
1722 * panel_compute_totals:
1724 * compute the number of files and the number of bytes
1725 * used up by the whole selection, recursing directories
1726 * as required. In addition, it checks to see if it will
1727 * overwrite any files by doing the copy.
1729 static FileProgressStatus
1730 panel_compute_totals (const WPanel
* panel
, const void *ui
,
1731 compute_dir_size_callback cback
,
1732 off_t
* ret_marked
, double *ret_total
, gboolean compute_symlinks
)
1739 for (i
= 0; i
< panel
->count
; i
++)
1743 if (!panel
->dir
.list
[i
].f
.marked
)
1746 s
= &panel
->dir
.list
[i
].st
;
1748 if (S_ISDIR (s
->st_mode
))
1751 off_t subdir_count
= 0;
1752 double subdir_bytes
= 0;
1753 FileProgressStatus status
;
1755 dir_name
= concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1757 status
= compute_dir_size (dir_name
, ui
, cback
,
1758 &subdir_count
, &subdir_bytes
, compute_symlinks
);
1761 if (status
!= FILE_CONT
)
1764 *ret_marked
+= subdir_count
;
1765 *ret_total
+= subdir_bytes
;
1770 *ret_total
+= s
->st_size
;
1777 /* Initialize variables for progress bars */
1778 static FileProgressStatus
1779 panel_operate_init_totals (FileOperation operation
,
1780 const WPanel
* panel
, const char *source
, FileOpContext
* ctx
)
1782 FileProgressStatus status
;
1784 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
)
1786 ComputeDirSizeUI
*ui
;
1788 ui
= compute_dir_size_create_ui ();
1791 status
= compute_dir_size (source
, ui
, compute_dir_size_update_ui
,
1792 &ctx
->progress_count
, &ctx
->progress_bytes
,
1795 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1796 &ctx
->progress_count
, &ctx
->progress_bytes
,
1799 compute_dir_size_destroy_ui (ui
);
1801 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1806 ctx
->progress_count
= panel
->marked
;
1807 ctx
->progress_bytes
= panel
->total
;
1808 ctx
->progress_totals_computed
= FALSE
;
1815 * This array introduced to avoid translation problems. The former (op_names)
1816 * is assumed to be nouns, suitable in dialog box titles; this one should
1817 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1818 * (I don't use spaces around the words, because someday they could be
1819 * dropped, when widgets get smarter)
1822 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
1823 static const char *op_names1
[] = {
1824 N_("FileOperation|Copy"),
1825 N_("FileOperation|Move"),
1826 N_("FileOperation|Delete")
1830 * These are formats for building a prompt. Parts encoded as follows:
1831 * %o - operation from op_names1
1832 * %f - file/files or files/directories, as appropriate
1833 * %m - "with source mask" or question mark for delete
1834 * %s - source name (truncated)
1835 * %d - number of marked files
1836 * %e - "to:" or question mark for delete
1838 * xgettext:no-c-format */
1839 static const char *one_format
= N_("%o %f \"%s\"%m");
1840 /* xgettext:no-c-format */
1841 static const char *many_format
= N_("%o %d %f%m");
1843 static const char *prompt_parts
[] = {
1848 N_("files/directories"),
1849 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
1850 N_(" with source mask:"),
1854 static const char *question_format
= N_("%s?");
1857 * Generate user prompt for panel operation.
1858 * single_source is the name if the source entry or NULL for multiple
1860 * src_stat is only used when single_source is not NULL.
1863 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1864 gboolean single_source
, const struct stat
*src_stat
)
1866 const char *sp
, *cp
;
1867 char format_string
[BUF_MEDIUM
];
1868 char *dp
= format_string
;
1869 gboolean build_question
= FALSE
;
1871 static gboolean i18n_flag
= FALSE
;
1876 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1877 op_names1
[i
] = Q_ (op_names1
[i
]);
1880 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1881 prompt_parts
[i
] = _(prompt_parts
[i
]);
1883 one_format
= _(one_format
);
1884 many_format
= _(many_format
);
1885 question_format
= _(question_format
);
1886 #endif /* ENABLE_NLS */
1890 sp
= single_source
? one_format
: many_format
;
1901 cp
= op_names1
[operation
];
1904 if (operation
== OP_DELETE
)
1907 build_question
= TRUE
;
1910 cp
= prompt_parts
[5];
1913 if (operation
== OP_DELETE
)
1916 build_question
= TRUE
;
1919 cp
= prompt_parts
[6];
1923 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1925 cp
= (panel
->marked
== panel
->dirs_marked
)
1927 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1948 char tmp
[BUF_MEDIUM
];
1950 memmove (tmp
, format_string
, sizeof (tmp
));
1951 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1954 return g_strdup (format_string
);
1957 #ifdef WITH_BACKGROUND
1959 end_bg_process (FileOpContext
* ctx
, enum OperationMode mode
)
1966 unregister_task_with_pid (pid
);
1967 /* file_op_context_destroy(ctx); */
1975 * Performs one of the operations on the selection on the source_panel
1976 * (copy, delete, move).
1978 * Returns TRUE if did change the directory
1979 * structure, Returns FALSE if user aborted
1981 * force_single forces operation on the current entry and affects
1982 * default destination. Current filename is used as default.
1985 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
1987 WPanel
*panel
= (WPanel
*) source_panel
;
1988 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
1989 || (get_current_type () == view_tree
);
1991 char *source
= NULL
;
1992 #ifdef WITH_FULL_PATHS
1993 char *source_with_path
= NULL
;
1995 # define source_with_path source
1996 #endif /* !WITH_FULL_PATHS */
1999 char *save_cwd
= NULL
, *save_dest
= NULL
;
2000 struct stat src_stat
;
2001 gboolean ret_val
= TRUE
;
2003 FileProgressStatus value
;
2005 FileOpTotalContext
*tctx
;
2007 gboolean do_bg
= FALSE
; /* do background operation? */
2009 static gboolean i18n_flag
= FALSE
;
2012 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
2013 op_names
[i
] = Q_ (op_names
[i
]);
2017 free_linklist (&linklist
);
2018 free_linklist (&dest_dirs
);
2020 /* Update panel contents to avoid actions on deleted files */
2021 if (!panel
->is_panelized
)
2023 update_panels (UP_RELOAD
, UP_KEEPSEL
);
2031 source
= selection (panel
)->fname
;
2032 src_stat
= selection (panel
)->st
;
2035 source
= panel_get_file (panel
, &src_stat
);
2037 if (!strcmp (source
, ".."))
2039 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2044 ctx
= file_op_context_new (operation
);
2045 tctx
= file_op_total_context_new ();
2046 gettimeofday (&(tctx
->transfer_start
), (struct timezone
*) NULL
);
2048 /* Show confirmation dialog */
2049 if (operation
!= OP_DELETE
)
2055 /* Forced single operations default to the original name */
2058 else if (get_other_type () == view_listing
)
2059 dest_dir
= other_panel
->cwd
;
2061 dest_dir
= panel
->cwd
;
2063 * Add trailing backslash only when do non-local ops.
2064 * It saves user from occasional file renames (when destination
2067 if (!force_single
&& dest_dir
[0] != '\0' && dest_dir
[strlen (dest_dir
) - 1] != PATH_SEP
)
2069 /* add trailing separator */
2070 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2075 dest_dir_
= g_strdup (dest_dir
);
2077 if (dest_dir_
== NULL
)
2079 file_op_total_context_destroy (tctx
);
2080 file_op_context_destroy (ctx
);
2084 /* Generate confirmation prompt */
2085 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2087 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2088 source
!= NULL
? (void *) source
2089 : (void *) &panel
->marked
, dest_dir_
, &do_bg
);
2094 if (dest
== NULL
|| dest
[0] == '\0')
2096 file_op_total_context_destroy (tctx
);
2097 file_op_context_destroy (ctx
);
2102 else if (confirm_delete
)
2105 char fmd_buf
[BUF_MEDIUM
];
2107 /* Generate confirmation prompt */
2108 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2111 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2114 const int fmd_xlen
= 64;
2115 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2116 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2124 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2128 file_op_total_context_destroy (tctx
);
2129 file_op_context_destroy (ctx
);
2135 filegui_dialog_type_t dialog_type
;
2137 if (operation
== OP_DELETE
)
2138 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2141 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2142 ? FILEGUI_DIALOG_MULTI_ITEM
: FILEGUI_DIALOG_ONE_ITEM
;
2144 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2145 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2148 /* Background also need ctx->ui, but not full */
2150 file_op_context_create_ui_without_init (ctx
, 1, dialog_type
);
2152 file_op_context_create_ui (ctx
, 1, dialog_type
);
2155 #ifdef WITH_BACKGROUND
2156 /* Did the user select to do a background operation? */
2161 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", panel
->cwd
, (char *) NULL
));
2163 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2165 /* If we are the parent */
2168 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
2169 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
2170 /* file_op_context_destroy (ctx); */
2174 #endif /* WITH_BACKGROUND */
2176 /* Initialize things */
2177 /* We do not want to trash cache every time file is
2178 created/touched. However, this will make our cache contain
2180 if ((dest
!= NULL
) && (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2181 save_dest
= g_strdup (dest
);
2183 if ((panel
->cwd
[0] != '\0') && (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2184 save_cwd
= g_strdup (panel
->cwd
);
2186 /* Now, let's do the job */
2188 /* This code is only called by the tree and panel code */
2191 /* We now have ETA in all cases */
2193 /* One file: FIXME mc_chdir will take user out of any vfs */
2194 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
) &&
2195 (mc_chdir (PATH_SEP_STR
) < 0))
2201 /* The source and src_stat variables have been initialized before */
2202 #ifdef WITH_FULL_PATHS
2203 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2204 #endif /* WITH_FULL_PATHS */
2206 if (panel_operate_init_totals (operation
, panel
, source_with_path
, ctx
) == FILE_CONT
)
2208 if (operation
== OP_DELETE
)
2210 if (S_ISDIR (src_stat
.st_mode
))
2211 value
= erase_dir (tctx
, ctx
, source_with_path
);
2213 value
= erase_file (tctx
, ctx
, source_with_path
, 1);
2217 temp
= transform_source (ctx
, source_with_path
);
2219 value
= transform_error
;
2222 char *repl_dest
, *temp2
;
2224 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2225 temp2
= concat_dir_and_file (repl_dest
, temp
);
2234 /* we use file_mask_op_follow_links only with OP_COPY */
2235 ctx
->stat_func (source_with_path
, &src_stat
);
2237 if (S_ISDIR (src_stat
.st_mode
))
2238 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, dest
,
2239 TRUE
, FALSE
, FALSE
, NULL
);
2241 value
= copy_file_file (tctx
, ctx
, source_with_path
, dest
);
2245 if (S_ISDIR (src_stat
.st_mode
))
2246 value
= move_dir_dir (tctx
, ctx
, source_with_path
, dest
);
2248 value
= move_file_file (tctx
, ctx
, source_with_path
, dest
);
2252 /* Unknown file operation */
2256 } /* Copy or move operation */
2258 if ((value
== FILE_CONT
) && !force_single
)
2259 unmark_files (panel
);
2266 /* Check destination for copy or move operation */
2267 while (operation
!= OP_DELETE
)
2270 struct stat dst_stat
;
2272 dst_result
= mc_stat (dest
, &dst_stat
);
2274 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2277 if (file_error (_("Destination \"%s\" must be a directory\n%s"),
2278 dest
) != FILE_RETRY
)
2282 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
)
2284 /* Loop for every file, perform the actual copy operation */
2285 for (i
= 0; i
< panel
->count
; i
++)
2287 if (!panel
->dir
.list
[i
].f
.marked
)
2288 continue; /* Skip the unmarked ones */
2290 source
= panel
->dir
.list
[i
].fname
;
2291 src_stat
= panel
->dir
.list
[i
].st
;
2293 #ifdef WITH_FULL_PATHS
2294 g_free (source_with_path
);
2295 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2296 #endif /* WITH_FULL_PATHS */
2298 if (operation
== OP_DELETE
)
2300 if (S_ISDIR (src_stat
.st_mode
))
2301 value
= erase_dir (tctx
, ctx
, source_with_path
);
2303 value
= erase_file (tctx
, ctx
, source_with_path
, 1);
2307 temp
= transform_source (ctx
, source_with_path
);
2310 value
= transform_error
;
2313 char *temp2
, *temp3
, *repl_dest
;
2315 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2316 temp2
= concat_dir_and_file (repl_dest
, temp
);
2319 temp3
= source_with_path
;
2320 source_with_path
= strutils_shell_unescape (source_with_path
);
2323 temp2
= strutils_shell_unescape (temp2
);
2329 /* we use file_mask_op_follow_links only with OP_COPY */
2330 ctx
->stat_func (source_with_path
, &src_stat
);
2331 if (S_ISDIR (src_stat
.st_mode
))
2332 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, temp2
,
2333 TRUE
, FALSE
, FALSE
, NULL
);
2335 value
= copy_file_file (tctx
, ctx
, source_with_path
, temp2
);
2336 free_linklist (&dest_dirs
);
2340 if (S_ISDIR (src_stat
.st_mode
))
2341 value
= move_dir_dir (tctx
, ctx
, source_with_path
, temp2
);
2343 value
= move_file_file (tctx
, ctx
, source_with_path
, temp2
);
2347 /* Unknown file operation */
2353 } /* Copy or move operation */
2355 if (value
== FILE_ABORT
)
2358 if (value
== FILE_CONT
)
2359 do_file_mark (panel
, i
, 0);
2361 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2365 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
2367 if (operation
!= OP_DELETE
)
2368 file_progress_show (ctx
, 0, 0, "", FALSE
);
2371 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2375 } /* Loop for every file */
2377 } /* Many entries */
2381 if (save_cwd
!= NULL
)
2383 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2387 if (save_dest
!= NULL
)
2389 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2393 free_linklist (&linklist
);
2394 free_linklist (&dest_dirs
);
2395 #ifdef WITH_FULL_PATHS
2396 g_free (source_with_path
);
2397 #endif /* WITH_FULL_PATHS */
2399 g_free (ctx
->dest_mask
);
2400 ctx
->dest_mask
= NULL
;
2402 #ifdef WITH_BACKGROUND
2403 /* Let our parent know we are saying bye bye */
2404 if (we_are_background
)
2406 int cur_pid
= getpid ();
2407 /* Send pid to parent with child context, it is fork and
2408 don't modify real parent ctx */
2410 parent_call ((void *) end_bg_process
, ctx
, 0);
2415 #endif /* WITH_BACKGROUND */
2417 file_op_context_destroy (ctx
);
2418 file_op_total_context_destroy (tctx
);
2425 /* {{{ Query/status report routines */
2427 static FileProgressStatus
2428 real_do_file_error (enum OperationMode mode
, const char *error
)
2433 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
2434 result
= query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"), _("&Abort"));
2452 /* Report error with one file */
2454 file_error (const char *format
, const char *file
)
2456 char buf
[BUF_MEDIUM
];
2458 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
2460 return do_file_error (buf
);
2463 /* Report error with two files */
2464 static FileProgressStatus
2465 files_error (const char *format
, const char *file1
, const char *file2
)
2467 char buf
[BUF_MEDIUM
];
2468 char *nfile1
= g_strdup (path_trunc (file1
, 15));
2469 char *nfile2
= g_strdup (path_trunc (file2
, 15));
2471 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
2476 return do_file_error (buf
);
2479 static FileProgressStatus
2480 real_query_recursive (FileOpContext
* ctx
, enum OperationMode mode
, const char *s
)
2484 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
2486 const char *msg
= mode
== Foreground
2487 ? _("\nDirectory not empty.\nDelete it recursively?")
2488 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
2489 text
= g_strconcat (_("Delete:"), " ", path_trunc (s
, 30), (char *) NULL
);
2494 ctx
->recursive_result
=
2495 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
2496 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
2498 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2503 switch (ctx
->recursive_result
)
2506 case RECURSIVE_ALWAYS
:
2510 case RECURSIVE_NEVER
:
2513 case RECURSIVE_ABORT
:
2519 #ifdef WITH_BACKGROUND
2520 static FileProgressStatus
2521 do_file_error (const char *str
)
2526 FileProgressStatus (*f
) (enum OperationMode
, const char *);
2528 pntr
.f
= real_do_file_error
;
2530 if (we_are_background
)
2531 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
2533 return real_do_file_error (Foreground
, str
);
2536 static FileProgressStatus
2537 query_recursive (FileOpContext
* ctx
, const char *s
)
2542 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
2544 pntr
.f
= real_query_recursive
;
2546 if (we_are_background
)
2547 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
2549 return real_query_recursive (ctx
, Foreground
, s
);
2552 static FileProgressStatus
2553 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
2554 struct stat
*_d_stat
)
2559 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
2560 struct stat
*, struct stat
*);
2562 pntr
.f
= file_progress_real_query_replace
;
2564 if (we_are_background
)
2565 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
2566 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
2568 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
2572 static FileProgressStatus
2573 do_file_error (const char *str
)
2575 return real_do_file_error (Foreground
, str
);
2578 static FileProgressStatus
2579 query_recursive (FileOpContext
* ctx
, const char *s
)
2581 return real_query_recursive (ctx
, Foreground
, s
);
2584 static FileProgressStatus
2585 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
2586 struct stat
*_d_stat
)
2588 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
2591 #endif /* !WITH_BACKGROUND */
2594 Cause emacs to enter folding mode for this file: