4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011, 2012, 2013
6 The Free Software Foundation, Inc.
9 Janne Kukonlehto, 1994, 1995
10 Fred Leeflang, 1994, 1995
11 Miguel de Icaza, 1994, 1995, 1996
12 Jakub Jelinek, 1995, 1996
15 Andrew Borodin <aborodin@vmail.ru>, 2011, 2012, 2013
17 The copy code was based in GNU's cp, and was written by:
18 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
20 The move code was based in GNU's mv, and was written by:
21 Mike Parker and David MacKenzie.
23 Janne Kukonlehto added much error recovery to them for being used
24 in an interactive program.
26 This file is part of the Midnight Commander.
28 The Midnight Commander is free software: you can redistribute it
29 and/or modify it under the terms of the GNU General Public License as
30 published by the Free Software Foundation, either version 3 of the License,
31 or (at your option) any later version.
33 The Midnight Commander is distributed in the hope that it will be useful,
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 GNU General Public License for more details.
38 You should have received a copy of the GNU General Public License
39 along with this program. If not, see <http://www.gnu.org/licenses/>.
43 * Please note that all dialogs used here must be safe for background
47 /** \file src/filemanager/file.c
48 * \brief Source: file management
51 /* {{{ Include files */
60 #include <sys/types.h>
65 #include "lib/global.h"
66 #include "lib/tty/tty.h"
67 #include "lib/tty/key.h"
68 #include "lib/search.h"
69 #include "lib/strescape.h"
70 #include "lib/strutil.h"
72 #include "lib/vfs/vfs.h"
73 #include "lib/widget.h"
75 #include "src/setup.h"
76 #ifdef ENABLE_BACKGROUND
77 #include "src/background.h" /* do_background() */
80 /* Needed for current_panel, other_panel and WTree */
85 #include "midnight.h" /* current_panel */
86 #include "layout.h" /* rotate_dash() */
92 /*** global variables ****************************************************************************/
94 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
95 const char *op_names
[3] = {
96 N_("DialogTitle|Copy"),
97 N_("DialogTitle|Move"),
98 N_("DialogTitle|Delete")
101 /*** file scope macro definitions ****************************************************************/
103 /* Hack: the vfs code should not rely on this */
104 #define WITH_FULL_PATHS 1
106 #define FILEOP_UPDATE_INTERVAL 2
107 #define FILEOP_STALLING_INTERVAL 4
109 /*** file scope type declarations ****************************************************************/
111 /* This is a hard link cache */
114 const struct vfs_class
*vfs
;
119 vfs_path_t
*src_vpath
;
120 vfs_path_t
*dst_vpath
;
123 /* Status of the destination file */
126 DEST_NONE
= 0, /* Not created */
127 DEST_SHORT
= 1, /* Created, not fully copied */
128 DEST_FULL
= 2 /* Created, fully copied */
132 * This array introduced to avoid translation problems. The former (op_names)
133 * is assumed to be nouns, suitable in dialog box titles; this one should
134 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
135 * (I don't use spaces around the words, because someday they could be
136 * dropped, when widgets get smarter)
139 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
140 static const char *op_names1
[] = {
141 N_("FileOperation|Copy"),
142 N_("FileOperation|Move"),
143 N_("FileOperation|Delete")
147 * These are formats for building a prompt. Parts encoded as follows:
148 * %o - operation from op_names1
149 * %f - file/files or files/directories, as appropriate
150 * %m - "with source mask" or question mark for delete
151 * %s - source name (truncated)
152 * %d - number of marked files
153 * %n - the '\n' symbol to form two-line prompt for delete or space for other operations
155 /* xgettext:no-c-format */
156 static const char *one_format
= N_("%o %f%n\"%s\"%m");
157 /* xgettext:no-c-format */
158 static const char *many_format
= N_("%o %d %f%m");
160 static const char *prompt_parts
[] = {
165 N_("files/directories"),
166 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
167 N_(" with source mask:")
170 /*** file scope variables ************************************************************************/
172 /* the hard link cache */
173 static GSList
*linklist
= NULL
;
175 /* the files-to-be-erased list */
176 static GSList
*erase_list
= NULL
;
179 * In copy_dir_dir we use two additional single linked lists: The first -
180 * variable name 'parent_dirs' - holds information about already copied
181 * directories and is used to detect cyclic symbolic links.
182 * The second ('dest_dirs' below) holds information about just created
183 * target directories and is used to detect when an directory is copied
184 * into itself (we don't want to copy infinitly).
185 * Both lists don't use the linkcount and name structure members of struct
188 static GSList
*dest_dirs
= NULL
;
190 static FileProgressStatus transform_error
= FILE_CONT
;
192 /*** file scope functions ************************************************************************/
193 /* --------------------------------------------------------------------------------------------- */
196 transform_source (file_op_context_t
* ctx
, const vfs_path_t
* source_vpath
)
201 s
= g_strdup (vfs_path_as_str (source_vpath
));
203 /* We remove \n from the filename since regex routines would use \n as an anchor */
204 /* this is just to be allowed to maniupulate file names with \n on it */
205 for (q
= s
; *q
!= '\0'; q
++)
209 fnsource
= (char *) x_basename (s
);
211 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
213 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
214 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
216 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
218 transform_error
= FILE_ABORT
;
224 transform_error
= FILE_SKIP
;
231 /* --------------------------------------------------------------------------------------------- */
234 free_link (void *data
)
236 struct link
*lp
= (struct link
*) data
;
238 vfs_path_free (lp
->src_vpath
);
239 vfs_path_free (lp
->dst_vpath
);
243 /* --------------------------------------------------------------------------------------------- */
246 free_linklist (GSList
* lp
)
248 g_slist_free_full (lp
, free_link
);
253 /* --------------------------------------------------------------------------------------------- */
256 is_in_linklist (const GSList
* lp
, const vfs_path_t
* vpath
, const struct stat
*sb
)
258 const struct vfs_class
*class;
259 ino_t ino
= sb
->st_ino
;
260 dev_t dev
= sb
->st_dev
;
262 class = vfs_path_get_last_path_vfs (vpath
);
264 for (; lp
!= NULL
; lp
= g_slist_next (lp
))
266 const struct link
*lnk
= (const struct link
*) lp
->data
;
268 if (lnk
->vfs
== class && lnk
->ino
== ino
&& lnk
->dev
== dev
)
274 /* --------------------------------------------------------------------------------------------- */
276 * Check and made hardlink
278 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
279 * and a hardlink was successfully made
283 check_hardlinks (const vfs_path_t
* src_vpath
, const vfs_path_t
* dst_vpath
, struct stat
*pstat
)
288 const struct vfs_class
*my_vfs
;
289 ino_t ino
= pstat
->st_ino
;
290 dev_t dev
= pstat
->st_dev
;
291 struct stat link_stat
;
293 if ((vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
296 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
298 for (lp
= linklist
; lp
!= NULL
; lp
= g_slist_next (lp
))
300 lnk
= (struct link
*) lp
->data
;
302 if (lnk
->vfs
== my_vfs
&& lnk
->ino
== ino
&& lnk
->dev
== dev
)
304 const struct vfs_class
*lp_name_class
;
307 lp_name_class
= vfs_path_get_last_path_vfs (lnk
->src_vpath
);
308 stat_result
= mc_stat (lnk
->src_vpath
, &link_stat
);
310 if (stat_result
== 0 && link_stat
.st_ino
== ino
311 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
313 const struct vfs_class
*p_class
, *dst_name_class
;
315 dst_name_class
= vfs_path_get_last_path_vfs (dst_vpath
);
316 p_class
= vfs_path_get_last_path_vfs (lnk
->dst_vpath
);
318 if (dst_name_class
== p_class
&&
319 mc_stat (lnk
->dst_vpath
, &link_stat
) == 0 &&
320 mc_link (lnk
->dst_vpath
, dst_vpath
) == 0)
324 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
329 lnk
= g_new0 (struct link
, 1);
335 lnk
->src_vpath
= vfs_path_clone (src_vpath
);
336 lnk
->dst_vpath
= vfs_path_clone (dst_vpath
);
337 linklist
= g_slist_prepend (linklist
, lnk
);
343 /* --------------------------------------------------------------------------------------------- */
345 * Duplicate the contents of the symbolic link src_path in dst_path.
346 * Try to make a stable symlink if the option "stable symlink" was
347 * set in the file mask dialog.
348 * If dst_path is an existing symlink it will be deleted silently
349 * (upper levels take already care of existing files at dst_path).
352 static FileProgressStatus
353 make_symlink (file_op_context_t
* ctx
, const char *src_path
, const char *dst_path
)
355 char link_target
[MC_MAXPATHLEN
];
357 FileProgressStatus return_status
;
359 vfs_path_t
*src_vpath
;
360 vfs_path_t
*dst_vpath
;
361 gboolean dst_is_symlink
;
362 vfs_path_t
*link_target_vpath
= NULL
;
364 src_vpath
= vfs_path_from_str (src_path
);
365 dst_vpath
= vfs_path_from_str (dst_path
);
366 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
369 len
= mc_readlink (src_vpath
, link_target
, MC_MAXPATHLEN
- 1);
373 return_status
= FILE_SKIPALL
;
376 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
377 if (return_status
== FILE_SKIPALL
)
378 ctx
->skip_all
= TRUE
;
379 if (return_status
== FILE_RETRY
)
380 goto retry_src_readlink
;
384 link_target
[len
] = 0;
386 if (ctx
->stable_symlinks
)
389 if (!vfs_file_is_local (src_vpath
) || !vfs_file_is_local (dst_vpath
))
391 message (D_ERROR
, MSG_ERROR
,
392 _("Cannot make stable symlinks across"
393 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
394 ctx
->stable_symlinks
= FALSE
;
398 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
400 const char *r
= strrchr (src_path
, PATH_SEP
);
407 p
= g_strndup (src_path
, r
- src_path
+ 1);
408 if (g_path_is_absolute (dst_path
))
409 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
411 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
413 if (vfs_path_tokens_count (q
) > 1)
416 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
418 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
419 s
= g_strconcat (p
, link_target
, (char *) NULL
);
421 g_strlcpy (link_target
, s
, sizeof (link_target
));
423 tmp_vpath2
= vfs_path_from_str (link_target
);
424 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
425 vfs_path_free (tmp_vpath1
);
426 vfs_path_free (tmp_vpath2
);
429 g_strlcpy (link_target
, s
, sizeof (link_target
));
438 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
441 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
444 return_status
= FILE_CONT
;
448 * if dst_exists, it is obvious that this had failed.
449 * We can delete the old symlink and try again...
453 if (mc_unlink (dst_vpath
) == 0)
454 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
457 return_status
= FILE_CONT
;
462 return_status
= FILE_SKIPALL
;
465 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
466 if (return_status
== FILE_SKIPALL
)
467 ctx
->skip_all
= TRUE
;
468 if (return_status
== FILE_RETRY
)
469 goto retry_dst_symlink
;
473 vfs_path_free (src_vpath
);
474 vfs_path_free (dst_vpath
);
475 vfs_path_free (link_target_vpath
);
476 return return_status
;
479 /* --------------------------------------------------------------------------------------------- */
481 * do_compute_dir_size:
483 * Computes the number of bytes used by the files in a directory
486 static FileProgressStatus
487 do_compute_dir_size (const vfs_path_t
* dirname_vpath
, void *ui
,
488 compute_dir_size_callback cback
, size_t * dir_count
, size_t * ret_marked
,
489 uintmax_t * ret_total
, gboolean compute_symlinks
)
491 static unsigned short int update_ui_count
= 0;
496 struct dirent
*dirent
;
497 FileProgressStatus ret
= FILE_CONT
;
499 if (!compute_symlinks
)
501 res
= mc_lstat (dirname_vpath
, &s
);
505 /* don't scan symlink to directory */
506 if (S_ISLNK (s
.st_mode
))
509 *ret_total
+= (uintmax_t) s
.st_size
;
516 dir
= mc_opendir (dirname_vpath
);
520 while (ret
== FILE_CONT
&& (dirent
= mc_readdir (dir
)) != NULL
)
522 vfs_path_t
*tmp_vpath
;
524 if (DIR_IS_DOT (dirent
->d_name
) || DIR_IS_DOTDOT (dirent
->d_name
))
527 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, NULL
);
529 res
= mc_lstat (tmp_vpath
, &s
);
532 if (S_ISDIR (s
.st_mode
))
535 do_compute_dir_size (tmp_vpath
, ui
, cback
, dir_count
, ret_marked
, ret_total
,
537 if (ret
== FILE_CONT
)
539 (cback
== NULL
) ? FILE_CONT
: cback (ui
, tmp_vpath
, *dir_count
, *ret_total
);
544 *ret_total
+= (uintmax_t) s
.st_size
;
547 if ((update_ui_count
& 31) == 0)
549 (cback
== NULL
) ? FILE_CONT
: cback (ui
, dirname_vpath
, *dir_count
,
554 vfs_path_free (tmp_vpath
);
561 /* --------------------------------------------------------------------------------------------- */
563 static FileProgressStatus
564 progress_update_one (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, off_t add
)
566 struct timeval tv_current
;
567 static struct timeval tv_start
= { };
569 tctx
->progress_count
++;
570 tctx
->progress_bytes
+= (uintmax_t) add
;
572 if (tv_start
.tv_sec
== 0)
574 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
576 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
577 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
579 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
581 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
582 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
584 tv_start
.tv_sec
= tv_current
.tv_sec
;
587 return check_progress_buttons (ctx
);
590 /* --------------------------------------------------------------------------------------------- */
592 static FileProgressStatus
593 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
597 const char *head_msg
;
599 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
601 msg
= g_strdup_printf (fmt
, a
, b
);
602 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
606 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
609 /* --------------------------------------------------------------------------------------------- */
611 static FileProgressStatus
612 warn_same_file (const char *fmt
, const char *a
, const char *b
)
614 #ifdef ENABLE_BACKGROUND
619 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
, const char *a
, const char *b
);
623 pntr
.f
= real_warn_same_file
;
625 if (mc_global
.we_are_background
)
626 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
628 return real_warn_same_file (Foreground
, fmt
, a
, b
);
631 /* --------------------------------------------------------------------------------------------- */
632 /* {{{ Query/status report routines */
634 static FileProgressStatus
635 real_do_file_error (enum OperationMode mode
, const char *error
)
640 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
642 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
664 /* --------------------------------------------------------------------------------------------- */
666 static FileProgressStatus
667 real_query_recursive (file_op_context_t
* ctx
, enum OperationMode mode
, const char *s
)
669 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
674 msg
= mode
== Foreground
675 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
676 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
677 text
= g_strdup_printf (msg
, path_trunc (s
, 30));
682 ctx
->recursive_result
=
683 (FileCopyMode
) query_dialog (op_names
[OP_DELETE
], text
, D_ERROR
, 5,
684 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
687 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
691 switch (ctx
->recursive_result
)
694 case RECURSIVE_ALWAYS
:
698 case RECURSIVE_NEVER
:
701 case RECURSIVE_ABORT
:
707 /* --------------------------------------------------------------------------------------------- */
709 #ifdef ENABLE_BACKGROUND
710 static FileProgressStatus
711 do_file_error (const char *str
)
717 FileProgressStatus (*f
) (enum OperationMode
, const char *);
721 pntr
.f
= real_do_file_error
;
723 if (mc_global
.we_are_background
)
724 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
726 return real_do_file_error (Foreground
, str
);
729 /* --------------------------------------------------------------------------------------------- */
731 static FileProgressStatus
732 query_recursive (file_op_context_t
* ctx
, const char *s
)
738 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *);
742 pntr
.f
= real_query_recursive
;
744 if (mc_global
.we_are_background
)
745 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
747 return real_query_recursive (ctx
, Foreground
, s
);
750 /* --------------------------------------------------------------------------------------------- */
752 static FileProgressStatus
753 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
754 struct stat
*_d_stat
)
760 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *,
761 struct stat
*, struct stat
*);
765 pntr
.f
= file_progress_real_query_replace
;
767 if (mc_global
.we_are_background
)
768 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
769 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
771 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
775 /* --------------------------------------------------------------------------------------------- */
777 static FileProgressStatus
778 do_file_error (const char *str
)
780 return real_do_file_error (Foreground
, str
);
783 /* --------------------------------------------------------------------------------------------- */
785 static FileProgressStatus
786 query_recursive (file_op_context_t
* ctx
, const char *s
)
788 return real_query_recursive (ctx
, Foreground
, s
);
791 /* --------------------------------------------------------------------------------------------- */
793 static FileProgressStatus
794 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
795 struct stat
*_d_stat
)
797 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
800 #endif /* !ENABLE_BACKGROUND */
802 /* --------------------------------------------------------------------------------------------- */
803 /** Report error with two files */
805 static FileProgressStatus
806 files_error (const char *format
, const char *file1
, const char *file2
)
808 char buf
[BUF_MEDIUM
];
809 char *nfile1
= g_strdup (path_trunc (file1
, 15));
810 char *nfile2
= g_strdup (path_trunc (file2
, 15));
812 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
817 return do_file_error (buf
);
822 /* --------------------------------------------------------------------------------------------- */
825 copy_file_file_display_progress (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
,
826 struct timeval tv_current
, struct timeval tv_transfer_start
,
827 off_t file_size
, off_t n_read_total
)
831 /* 1. Update rotating dash after some time */
835 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
837 if (n_read_total
== 0)
841 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
842 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
845 /* 4. Compute BPS rate */
846 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
847 if (ctx
->bps_time
< 1)
849 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
851 /* 5. Compute total ETA and BPS */
852 if (ctx
->progress_bytes
!= 0)
854 uintmax_t remain_bytes
;
856 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
859 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
864 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
865 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
868 /* broken on lot of little files */
870 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
871 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
876 /* --------------------------------------------------------------------------------------------- */
878 /* {{{ Move routines */
879 static FileProgressStatus
880 move_file_file (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
)
882 struct stat src_stats
, dst_stats
;
883 FileProgressStatus return_status
= FILE_CONT
;
884 gboolean copy_done
= FALSE
;
885 gboolean old_ask_overwrite
;
886 vfs_path_t
*src_vpath
, *dst_vpath
;
888 src_vpath
= vfs_path_from_str (s
);
889 dst_vpath
= vfs_path_from_str (d
);
891 file_progress_show_source (ctx
, src_vpath
);
892 file_progress_show_target (ctx
, dst_vpath
);
894 if (check_progress_buttons (ctx
) == FILE_ABORT
)
896 return_status
= FILE_ABORT
;
902 while (mc_lstat (src_vpath
, &src_stats
) != 0)
904 /* Source doesn't exist */
906 return_status
= FILE_SKIPALL
;
909 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
910 if (return_status
== FILE_SKIPALL
)
911 ctx
->skip_all
= TRUE
;
914 if (return_status
!= FILE_RETRY
)
918 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
920 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
922 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
926 if (S_ISDIR (dst_stats
.st_mode
))
928 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
930 return_status
= FILE_SKIP
;
934 if (confirm_overwrite
)
936 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
937 if (return_status
!= FILE_CONT
)
940 /* Ok to overwrite */
945 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
947 return_status
= make_symlink (ctx
, s
, d
);
948 if (return_status
== FILE_CONT
)
949 goto retry_src_remove
;
953 if (mc_rename (src_vpath
, dst_vpath
) == 0)
955 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
960 /* Comparison to EXDEV seems not to work in nfs if you're moving from
961 one nfs to the same, but on the server it is on two different
962 filesystems. Then nfs returns EIO instead of EXDEV.
963 Hope it will not hurt if we always in case of error try to copy/delete. */
965 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
970 return_status
= FILE_SKIPALL
;
973 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
974 if (return_status
== FILE_SKIPALL
)
975 ctx
->skip_all
= TRUE
;
976 if (return_status
== FILE_RETRY
)
984 /* Failed because filesystem boundary -> copy the file instead */
985 old_ask_overwrite
= tctx
->ask_overwrite
;
986 tctx
->ask_overwrite
= FALSE
;
987 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
988 tctx
->ask_overwrite
= old_ask_overwrite
;
989 if (return_status
!= FILE_CONT
)
994 file_progress_show_source (ctx
, NULL
);
995 file_progress_show (ctx
, 0, 0, "", FALSE
);
997 return_status
= check_progress_buttons (ctx
);
998 if (return_status
!= FILE_CONT
)
1003 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
1005 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
1006 if (return_status
== FILE_RETRY
)
1007 goto retry_src_remove
;
1008 if (return_status
== FILE_SKIPALL
)
1009 ctx
->skip_all
= TRUE
;
1014 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
1017 vfs_path_free (src_vpath
);
1018 vfs_path_free (dst_vpath
);
1020 return return_status
;
1025 /* --------------------------------------------------------------------------------------------- */
1026 /* {{{ Erase routines */
1027 /** Don't update progress status if progress_count==NULL */
1029 static FileProgressStatus
1030 erase_file (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1034 file_progress_show_deleting (ctx
, vfs_path_as_str (vpath
), &tctx
->progress_count
);
1035 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1036 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1041 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
1043 /* ignore, most likely the mc_unlink fails, too */
1047 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
1051 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), vfs_path_as_str (vpath
));
1052 if (return_status
== FILE_ABORT
)
1053 return return_status
;
1054 if (return_status
== FILE_RETRY
)
1056 if (return_status
== FILE_SKIPALL
)
1057 ctx
->skip_all
= TRUE
;
1061 if (tctx
->progress_count
== 0)
1064 return check_progress_buttons (ctx
);
1067 /* --------------------------------------------------------------------------------------------- */
1070 Recursive remove of files
1072 skip ->warn every level, gets default
1073 skipall->remove as much as possible
1075 static FileProgressStatus
1076 recursive_erase (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1078 struct dirent
*next
;
1081 FileProgressStatus return_status
= FILE_CONT
;
1083 reading
= mc_opendir (vpath
);
1084 if (reading
== NULL
)
1087 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1089 vfs_path_t
*tmp_vpath
;
1092 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
1095 tmp_vpath
= vfs_path_append_new (vpath
, next
->d_name
, NULL
);
1096 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1098 mc_closedir (reading
);
1099 vfs_path_free (tmp_vpath
);
1102 if (S_ISDIR (buf
.st_mode
))
1103 return_status
= recursive_erase (tctx
, ctx
, tmp_vpath
);
1105 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
1106 vfs_path_free (tmp_vpath
);
1108 mc_closedir (reading
);
1110 if (return_status
== FILE_ABORT
)
1113 s
= vfs_path_as_str (vpath
);
1115 file_progress_show_deleting (ctx
, s
, NULL
);
1116 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1117 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1122 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1124 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1125 if (return_status
== FILE_RETRY
)
1127 if (return_status
== FILE_ABORT
)
1129 if (return_status
== FILE_SKIPALL
)
1130 ctx
->skip_all
= TRUE
;
1134 return return_status
;
1137 /* --------------------------------------------------------------------------------------------- */
1138 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1139 in the directory path points to, 0 else. */
1142 check_dir_is_empty (const vfs_path_t
* vpath
)
1148 dir
= mc_opendir (vpath
);
1152 for (d
= mc_readdir (dir
); d
!= NULL
; d
= mc_readdir (dir
))
1153 if (!DIR_IS_DOT (d
->d_name
) && !DIR_IS_DOTDOT (d
->d_name
))
1163 /* --------------------------------------------------------------------------------------------- */
1165 static FileProgressStatus
1166 erase_dir_iff_empty (file_op_context_t
* ctx
, const vfs_path_t
* vpath
, size_t count
)
1168 FileProgressStatus error
= FILE_CONT
;
1171 s
= vfs_path_as_str (vpath
);
1173 file_progress_show_deleting (ctx
, s
, NULL
);
1174 file_progress_show_count (ctx
, count
, ctx
->progress_count
);
1175 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1180 if (check_dir_is_empty (vpath
) == 1) /* not empty or error */
1182 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1184 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1185 if (error
== FILE_SKIPALL
)
1186 ctx
->skip_all
= TRUE
;
1187 if (error
!= FILE_RETRY
)
1197 /* --------------------------------------------------------------------------------------------- */
1198 /* {{{ Panel operate routines */
1201 * Return currently selected entry name or the name of the first marked
1202 * entry if there is one.
1206 panel_get_file (WPanel
* panel
)
1208 if (get_current_type () == view_tree
)
1211 vfs_path_t
*selected_name
;
1213 tree
= (WTree
*) get_panel_widget (get_current_index ());
1214 selected_name
= tree_selected_name (tree
);
1215 return g_strdup (vfs_path_as_str (selected_name
));
1218 if (panel
->marked
!= 0)
1222 for (i
= 0; i
< panel
->dir
.len
; i
++)
1223 if (panel
->dir
.list
[i
].f
.marked
)
1224 return g_strdup (panel
->dir
.list
[i
].fname
);
1226 return g_strdup (panel
->dir
.list
[panel
->selected
].fname
);
1229 /* --------------------------------------------------------------------------------------------- */
1231 * panel_compute_totals:
1233 * compute the number of files and the number of bytes
1234 * used up by the whole selection, recursing directories
1235 * as required. In addition, it checks to see if it will
1236 * overwrite any files by doing the copy.
1239 static FileProgressStatus
1240 panel_compute_totals (const WPanel
* panel
, void *ui
, compute_dir_size_callback cback
,
1241 size_t * ret_count
, uintmax_t * ret_total
, gboolean compute_symlinks
)
1244 size_t dir_count
= 0;
1246 for (i
= 0; i
< panel
->dir
.len
; i
++)
1250 if (!panel
->dir
.list
[i
].f
.marked
)
1253 s
= &panel
->dir
.list
[i
].st
;
1255 if (S_ISDIR (s
->st_mode
))
1258 FileProgressStatus status
;
1260 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, NULL
);
1261 status
= compute_dir_size (p
, ui
, cback
, &dir_count
, ret_count
, ret_total
,
1265 if (status
!= FILE_CONT
)
1271 *ret_total
+= (uintmax_t) s
->st_size
;
1278 /* --------------------------------------------------------------------------------------------- */
1280 /** Initialize variables for progress bars */
1281 static FileProgressStatus
1282 panel_operate_init_totals (const WPanel
* panel
, const char *source
, file_op_context_t
* ctx
,
1283 filegui_dialog_type_t dialog_type
)
1285 FileProgressStatus status
;
1287 #ifdef ENABLE_BACKGROUND
1288 if (mc_global
.we_are_background
)
1292 if (verbose
&& file_op_compute_totals
)
1294 ComputeDirSizeUI
*ui
;
1296 ui
= compute_dir_size_create_ui (TRUE
);
1298 ctx
->progress_count
= 0;
1299 ctx
->progress_bytes
= 0;
1302 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1303 &ctx
->progress_count
, &ctx
->progress_bytes
,
1308 size_t dir_count
= 0;
1310 p
= vfs_path_from_str (source
);
1311 status
= compute_dir_size (p
, ui
, compute_dir_size_update_ui
, &dir_count
,
1312 &ctx
->progress_count
, &ctx
->progress_bytes
,
1317 compute_dir_size_destroy_ui (ui
);
1319 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1321 if (status
== FILE_SKIP
)
1327 ctx
->progress_count
= panel
->marked
;
1328 ctx
->progress_bytes
= panel
->total
;
1329 ctx
->progress_totals_computed
= FALSE
;
1332 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
1337 /* --------------------------------------------------------------------------------------------- */
1339 * Generate user prompt for panel operation.
1340 * single_source is the name if the source entry or NULL for multiple
1342 * src_stat is only used when single_source is not NULL.
1346 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1347 gboolean single_source
, const struct stat
*src_stat
)
1350 char *format_string
;
1353 static gboolean i18n_flag
= FALSE
;
1358 for (i
= G_N_ELEMENTS (op_names1
); i
-- != 0;)
1359 op_names1
[i
] = Q_ (op_names1
[i
]);
1362 for (i
= G_N_ELEMENTS (prompt_parts
); i
-- != 0;)
1363 prompt_parts
[i
] = _(prompt_parts
[i
]);
1365 one_format
= _(one_format
);
1366 many_format
= _(many_format
);
1367 #endif /* ENABLE_NLS */
1371 /* Possible prompts:
1373 * "Copy file \"%s\" with source mask:"
1374 * "Copy %d files with source mask:"
1375 * "Copy directory \"%s\" with source mask:"
1376 * "Copy %d directories with source mask:"
1377 * "Copy %d files/directories with source mask:"
1379 * "Move file \"%s\" with source mask:"
1380 * "Move %d files with source mask:"
1381 * "Move directory \"%s\" with source mask:"
1382 * "Move %d directories with source mask:"
1383 * "Move %d files/directories with source mask:"
1385 * "Delete file \"%s\"?"
1386 * "Delete %d files?"
1387 * "Delete directory \"%s\"?"
1388 * "Delete %d directories?"
1389 * "Delete %d files/directories?"
1392 sp
= (char *) (single_source
? one_format
: many_format
);
1394 /* 1. Substitute %o */
1395 format_string
= str_replace_all (sp
, "%o", op_names1
[(int) operation
]);
1397 /* 2. Substitute %n */
1398 cp
= operation
== OP_DELETE
? "\n" : " ";
1400 format_string
= str_replace_all (sp
, "%n", cp
);
1403 /* 3. Substitute %f */
1405 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1406 else if (panel
->marked
== panel
->dirs_marked
)
1407 cp
= prompt_parts
[3];
1409 cp
= panel
->dirs_marked
!= 0 ? prompt_parts
[4] : prompt_parts
[1];
1412 format_string
= str_replace_all (sp
, "%f", cp
);
1415 /* 4. Substitute %m */
1416 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1418 format_string
= str_replace_all (sp
, "%m", cp
);
1421 return format_string
;
1424 /* --------------------------------------------------------------------------------------------- */
1426 #ifdef ENABLE_BACKGROUND
1428 end_bg_process (file_op_context_t
* ctx
, enum OperationMode mode
)
1435 unregister_task_with_pid (pid
);
1436 /* file_op_context_destroy(ctx); */
1442 /* --------------------------------------------------------------------------------------------- */
1443 /*** public functions ****************************************************************************/
1444 /* --------------------------------------------------------------------------------------------- */
1447 copy_file_file (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
,
1448 const char *src_path
, const char *dst_path
)
1450 uid_t src_uid
= (uid_t
) (-1);
1451 gid_t src_gid
= (gid_t
) (-1);
1453 int src_desc
, dest_desc
= -1;
1454 int n_read
, n_written
;
1455 mode_t src_mode
= 0; /* The mode of the source file */
1456 struct stat sb
, sb2
;
1458 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1459 off_t file_size
= -1;
1460 FileProgressStatus return_status
, temp_status
;
1461 struct timeval tv_transfer_start
;
1462 dest_status_t dst_status
= DEST_NONE
;
1464 gboolean is_first_time
= TRUE
;
1465 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1466 gboolean write_errno_nospace
= FALSE
;
1468 /* FIXME: We should not be using global variables! */
1470 return_status
= FILE_RETRY
;
1472 dst_vpath
= vfs_path_from_str (dst_path
);
1473 src_vpath
= vfs_path_from_str (src_path
);
1475 file_progress_show_source (ctx
, src_vpath
);
1476 file_progress_show_target (ctx
, dst_vpath
);
1478 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1480 return_status
= FILE_ABORT
;
1486 while (mc_stat (dst_vpath
, &sb2
) == 0)
1488 if (S_ISDIR (sb2
.st_mode
))
1491 return_status
= FILE_SKIPALL
;
1494 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1495 if (return_status
== FILE_SKIPALL
)
1496 ctx
->skip_all
= TRUE
;
1497 if (return_status
== FILE_RETRY
)
1507 while ((*ctx
->stat_func
) (src_vpath
, &sb
) != 0)
1510 return_status
= FILE_SKIPALL
;
1513 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1514 if (return_status
== FILE_SKIPALL
)
1515 ctx
->skip_all
= TRUE
;
1518 if (return_status
!= FILE_RETRY
)
1524 /* Destination already exists */
1525 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1527 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1528 src_path
, dst_path
);
1532 /* Should we replace destination? */
1533 if (tctx
->ask_overwrite
)
1536 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1537 if (return_status
!= FILE_CONT
)
1542 if (!ctx
->do_append
)
1544 /* Check the hardlinks */
1545 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &sb
))
1547 /* We have made a hardlink - no more processing is necessary */
1548 return_status
= FILE_CONT
;
1552 if (S_ISLNK (sb
.st_mode
))
1554 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1558 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1559 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1561 while (mc_mknod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0
1564 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1565 if (return_status
== FILE_RETRY
)
1567 if (return_status
== FILE_SKIPALL
)
1568 ctx
->skip_all
= TRUE
;
1573 while (ctx
->preserve_uidgid
&& mc_chown (dst_vpath
, sb
.st_uid
, sb
.st_gid
) != 0
1576 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1577 if (temp_status
== FILE_SKIP
)
1579 if (temp_status
== FILE_SKIPALL
)
1580 ctx
->skip_all
= TRUE
;
1581 if (temp_status
!= FILE_RETRY
)
1583 return_status
= temp_status
;
1588 while (ctx
->preserve
&& mc_chmod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
) != 0
1591 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1592 if (temp_status
== FILE_SKIP
)
1594 if (temp_status
== FILE_SKIPALL
)
1595 ctx
->skip_all
= TRUE
;
1596 if (temp_status
!= FILE_RETRY
)
1598 return_status
= temp_status
;
1603 return_status
= FILE_CONT
;
1608 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1610 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1612 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1613 if (return_status
== FILE_RETRY
)
1615 if (return_status
== FILE_SKIPALL
)
1616 ctx
->skip_all
= TRUE
;
1617 if (return_status
== FILE_SKIP
)
1623 if (ctx
->do_reget
!= 0)
1625 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1627 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1629 ctx
->do_append
= FALSE
;
1633 while (mc_fstat (src_desc
, &sb
) != 0)
1636 return_status
= FILE_SKIPALL
;
1639 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1640 if (return_status
== FILE_RETRY
)
1642 if (return_status
== FILE_SKIPALL
)
1643 ctx
->skip_all
= TRUE
;
1644 ctx
->do_append
= FALSE
;
1649 src_mode
= sb
.st_mode
;
1650 src_uid
= sb
.st_uid
;
1651 src_gid
= sb
.st_gid
;
1652 utb
.actime
= sb
.st_atime
;
1653 utb
.modtime
= sb
.st_mtime
;
1654 file_size
= sb
.st_size
;
1656 open_flags
= O_WRONLY
;
1659 if (ctx
->do_append
!= 0)
1660 open_flags
|= O_APPEND
;
1662 open_flags
|= O_CREAT
| O_TRUNC
;
1666 open_flags
|= O_CREAT
| O_EXCL
;
1669 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1671 if (errno
!= EEXIST
)
1674 return_status
= FILE_SKIPALL
;
1677 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1678 if (return_status
== FILE_RETRY
)
1680 if (return_status
== FILE_SKIPALL
)
1681 ctx
->skip_all
= TRUE
;
1682 ctx
->do_append
= FALSE
;
1687 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1689 appending
= ctx
->do_append
;
1690 ctx
->do_append
= FALSE
;
1692 /* Find out the optimal buffer size. */
1693 while (mc_fstat (dest_desc
, &sb
) != 0)
1696 return_status
= FILE_SKIPALL
;
1699 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1700 if (return_status
== FILE_RETRY
)
1702 if (return_status
== FILE_SKIPALL
)
1703 ctx
->skip_all
= TRUE
;
1710 errno
= vfs_preallocate (dest_desc
, file_size
, (ctx
->do_append
!= 0) ? sb
.st_size
: 0);
1715 return_status
= FILE_SKIPALL
;
1719 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1720 if (return_status
== FILE_RETRY
)
1722 if (return_status
== FILE_SKIPALL
)
1723 ctx
->skip_all
= TRUE
;
1725 mc_close (dest_desc
);
1727 mc_unlink (dst_vpath
);
1728 dst_status
= DEST_NONE
;
1732 ctx
->eta_secs
= 0.0;
1735 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1736 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1738 file_progress_show (ctx
, 1, 1, "", TRUE
);
1739 return_status
= check_progress_buttons (ctx
);
1742 if (return_status
!= FILE_CONT
)
1746 off_t n_read_total
= 0;
1747 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1748 int secs
, update_secs
;
1749 const char *stalled_msg
= "";
1751 tv_last_update
= tv_transfer_start
;
1758 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1761 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0 && !ctx
->skip_all
)
1763 return_status
= file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
1764 if (return_status
== FILE_RETRY
)
1766 if (return_status
== FILE_SKIPALL
)
1767 ctx
->skip_all
= TRUE
;
1773 gettimeofday (&tv_current
, NULL
);
1778 n_read_total
+= n_read
;
1780 /* Windows NT ftp servers report that files have no
1781 * permissions: -------, so if we happen to have actually
1782 * read something, we should fix the permissions.
1784 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1785 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1786 gettimeofday (&tv_last_input
, NULL
);
1789 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
)
1793 n_read
-= n_written
;
1798 write_errno_nospace
= (n_written
< 0 && errno
== ENOSPC
);
1801 return_status
= FILE_SKIPALL
;
1804 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1806 if (return_status
== FILE_SKIP
)
1808 if (write_errno_nospace
)
1812 if (return_status
== FILE_SKIPALL
)
1814 ctx
->skip_all
= TRUE
;
1815 if (write_errno_nospace
)
1818 if (return_status
!= FILE_RETRY
)
1821 /* User pressed "Retry". Will the next mc_write() call be successful?
1822 * Reset error flag to be ready for that. */
1823 write_errno_nospace
= FALSE
;
1827 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1829 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1830 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1832 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1834 copy_file_file_display_progress (tctx
, ctx
,
1836 tv_transfer_start
, file_size
, n_read_total
);
1837 tv_last_update
= tv_current
;
1839 is_first_time
= FALSE
;
1841 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1843 stalled_msg
= _("(stalled)");
1847 gboolean force_update
;
1850 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1852 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1854 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1855 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1858 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1863 return_status
= check_progress_buttons (ctx
);
1865 if (return_status
!= FILE_CONT
)
1873 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1876 rotate_dash (FALSE
);
1877 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1879 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1880 if (temp_status
== FILE_RETRY
)
1882 if (temp_status
== FILE_ABORT
)
1883 return_status
= temp_status
;
1884 if (temp_status
== FILE_SKIPALL
)
1885 ctx
->skip_all
= TRUE
;
1889 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1891 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1892 if (temp_status
== FILE_RETRY
)
1894 if (temp_status
== FILE_SKIPALL
)
1895 ctx
->skip_all
= TRUE
;
1896 return_status
= temp_status
;
1900 if (dst_status
== DEST_SHORT
)
1902 /* Remove short file */
1905 /* In case of copy/move to full partition, keep source file
1906 * and remove incomplete destination one */
1907 if (!write_errno_nospace
)
1908 result
= query_dialog (Q_ ("DialogTitle|Copy"),
1909 _("Incomplete file was retrieved. Keep it?"),
1910 D_ERROR
, 2, _("&Delete"), _("&Keep"));
1912 mc_unlink (dst_vpath
);
1914 else if (dst_status
== DEST_FULL
)
1916 /* Copy has succeeded */
1917 if (!appending
&& ctx
->preserve_uidgid
)
1919 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1921 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1922 if (temp_status
== FILE_RETRY
)
1924 if (temp_status
== FILE_SKIPALL
)
1926 ctx
->skip_all
= TRUE
;
1927 return_status
= FILE_CONT
;
1929 if (temp_status
== FILE_SKIP
)
1930 return_status
= FILE_CONT
;
1939 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1941 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1942 if (temp_status
== FILE_RETRY
)
1944 if (temp_status
== FILE_SKIPALL
)
1946 ctx
->skip_all
= TRUE
;
1947 return_status
= FILE_CONT
;
1949 if (temp_status
== FILE_SKIP
)
1950 return_status
= FILE_CONT
;
1954 else if (!dst_exists
)
1956 src_mode
= umask (-1);
1958 src_mode
= 0100666 & ~src_mode
;
1959 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
1961 mc_utime (dst_vpath
, &utb
);
1965 if (return_status
== FILE_CONT
)
1966 return_status
= progress_update_one (tctx
, ctx
, file_size
);
1969 vfs_path_free (src_vpath
);
1970 vfs_path_free (dst_vpath
);
1971 return return_status
;
1974 /* --------------------------------------------------------------------------------------------- */
1976 * I think these copy_*_* functions should have a return type.
1977 * anyway, this function *must* have two directories as arguments.
1979 /* FIXME: This function needs to check the return values of the
1983 copy_dir_dir (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
,
1984 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, GSList
* parent_dirs
)
1986 struct dirent
*next
;
1987 struct stat buf
, cbuf
;
1989 FileProgressStatus return_status
= FILE_CONT
;
1991 vfs_path_t
*src_vpath
, *dst_vpath
;
1992 gboolean do_mkdir
= TRUE
;
1994 src_vpath
= vfs_path_from_str (s
);
1995 dst_vpath
= vfs_path_from_str (d
);
1997 /* First get the mode of the source dir */
2000 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
2003 return_status
= FILE_SKIPALL
;
2006 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
2007 if (return_status
== FILE_RETRY
)
2008 goto retry_src_stat
;
2009 if (return_status
== FILE_SKIPALL
)
2010 ctx
->skip_all
= TRUE
;
2015 if (is_in_linklist (dest_dirs
, src_vpath
, &cbuf
))
2017 /* Don't copy a directory we created before (we don't want to copy
2018 infinitely if a directory is copied into itself) */
2019 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2020 return_status
= FILE_CONT
;
2024 /* Hmm, hardlink to directory??? - Norbert */
2025 /* FIXME: In this step we should do something
2026 in case the destination already exist */
2027 /* Check the hardlinks */
2028 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &cbuf
))
2030 /* We have made a hardlink - no more processing is necessary */
2034 if (!S_ISDIR (cbuf
.st_mode
))
2037 return_status
= FILE_SKIPALL
;
2040 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
2041 if (return_status
== FILE_RETRY
)
2042 goto retry_src_stat
;
2043 if (return_status
== FILE_SKIPALL
)
2044 ctx
->skip_all
= TRUE
;
2049 if (is_in_linklist (parent_dirs
, src_vpath
, &cbuf
))
2051 /* we found a cyclic symbolic link */
2052 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
2053 return_status
= FILE_SKIP
;
2057 lp
= g_new0 (struct link
, 1);
2058 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
2059 lp
->ino
= cbuf
.st_ino
;
2060 lp
->dev
= cbuf
.st_dev
;
2061 parent_dirs
= g_slist_prepend (parent_dirs
, lp
);
2064 /* Now, check if the dest dir exists, if not, create it. */
2065 if (mc_stat (dst_vpath
, &buf
) != 0)
2067 /* Here the dir doesn't exist : make it ! */
2068 if (move_over
&& mc_rename (src_vpath
, dst_vpath
) == 0)
2070 return_status
= FILE_CONT
;
2077 * If the destination directory exists, we want to copy the whole
2078 * directory, but we only want this to happen once.
2080 * Escape sequences added to the * to compiler warnings.
2081 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2082 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2084 if (!S_ISDIR (buf
.st_mode
))
2087 return_status
= FILE_SKIPALL
;
2090 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2091 if (return_status
== FILE_SKIPALL
)
2092 ctx
->skip_all
= TRUE
;
2093 if (return_status
== FILE_RETRY
)
2094 goto retry_dst_stat
;
2098 /* Dive into subdir if exists */
2099 if (toplevel
&& ctx
->dive_into_subdirs
)
2104 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), NULL
);
2105 vfs_path_free (tmp
);
2112 d
= vfs_path_as_str (dst_vpath
);
2116 while (my_mkdir (dst_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2119 return_status
= FILE_SKIPALL
;
2122 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), d
);
2123 if (return_status
== FILE_SKIPALL
)
2124 ctx
->skip_all
= TRUE
;
2126 if (return_status
!= FILE_RETRY
)
2130 lp
= g_new0 (struct link
, 1);
2131 mc_stat (dst_vpath
, &buf
);
2132 lp
->vfs
= vfs_path_get_by_index (dst_vpath
, -1)->class;
2133 lp
->ino
= buf
.st_ino
;
2134 lp
->dev
= buf
.st_dev
;
2135 dest_dirs
= g_slist_prepend (dest_dirs
, lp
);
2138 if (ctx
->preserve_uidgid
)
2140 while (mc_chown (dst_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2143 return_status
= FILE_SKIPALL
;
2146 return_status
= file_error (_("Cannot chown target directory \"%s\"\n%s"), d
);
2147 if (return_status
== FILE_SKIPALL
)
2148 ctx
->skip_all
= TRUE
;
2150 if (return_status
!= FILE_RETRY
)
2155 /* open the source dir for reading */
2156 reading
= mc_opendir (src_vpath
);
2157 if (reading
== NULL
)
2160 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2163 vfs_path_t
*tmp_vpath
;
2166 * Now, we don't want '.' and '..' to be created / copied at any time
2168 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
2171 /* get the filename and add it to the src directory */
2172 path
= mc_build_filename (s
, next
->d_name
, NULL
);
2173 tmp_vpath
= vfs_path_from_str (path
);
2175 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2176 if (S_ISDIR (buf
.st_mode
))
2180 mdpath
= mc_build_filename (d
, next
->d_name
, NULL
);
2182 * From here, we just intend to recursively copy subdirs, not
2183 * the double functionality of copying different when the target
2184 * dir already exists. So, we give the recursive call the flag 0
2185 * meaning no toplevel.
2188 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2195 dest_file
= mc_build_filename (d
, x_basename (path
), NULL
);
2196 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2202 if (do_delete
&& return_status
== FILE_CONT
)
2204 if (ctx
->erase_at_end
)
2206 lp
= g_new0 (struct link
, 1);
2207 lp
->src_vpath
= tmp_vpath
;
2208 lp
->st_mode
= buf
.st_mode
;
2209 erase_list
= g_slist_append (erase_list
, lp
);
2212 else if (S_ISDIR (buf
.st_mode
))
2213 return_status
= erase_dir_iff_empty (ctx
, tmp_vpath
, tctx
->progress_count
);
2215 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
2217 vfs_path_free (tmp_vpath
);
2219 mc_closedir (reading
);
2225 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2226 utb
.actime
= cbuf
.st_atime
;
2227 utb
.modtime
= cbuf
.st_mtime
;
2228 mc_utime (dst_vpath
, &utb
);
2232 cbuf
.st_mode
= umask (-1);
2233 umask (cbuf
.st_mode
);
2234 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2235 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2239 free_link (parent_dirs
->data
);
2240 g_slist_free_1 (parent_dirs
);
2242 vfs_path_free (src_vpath
);
2243 vfs_path_free (dst_vpath
);
2244 return return_status
;
2249 /* --------------------------------------------------------------------------------------------- */
2250 /* {{{ Move routines */
2253 move_dir_dir (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
)
2255 struct stat sbuf
, dbuf
;
2256 FileProgressStatus return_status
;
2257 gboolean move_over
= FALSE
;
2259 vfs_path_t
*src_vpath
, *dst_vpath
;
2261 src_vpath
= vfs_path_from_str (s
);
2262 dst_vpath
= vfs_path_from_str (d
);
2264 file_progress_show_source (ctx
, src_vpath
);
2265 file_progress_show_target (ctx
, dst_vpath
);
2267 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2269 return_status
= FILE_ABORT
;
2275 mc_stat (src_vpath
, &sbuf
);
2277 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2278 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2280 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2285 ; /* destination doesn't exist */
2286 else if (!ctx
->dive_into_subdirs
)
2293 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), NULL
);
2294 vfs_path_free (tmp
);
2297 d
= vfs_path_as_str (dst_vpath
);
2299 /* Check if the user inputted an existing dir */
2301 if (mc_stat (dst_vpath
, &dbuf
) == 0)
2305 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, TRUE
, TRUE
, NULL
);
2307 if (return_status
!= FILE_CONT
)
2311 else if (ctx
->skip_all
)
2312 return_status
= FILE_SKIPALL
;
2315 if (S_ISDIR (dbuf
.st_mode
))
2316 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), d
);
2318 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), d
);
2319 if (return_status
== FILE_SKIPALL
)
2320 ctx
->skip_all
= TRUE
;
2321 if (return_status
== FILE_RETRY
)
2322 goto retry_dst_stat
;
2329 if (mc_rename (src_vpath
, dst_vpath
) == 0)
2331 return_status
= FILE_CONT
;
2339 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2340 if (return_status
== FILE_SKIPALL
)
2341 ctx
->skip_all
= TRUE
;
2342 if (return_status
== FILE_RETRY
)
2347 /* Failed because of filesystem boundary -> copy dir instead */
2348 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, FALSE
, TRUE
, NULL
);
2350 if (return_status
!= FILE_CONT
)
2353 file_progress_show_source (ctx
, NULL
);
2354 file_progress_show (ctx
, 0, 0, "", FALSE
);
2356 return_status
= check_progress_buttons (ctx
);
2357 if (return_status
!= FILE_CONT
)
2361 if (ctx
->erase_at_end
)
2363 while (erase_list
!= NULL
&& return_status
!= FILE_ABORT
)
2365 struct link
*lp
= (struct link
*) erase_list
->data
;
2367 if (S_ISDIR (lp
->st_mode
))
2368 return_status
= erase_dir_iff_empty (ctx
, lp
->src_vpath
, tctx
->progress_count
);
2370 return_status
= erase_file (tctx
, ctx
, lp
->src_vpath
);
2372 erase_list
= g_slist_remove (erase_list
, lp
);
2376 erase_dir_iff_empty (ctx
, src_vpath
, tctx
->progress_count
);
2379 erase_list
= free_linklist (erase_list
);
2381 vfs_path_free (src_vpath
);
2382 vfs_path_free (dst_vpath
);
2383 return return_status
;
2388 /* --------------------------------------------------------------------------------------------- */
2389 /* {{{ Erase routines */
2392 erase_dir (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* s_vpath
)
2394 FileProgressStatus error
;
2396 file_progress_show_deleting (ctx
, vfs_path_as_str (s_vpath
), NULL
);
2397 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2398 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2403 /* The old way to detect a non empty directory was:
2404 error = my_rmdir (s);
2405 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2406 For the linux user space nfs server (nfs-server-2.2beta29-2)
2407 we would have to check also for EIO. I hope the new way is
2408 fool proof. (Norbert)
2410 error
= check_dir_is_empty (s_vpath
);
2413 error
= query_recursive (ctx
, vfs_path_as_str (s_vpath
));
2414 if (error
== FILE_CONT
)
2415 error
= recursive_erase (tctx
, ctx
, s_vpath
);
2419 while (my_rmdir (vfs_path_as_str (s_vpath
)) == -1 && !ctx
->skip_all
)
2421 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), vfs_path_as_str (s_vpath
));
2422 if (error
!= FILE_RETRY
)
2431 /* --------------------------------------------------------------------------------------------- */
2432 /* {{{ Panel operate routines */
2435 compute_dir_size_create_ui (gboolean allow_skip
)
2437 ComputeDirSizeUI
*ui
;
2439 const char *b1_name
= N_("&Abort");
2440 const char *b2_name
= N_("&Skip");
2441 int b1_width
, b2_width
= 0, b_width
= 0;
2447 b1_name
= _(b1_name
);
2448 b2_name
= _(b2_name
);
2451 b1_width
= str_term_width1 (b1_name
) + 4;
2453 b2_width
= str_term_width1 (b2_name
) + 4 + 1;
2454 b_width
= b1_width
+ b2_width
;
2456 ui
= g_new (ComputeDirSizeUI
, 1);
2458 ui_width
= max (COLS
/ 2, b_width
+ 6);
2459 ui
->dlg
= dlg_create (TRUE
, 0, 0, 8, ui_width
, dialog_colors
, NULL
, NULL
, NULL
,
2460 _("Directory scanning"), DLG_CENTER
);
2462 ui
->dirname
= label_new (2, 3, "");
2463 add_widget (ui
->dlg
, ui
->dirname
);
2464 add_widget (ui
->dlg
, hline_new (4, -1, -1));
2465 b1_x
= (ui_width
- b_width
) / 2;
2466 b
= WIDGET (button_new (5, b1_x
, FILE_ABORT
, NORMAL_BUTTON
, b1_name
, NULL
));
2467 add_widget (ui
->dlg
, b
);
2470 add_widget (ui
->dlg
,
2471 button_new (5, b1_x
+ 1 + b1_width
, FILE_SKIP
, NORMAL_BUTTON
, b2_name
, NULL
));
2472 dlg_select_widget (b
);
2475 /* We will manage the dialog without any help,
2476 that's why we have to call dlg_init */
2482 /* --------------------------------------------------------------------------------------------- */
2485 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
2489 /* schedule to update passive panel */
2490 other_panel
->dirty
= 1;
2492 /* close and destroy dialog */
2493 dlg_run_done (ui
->dlg
);
2494 dlg_destroy (ui
->dlg
);
2499 /* --------------------------------------------------------------------------------------------- */
2502 compute_dir_size_update_ui (void *ui
, const vfs_path_t
* dirname_vpath
, size_t dir_count
,
2503 uintmax_t total_size
)
2505 ComputeDirSizeUI
*this = (ComputeDirSizeUI
*) ui
;
2508 char buffer
[BUF_1K
];
2513 g_snprintf (buffer
, sizeof (buffer
), _("%s\nDirectories: %zd, total size: %s"),
2514 str_trunc (vfs_path_as_str (dirname_vpath
), WIDGET (this->dlg
)->cols
- 6),
2515 dir_count
, size_trunc_sep (total_size
, panels_options
.kilobyte_si
));
2516 label_set_text (this->dirname
, buffer
);
2518 event
.x
= -1; /* Don't show the GPM cursor */
2519 c
= tty_get_event (&event
, FALSE
, FALSE
);
2523 /* Reinitialize to avoid old values after events other than
2524 selecting a button */
2525 this->dlg
->ret_value
= FILE_CONT
;
2527 dlg_process_event (this->dlg
, c
, &event
);
2529 switch (this->dlg
->ret_value
)
2541 /* --------------------------------------------------------------------------------------------- */
2545 * Computes the number of bytes used by the files in a directory
2549 compute_dir_size (const vfs_path_t
* dirname_vpath
, void *ui
, compute_dir_size_callback cback
,
2550 size_t * ret_dir_count
, size_t * ret_marked_count
, uintmax_t * ret_total
,
2551 gboolean compute_symlinks
)
2553 return do_compute_dir_size (dirname_vpath
, ui
, cback
, ret_dir_count
, ret_marked_count
,
2554 ret_total
, compute_symlinks
);
2557 /* --------------------------------------------------------------------------------------------- */
2561 * Performs one of the operations on the selection on the source_panel
2562 * (copy, delete, move).
2564 * Returns TRUE if did change the directory
2565 * structure, Returns FALSE if user aborted
2567 * force_single forces operation on the current entry and affects
2568 * default destination. Current filename is used as default.
2572 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2574 WPanel
*panel
= PANEL (source_panel
);
2575 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2576 || (get_current_type () == view_tree
);
2578 char *source
= NULL
;
2579 #ifdef WITH_FULL_PATHS
2580 vfs_path_t
*source_with_vpath
= NULL
;
2582 #define source_with_path source
2583 #endif /* !WITH_FULL_PATHS */
2585 vfs_path_t
*dest_vpath
= NULL
;
2587 char *save_cwd
= NULL
, *save_dest
= NULL
;
2588 struct stat src_stat
;
2589 gboolean ret_val
= TRUE
;
2591 FileProgressStatus value
;
2592 file_op_context_t
*ctx
;
2593 FileOpTotalContext
*tctx
;
2594 vfs_path_t
*tmp_vpath
;
2595 filegui_dialog_type_t dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2597 gboolean do_bg
= FALSE
; /* do background operation? */
2599 static gboolean i18n_flag
= FALSE
;
2602 for (i
= G_N_ELEMENTS (op_names
); i
-- != 0;)
2603 op_names
[i
] = Q_ (op_names
[i
]);
2607 linklist
= free_linklist (linklist
);
2608 dest_dirs
= free_linklist (dest_dirs
);
2612 vfs_path_t
*source_vpath
;
2615 source
= g_strdup (selection (panel
)->fname
);
2617 source
= panel_get_file (panel
);
2619 if (DIR_IS_DOTDOT (source
))
2622 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2626 source_vpath
= vfs_path_from_str (source
);
2627 /* Update stat to get actual info */
2628 if (mc_lstat (source_vpath
, &src_stat
) != 0)
2630 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2631 path_trunc (source
, 30), unix_error_string (errno
));
2633 /* Directory was changed outside MC. Reload it forced */
2634 if (!panel
->is_panelized
)
2636 panel_update_flags_t flags
= UP_RELOAD
;
2638 /* don't update panelized panel */
2639 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2640 flags
|= UP_ONLY_CURRENT
;
2642 update_panels (flags
, UP_KEEPSEL
);
2644 vfs_path_free (source_vpath
);
2647 vfs_path_free (source_vpath
);
2650 ctx
= file_op_context_new (operation
);
2652 /* Show confirmation dialog */
2653 if (operation
!= OP_DELETE
)
2655 char *tmp_dest_dir
, *dest_dir
;
2658 /* Forced single operations default to the original name */
2660 tmp_dest_dir
= g_strdup (source
);
2661 else if (get_other_type () == view_listing
)
2662 tmp_dest_dir
= g_strdup (vfs_path_as_str (other_panel
->cwd_vpath
));
2664 tmp_dest_dir
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
2666 * Add trailing backslash only when do non-local ops.
2667 * It saves user from occasional file renames (when destination
2670 if (!force_single
&& tmp_dest_dir
[0] != '\0'
2671 && tmp_dest_dir
[strlen (tmp_dest_dir
) - 1] != PATH_SEP
)
2673 /* add trailing separator */
2674 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2675 g_free (tmp_dest_dir
);
2680 dest_dir
= tmp_dest_dir
;
2682 if (dest_dir
== NULL
)
2688 /* Generate confirmation prompt */
2689 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2691 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2692 source
!= NULL
? (void *) source
2693 : (void *) &panel
->marked
, dest_dir
, &do_bg
);
2698 if (dest
== NULL
|| dest
[0] == '\0')
2704 dest_vpath
= vfs_path_from_str (dest
);
2706 else if (confirm_delete
)
2709 char fmd_buf
[BUF_MEDIUM
];
2711 /* Generate confirmation prompt */
2712 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2715 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2718 const int fmd_xlen
= 64;
2719 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2720 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2728 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2737 tctx
= file_op_total_context_new ();
2738 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2740 #ifdef ENABLE_BACKGROUND
2741 /* Did the user select to do a background operation? */
2746 v
= do_background (ctx
,
2747 g_strconcat (op_names
[operation
], ": ",
2748 vfs_path_as_str (panel
->cwd_vpath
), (char *) NULL
));
2750 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2752 /* If we are the parent */
2755 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2757 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2758 vfs_path_free (dest_vpath
);
2760 /* file_op_context_destroy (ctx); */
2765 #endif /* ENABLE_BACKGROUND */
2767 if (operation
== OP_DELETE
)
2768 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2769 else if (single_entry
&& S_ISDIR (selection (panel
)->st
.st_mode
))
2770 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2771 else if (single_entry
|| force_single
)
2772 dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2774 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2777 /* Initialize things */
2778 /* We do not want to trash cache every time file is
2779 created/touched. However, this will make our cache contain
2782 && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2783 save_dest
= g_strdup (dest
);
2785 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2786 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2787 save_cwd
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
2789 /* Now, let's do the job */
2791 /* This code is only called by the tree and panel code */
2794 /* We now have ETA in all cases */
2796 /* One file: FIXME mc_chdir will take user out of any vfs */
2797 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2802 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2803 chdir_retcode
= mc_chdir (vpath
);
2804 vfs_path_free (vpath
);
2805 if (chdir_retcode
< 0)
2812 /* The source and src_stat variables have been initialized before */
2813 #ifdef WITH_FULL_PATHS
2814 if (g_path_is_absolute (source
))
2815 source_with_vpath
= vfs_path_from_str (source
);
2817 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2818 #endif /* WITH_FULL_PATHS */
2819 if (panel_operate_init_totals (panel
, vfs_path_as_str (source_with_vpath
), ctx
, dialog_type
)
2822 if (operation
== OP_DELETE
)
2824 if (S_ISDIR (src_stat
.st_mode
))
2825 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2827 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2831 temp
= transform_source (ctx
, source_with_vpath
);
2833 value
= transform_error
;
2836 char *repl_dest
, *temp2
;
2838 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2839 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2841 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2846 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2850 vfs_path_free (dest_vpath
);
2852 dest_vpath
= vfs_path_from_str (dest
);
2857 /* we use file_mask_op_follow_links only with OP_COPY */
2858 ctx
->stat_func (source_with_vpath
, &src_stat
);
2860 if (S_ISDIR (src_stat
.st_mode
))
2862 copy_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2863 dest
, TRUE
, FALSE
, FALSE
, NULL
);
2866 copy_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2871 if (S_ISDIR (src_stat
.st_mode
))
2873 move_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
), dest
);
2876 move_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2881 /* Unknown file operation */
2885 } /* Copy or move operation */
2887 if ((value
== FILE_CONT
) && !force_single
)
2888 unmark_files (panel
);
2895 /* Check destination for copy or move operation */
2896 while (operation
!= OP_DELETE
)
2899 struct stat dst_stat
;
2901 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2903 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2907 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2911 if (panel_operate_init_totals (panel
, NULL
, ctx
, dialog_type
) == FILE_CONT
)
2913 /* Loop for every file, perform the actual copy operation */
2914 for (i
= 0; i
< panel
->dir
.len
; i
++)
2916 const char *source2
;
2918 if (!panel
->dir
.list
[i
].f
.marked
)
2919 continue; /* Skip the unmarked ones */
2921 source2
= panel
->dir
.list
[i
].fname
;
2922 src_stat
= panel
->dir
.list
[i
].st
;
2924 #ifdef WITH_FULL_PATHS
2925 vfs_path_free (source_with_vpath
);
2926 if (g_path_is_absolute (source2
))
2927 source_with_vpath
= vfs_path_from_str (source2
);
2930 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2931 #endif /* WITH_FULL_PATHS */
2933 if (operation
== OP_DELETE
)
2935 if (S_ISDIR (src_stat
.st_mode
))
2936 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2938 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2942 temp
= transform_source (ctx
, source_with_vpath
);
2944 value
= transform_error
;
2947 char *temp2
, *repl_dest
, *source_with_path_str
;
2949 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2950 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2952 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2957 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2960 source_with_path_str
=
2961 strutils_shell_unescape (vfs_path_as_str (source_with_vpath
));
2962 temp
= strutils_shell_unescape (temp2
);
2968 /* we use file_mask_op_follow_links only with OP_COPY */
2972 vpath
= vfs_path_from_str (source_with_path_str
);
2973 ctx
->stat_func (vpath
, &src_stat
);
2974 vfs_path_free (vpath
);
2976 if (S_ISDIR (src_stat
.st_mode
))
2977 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp
,
2978 TRUE
, FALSE
, FALSE
, NULL
);
2980 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp
);
2981 dest_dirs
= free_linklist (dest_dirs
);
2985 if (S_ISDIR (src_stat
.st_mode
))
2986 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp
);
2988 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp
);
2992 /* Unknown file operation */
2998 } /* Copy or move operation */
3000 if (value
== FILE_ABORT
)
3003 if (value
== FILE_CONT
)
3004 do_file_mark (panel
, i
, 0);
3006 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3008 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3009 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3012 if (operation
!= OP_DELETE
)
3013 file_progress_show (ctx
, 0, 0, "", FALSE
);
3015 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3019 } /* Loop for every file */
3021 } /* Many entries */
3025 if (save_cwd
!= NULL
)
3027 tmp_vpath
= vfs_path_from_str (save_cwd
);
3028 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3029 vfs_path_free (tmp_vpath
);
3033 if (save_dest
!= NULL
)
3035 tmp_vpath
= vfs_path_from_str (save_dest
);
3036 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3037 vfs_path_free (tmp_vpath
);
3041 linklist
= free_linklist (linklist
);
3042 dest_dirs
= free_linklist (dest_dirs
);
3043 #ifdef WITH_FULL_PATHS
3044 vfs_path_free (source_with_vpath
);
3045 #endif /* WITH_FULL_PATHS */
3047 vfs_path_free (dest_vpath
);
3048 g_free (ctx
->dest_mask
);
3049 ctx
->dest_mask
= NULL
;
3051 #ifdef ENABLE_BACKGROUND
3052 /* Let our parent know we are saying bye bye */
3053 if (mc_global
.we_are_background
)
3055 int cur_pid
= getpid ();
3056 /* Send pid to parent with child context, it is fork and
3057 don't modify real parent ctx */
3059 parent_call ((void *) end_bg_process
, ctx
, 0);
3062 my_exit (EXIT_SUCCESS
);
3064 #endif /* ENABLE_BACKGROUND */
3066 file_op_total_context_destroy (tctx
);
3068 file_op_context_destroy (ctx
);
3076 /* --------------------------------------------------------------------------------------------- */
3077 /* {{{ Query/status report routines */
3078 /** Report error with one file */
3080 file_error (const char *format
, const char *file
)
3082 char buf
[BUF_MEDIUM
];
3084 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3086 return do_file_error (buf
);
3089 /* --------------------------------------------------------------------------------------------- */
3092 Cause emacs to enter folding mode for this file: