4 Copyright (C) 1994-2018
5 Free Software Foundation, Inc.
8 Janne Kukonlehto, 1994, 1995
9 Fred Leeflang, 1994, 1995
10 Miguel de Icaza, 1994, 1995, 1996
11 Jakub Jelinek, 1995, 1996
14 Andrew Borodin <aborodin@vmail.ru>, 2011-2014
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
46 /** \file src/filemanager/file.c
47 * \brief Source: file management
50 /* {{{ Include files */
59 #include <sys/types.h>
63 #include "lib/global.h"
64 #include "lib/tty/tty.h"
65 #include "lib/tty/key.h"
66 #include "lib/search.h"
67 #include "lib/strescape.h"
68 #include "lib/strutil.h"
70 #include "lib/vfs/vfs.h"
71 #include "lib/widget.h"
73 #include "src/setup.h"
74 #ifdef ENABLE_BACKGROUND
75 #include "src/background.h" /* do_background() */
78 /* Needed for current_panel, other_panel and WTree */
83 #include "midnight.h" /* current_panel */
84 #include "layout.h" /* rotate_dash() */
85 #include "ioblksize.h" /* io_blksize() */
91 /*** global variables ****************************************************************************/
93 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
94 const char *op_names
[3] = {
95 N_("DialogTitle|Copy"),
96 N_("DialogTitle|Move"),
97 N_("DialogTitle|Delete")
100 /*** file scope macro definitions ****************************************************************/
102 #define FILEOP_UPDATE_INTERVAL 2
103 #define FILEOP_STALLING_INTERVAL 4
105 /*** file scope type declarations ****************************************************************/
107 /* This is a hard link cache */
110 const struct vfs_class
*vfs
;
115 vfs_path_t
*src_vpath
;
116 vfs_path_t
*dst_vpath
;
119 /* Status of the destination file */
122 DEST_NONE
= 0, /* Not created */
123 DEST_SHORT
= 1, /* Created, not fully copied */
124 DEST_FULL
= 2 /* Created, fully copied */
128 * This array introduced to avoid translation problems. The former (op_names)
129 * is assumed to be nouns, suitable in dialog box titles; this one should
130 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
131 * (I don't use spaces around the words, because someday they could be
132 * dropped, when widgets get smarter)
135 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
136 static const char *op_names1
[] = {
137 N_("FileOperation|Copy"),
138 N_("FileOperation|Move"),
139 N_("FileOperation|Delete")
143 * These are formats for building a prompt. Parts encoded as follows:
144 * %o - operation from op_names1
145 * %f - file/files or files/directories, as appropriate
146 * %m - "with source mask" or question mark for delete
147 * %s - source name (truncated)
148 * %d - number of marked files
149 * %n - the '\n' symbol to form two-line prompt for delete or space for other operations
151 /* xgettext:no-c-format */
152 static const char *one_format
= N_("%o %f%n\"%s\"%m");
153 /* xgettext:no-c-format */
154 static const char *many_format
= N_("%o %d %f%m");
156 static const char *prompt_parts
[] = {
161 N_("files/directories"),
162 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
163 N_(" with source mask:")
166 /*** file scope variables ************************************************************************/
168 /* the hard link cache */
169 static GSList
*linklist
= NULL
;
171 /* the files-to-be-erased list */
172 static GSList
*erase_list
= NULL
;
175 * In copy_dir_dir we use two additional single linked lists: The first -
176 * variable name 'parent_dirs' - holds information about already copied
177 * directories and is used to detect cyclic symbolic links.
178 * The second ('dest_dirs' below) holds information about just created
179 * target directories and is used to detect when an directory is copied
180 * into itself (we don't want to copy infinitly).
181 * Both lists don't use the linkcount and name structure members of struct
184 static GSList
*dest_dirs
= NULL
;
186 /* --------------------------------------------------------------------------------------------- */
187 /*** file scope functions ************************************************************************/
188 /* --------------------------------------------------------------------------------------------- */
191 dirsize_status_locate_buttons (dirsize_status_msg_t
* dsm
)
193 status_msg_t
*sm
= STATUS_MSG (dsm
);
194 Widget
*wd
= WIDGET (sm
->dlg
);
200 if (!dsm
->allow_skip
)
202 /* single button: "Abort" */
203 x
+= (wd
->cols
- dsm
->abort_button
->cols
) / 2;
204 widget_set_size (dsm
->abort_button
, y
, x
,
205 dsm
->abort_button
->lines
, dsm
->abort_button
->cols
);
209 /* two buttons: "Abort" and "Skip" */
212 cols
= dsm
->abort_button
->cols
+ dsm
->skip_button
->cols
+ 1;
213 x
+= (wd
->cols
- cols
) / 2;
214 widget_set_size (dsm
->abort_button
, y
, x
, dsm
->abort_button
->lines
,
215 dsm
->abort_button
->cols
);
216 x
+= dsm
->abort_button
->cols
+ 1;
217 widget_set_size (dsm
->skip_button
, y
, x
, dsm
->skip_button
->lines
, dsm
->skip_button
->cols
);
221 /* --------------------------------------------------------------------------------------------- */
224 build_dest (file_op_context_t
* ctx
, const char *src
, const char *dest
, FileProgressStatus
* status
)
227 const char *fnsource
;
233 /* We remove \n from the filename since regex routines would use \n as an anchor */
234 /* this is just to be allowed to maniupulate file names with \n on it */
235 for (q
= s
; *q
!= '\0'; q
++)
239 fnsource
= x_basename (s
);
241 if (!mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
248 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
249 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
251 if (ctx
->search_handle
->error_str
!= NULL
)
252 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
254 *status
= FILE_ABORT
;
260 if (*status
== FILE_CONT
)
264 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
265 if (ctx
->search_handle
->error
== MC_SEARCH_E_OK
)
266 s
= mc_build_filename (repl_dest
, q
, (char *) NULL
);
269 if (ctx
->search_handle
->error_str
!= NULL
)
270 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
272 *status
= FILE_ABORT
;
283 /* --------------------------------------------------------------------------------------------- */
286 free_link (void *data
)
288 struct link
*lp
= (struct link
*) data
;
290 vfs_path_free (lp
->src_vpath
);
291 vfs_path_free (lp
->dst_vpath
);
295 /* --------------------------------------------------------------------------------------------- */
298 free_linklist (GSList
* lp
)
300 g_slist_free_full (lp
, free_link
);
305 /* --------------------------------------------------------------------------------------------- */
308 is_in_linklist (const GSList
* lp
, const vfs_path_t
* vpath
, const struct stat
*sb
)
310 const struct vfs_class
*class;
311 ino_t ino
= sb
->st_ino
;
312 dev_t dev
= sb
->st_dev
;
314 class = vfs_path_get_last_path_vfs (vpath
);
316 for (; lp
!= NULL
; lp
= (const GSList
*) g_slist_next (lp
))
318 const struct link
*lnk
= (const struct link
*) lp
->data
;
320 if (lnk
->vfs
== class && lnk
->ino
== ino
&& lnk
->dev
== dev
)
326 /* --------------------------------------------------------------------------------------------- */
328 * Check and made hardlink
330 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
331 * and a hardlink was successfully made
335 check_hardlinks (const vfs_path_t
* src_vpath
, const struct stat
*src_stat
,
336 const vfs_path_t
* dst_vpath
)
341 const struct vfs_class
*my_vfs
;
342 ino_t ino
= src_stat
->st_ino
;
343 dev_t dev
= src_stat
->st_dev
;
344 struct stat link_stat
;
346 if (src_stat
->st_nlink
< 2 || (vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
349 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
351 for (lp
= linklist
; lp
!= NULL
; lp
= g_slist_next (lp
))
353 lnk
= (struct link
*) lp
->data
;
355 if (lnk
->vfs
== my_vfs
&& lnk
->ino
== ino
&& lnk
->dev
== dev
)
357 const struct vfs_class
*lp_name_class
;
360 lp_name_class
= vfs_path_get_last_path_vfs (lnk
->src_vpath
);
361 stat_result
= mc_stat (lnk
->src_vpath
, &link_stat
);
363 if (stat_result
== 0 && link_stat
.st_ino
== ino
364 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
366 const struct vfs_class
*p_class
, *dst_name_class
;
368 dst_name_class
= vfs_path_get_last_path_vfs (dst_vpath
);
369 p_class
= vfs_path_get_last_path_vfs (lnk
->dst_vpath
);
371 if (dst_name_class
== p_class
&&
372 mc_stat (lnk
->dst_vpath
, &link_stat
) == 0 &&
373 mc_link (lnk
->dst_vpath
, dst_vpath
) == 0)
377 message (D_ERROR
, MSG_ERROR
,
378 _("Cannot make the hardlink\n%s\nto\n%s"), vfs_path_as_str (dst_vpath
),
379 vfs_path_as_str (lnk
->dst_vpath
));
384 lnk
= g_try_new (struct link
, 1);
392 lnk
->src_vpath
= vfs_path_clone (src_vpath
);
393 lnk
->dst_vpath
= vfs_path_clone (dst_vpath
);
395 linklist
= g_slist_prepend (linklist
, lnk
);
401 /* --------------------------------------------------------------------------------------------- */
403 * Duplicate the contents of the symbolic link src_path in dst_path.
404 * Try to make a stable symlink if the option "stable symlink" was
405 * set in the file mask dialog.
406 * If dst_path is an existing symlink it will be deleted silently
407 * (upper levels take already care of existing files at dst_path).
410 static FileProgressStatus
411 make_symlink (file_op_context_t
* ctx
, const char *src_path
, const char *dst_path
)
413 char link_target
[MC_MAXPATHLEN
];
415 FileProgressStatus return_status
;
417 vfs_path_t
*src_vpath
;
418 vfs_path_t
*dst_vpath
;
419 gboolean dst_is_symlink
;
420 vfs_path_t
*link_target_vpath
= NULL
;
422 src_vpath
= vfs_path_from_str (src_path
);
423 dst_vpath
= vfs_path_from_str (dst_path
);
424 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
427 len
= mc_readlink (src_vpath
, link_target
, sizeof (link_target
) - 1);
431 return_status
= FILE_SKIPALL
;
434 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
435 if (return_status
== FILE_SKIPALL
)
436 ctx
->skip_all
= TRUE
;
437 if (return_status
== FILE_RETRY
)
438 goto retry_src_readlink
;
443 link_target
[len
] = '\0';
445 if (ctx
->stable_symlinks
&& !(vfs_file_is_local (src_vpath
) && vfs_file_is_local (dst_vpath
)))
447 message (D_ERROR
, MSG_ERROR
,
448 _("Cannot make stable symlinks across "
449 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
450 ctx
->stable_symlinks
= FALSE
;
453 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
457 r
= strrchr (src_path
, PATH_SEP
);
463 p
= g_strndup (src_path
, r
- src_path
+ 1);
464 if (g_path_is_absolute (dst_path
))
465 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
467 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
469 if (vfs_path_tokens_count (q
) > 1)
472 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
474 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
475 s
= g_strconcat (p
, link_target
, (char *) NULL
);
476 g_strlcpy (link_target
, s
, sizeof (link_target
));
478 tmp_vpath2
= vfs_path_from_str (link_target
);
479 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
480 vfs_path_free (tmp_vpath1
);
481 vfs_path_free (tmp_vpath2
);
484 g_strlcpy (link_target
, s
, sizeof (link_target
));
492 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
495 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
498 return_status
= FILE_CONT
;
502 * if dst_exists, it is obvious that this had failed.
503 * We can delete the old symlink and try again...
505 if (dst_is_symlink
&& mc_unlink (dst_vpath
) == 0
506 && mc_symlink (link_target_vpath
, dst_vpath
) == 0)
509 return_status
= FILE_CONT
;
514 return_status
= FILE_SKIPALL
;
517 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
518 if (return_status
== FILE_SKIPALL
)
519 ctx
->skip_all
= TRUE
;
520 if (return_status
== FILE_RETRY
)
521 goto retry_dst_symlink
;
525 vfs_path_free (src_vpath
);
526 vfs_path_free (dst_vpath
);
527 vfs_path_free (link_target_vpath
);
528 return return_status
;
531 /* --------------------------------------------------------------------------------------------- */
533 * do_compute_dir_size:
535 * Computes the number of bytes used by the files in a directory
538 static FileProgressStatus
539 do_compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* dsm
,
540 size_t * dir_count
, size_t * ret_marked
, uintmax_t * ret_total
,
541 gboolean compute_symlinks
)
543 static guint64 timestamp
= 0;
544 /* update with 25 FPS rate */
545 static const guint64 delay
= G_USEC_PER_SEC
/ 25;
547 status_msg_t
*sm
= STATUS_MSG (dsm
);
551 struct dirent
*dirent
;
552 FileProgressStatus ret
= FILE_CONT
;
554 if (!compute_symlinks
)
556 res
= mc_lstat (dirname_vpath
, &s
);
560 /* don't scan symlink to directory */
561 if (S_ISLNK (s
.st_mode
))
564 *ret_total
+= (uintmax_t) s
.st_size
;
571 dir
= mc_opendir (dirname_vpath
);
575 while (ret
== FILE_CONT
&& (dirent
= mc_readdir (dir
)) != NULL
)
577 vfs_path_t
*tmp_vpath
;
579 if (DIR_IS_DOT (dirent
->d_name
) || DIR_IS_DOTDOT (dirent
->d_name
))
582 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, (char *) NULL
);
584 res
= mc_lstat (tmp_vpath
, &s
);
587 if (S_ISDIR (s
.st_mode
))
589 do_compute_dir_size (tmp_vpath
, dsm
, dir_count
, ret_marked
, ret_total
,
596 *ret_total
+= (uintmax_t) s
.st_size
;
599 if (ret
== FILE_CONT
&& sm
->update
!= NULL
&& mc_time_elapsed (×tamp
, delay
))
601 dsm
->dirname_vpath
= tmp_vpath
;
602 dsm
->dir_count
= *dir_count
;
603 dsm
->total_size
= *ret_total
;
604 ret
= sm
->update (sm
);
608 vfs_path_free (tmp_vpath
);
615 /* --------------------------------------------------------------------------------------------- */
617 * panel_compute_totals:
619 * compute the number of files and the number of bytes
620 * used up by the whole selection, recursing directories
621 * as required. In addition, it checks to see if it will
622 * overwrite any files by doing the copy.
625 static FileProgressStatus
626 panel_compute_totals (const WPanel
* panel
, dirsize_status_msg_t
* sm
, size_t * ret_count
,
627 uintmax_t * ret_total
, gboolean compute_symlinks
)
630 size_t dir_count
= 0;
632 for (i
= 0; i
< panel
->dir
.len
; i
++)
636 if (!panel
->dir
.list
[i
].f
.marked
)
639 s
= &panel
->dir
.list
[i
].st
;
641 if (S_ISDIR (s
->st_mode
))
644 FileProgressStatus status
;
646 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, (char *) NULL
);
647 status
= compute_dir_size (p
, sm
, &dir_count
, ret_count
, ret_total
, compute_symlinks
);
650 if (status
!= FILE_CONT
)
656 *ret_total
+= (uintmax_t) s
->st_size
;
663 /* --------------------------------------------------------------------------------------------- */
665 /** Initialize variables for progress bars */
666 static FileProgressStatus
667 panel_operate_init_totals (const WPanel
* panel
, const vfs_path_t
* source
,
668 const struct stat
*source_stat
, file_op_context_t
* ctx
,
669 gboolean compute_totals
, filegui_dialog_type_t dialog_type
)
671 FileProgressStatus status
;
673 #ifdef ENABLE_BACKGROUND
674 if (mc_global
.we_are_background
)
678 if (verbose
&& compute_totals
)
680 dirsize_status_msg_t dsm
;
682 memset (&dsm
, 0, sizeof (dsm
));
683 dsm
.allow_skip
= TRUE
;
684 status_msg_init (STATUS_MSG (&dsm
), _("Directory scanning"), 0, dirsize_status_init_cb
,
685 dirsize_status_update_cb
, dirsize_status_deinit_cb
);
687 ctx
->progress_count
= 0;
688 ctx
->progress_bytes
= 0;
691 status
= panel_compute_totals (panel
, &dsm
, &ctx
->progress_count
, &ctx
->progress_bytes
,
693 else if (S_ISDIR (source_stat
->st_mode
))
695 size_t dir_count
= 0;
697 status
= compute_dir_size (source
, &dsm
, &dir_count
, &ctx
->progress_count
,
698 &ctx
->progress_bytes
, ctx
->follow_links
);
702 ctx
->progress_count
++;
703 ctx
->progress_bytes
+= (uintmax_t) source_stat
->st_size
;
707 status_msg_deinit (STATUS_MSG (&dsm
));
709 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
711 if (status
== FILE_SKIP
)
717 ctx
->progress_count
= panel
->marked
;
718 ctx
->progress_bytes
= panel
->total
;
719 ctx
->progress_totals_computed
= FALSE
;
722 /* destroy already created UI for single file rename operation */
723 file_op_context_destroy_ui (ctx
);
725 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
730 /* --------------------------------------------------------------------------------------------- */
732 static FileProgressStatus
733 progress_update_one (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, off_t add
)
735 struct timeval tv_current
;
736 static struct timeval tv_start
= { 0, 0 };
738 tctx
->progress_count
++;
739 tctx
->progress_bytes
+= (uintmax_t) add
;
741 if (tv_start
.tv_sec
== 0)
743 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
745 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
746 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
748 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
750 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
751 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
753 tv_start
.tv_sec
= tv_current
.tv_sec
;
756 return check_progress_buttons (ctx
);
759 /* --------------------------------------------------------------------------------------------- */
761 static FileProgressStatus
762 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
766 const char *head_msg
;
768 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
770 msg
= g_strdup_printf (fmt
, a
, b
);
771 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
775 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
778 /* --------------------------------------------------------------------------------------------- */
780 static FileProgressStatus
781 warn_same_file (const char *fmt
, const char *a
, const char *b
)
783 #ifdef ENABLE_BACKGROUND
788 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
, const char *a
, const char *b
);
792 pntr
.f
= real_warn_same_file
;
794 if (mc_global
.we_are_background
)
795 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
797 return real_warn_same_file (Foreground
, fmt
, a
, b
);
800 /* --------------------------------------------------------------------------------------------- */
803 check_same_file (const char *a
, const struct stat
*ast
, const char *b
, const struct stat
*bst
,
804 FileProgressStatus
* status
)
806 if (ast
->st_dev
!= bst
->st_dev
|| ast
->st_ino
!= bst
->st_ino
)
809 if (S_ISDIR (ast
->st_mode
))
810 *status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), a
, b
);
812 *status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), a
, b
);
817 /* --------------------------------------------------------------------------------------------- */
820 get_times (const struct stat
*sb
, mc_timesbuf_t
* times
)
822 #ifdef HAVE_UTIMENSAT
823 (*times
)[0] = sb
->st_atim
;
824 (*times
)[1] = sb
->st_mtim
;
826 times
->actime
= sb
->st_atime
;
827 times
->modtime
= sb
->st_mtime
;
831 /* --------------------------------------------------------------------------------------------- */
832 /* {{{ Query/status report routines */
834 static FileProgressStatus
835 real_do_file_error (enum OperationMode mode
, const char *error
)
840 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
842 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
864 /* --------------------------------------------------------------------------------------------- */
866 static FileProgressStatus
867 real_query_recursive (file_op_context_t
* ctx
, enum OperationMode mode
, const char *s
)
869 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
874 msg
= mode
== Foreground
875 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
876 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
877 text
= g_strdup_printf (msg
, path_trunc (s
, 30));
882 ctx
->recursive_result
=
883 query_dialog (op_names
[OP_DELETE
], text
, D_ERROR
, 5, _("&Yes"), _("&No"), _("A&ll"),
884 _("Non&e"), _("&Abort"));
887 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
891 switch (ctx
->recursive_result
)
894 case RECURSIVE_ALWAYS
:
898 case RECURSIVE_NEVER
:
901 case RECURSIVE_ABORT
:
907 /* --------------------------------------------------------------------------------------------- */
909 #ifdef ENABLE_BACKGROUND
910 static FileProgressStatus
911 do_file_error (const char *str
)
917 FileProgressStatus (*f
) (enum OperationMode
, const char *);
921 pntr
.f
= real_do_file_error
;
923 if (mc_global
.we_are_background
)
924 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
926 return real_do_file_error (Foreground
, str
);
929 /* --------------------------------------------------------------------------------------------- */
931 static FileProgressStatus
932 query_recursive (file_op_context_t
* ctx
, const char *s
)
938 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *);
942 pntr
.f
= real_query_recursive
;
944 if (mc_global
.we_are_background
)
945 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
947 return real_query_recursive (ctx
, Foreground
, s
);
950 /* --------------------------------------------------------------------------------------------- */
952 static FileProgressStatus
953 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
954 struct stat
*_d_stat
)
960 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *,
961 struct stat
*, struct stat
*);
965 pntr
.f
= file_progress_real_query_replace
;
967 if (mc_global
.we_are_background
)
968 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
969 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
971 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
975 /* --------------------------------------------------------------------------------------------- */
977 static FileProgressStatus
978 do_file_error (const char *str
)
980 return real_do_file_error (Foreground
, str
);
983 /* --------------------------------------------------------------------------------------------- */
985 static FileProgressStatus
986 query_recursive (file_op_context_t
* ctx
, const char *s
)
988 return real_query_recursive (ctx
, Foreground
, s
);
991 /* --------------------------------------------------------------------------------------------- */
993 static FileProgressStatus
994 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
995 struct stat
*_d_stat
)
997 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
1000 #endif /* !ENABLE_BACKGROUND */
1002 /* --------------------------------------------------------------------------------------------- */
1003 /** Report error with two files */
1005 static FileProgressStatus
1006 files_error (const char *format
, const char *file1
, const char *file2
)
1008 char buf
[BUF_MEDIUM
];
1009 char *nfile1
= g_strdup (path_trunc (file1
, 15));
1010 char *nfile2
= g_strdup (path_trunc (file2
, 15));
1012 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
1017 return do_file_error (buf
);
1022 /* --------------------------------------------------------------------------------------------- */
1025 copy_file_file_display_progress (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
1026 struct timeval tv_current
, struct timeval tv_transfer_start
,
1027 off_t file_size
, off_t n_read_total
)
1031 /* 1. Update rotating dash after some time */
1034 /* 3. Compute ETA */
1035 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
1037 if (n_read_total
== 0)
1038 ctx
->eta_secs
= 0.0;
1041 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
1042 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
1045 /* 4. Compute BPS rate */
1046 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
1047 if (ctx
->bps_time
< 1)
1049 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
1051 /* 5. Compute total ETA and BPS */
1052 if (ctx
->progress_bytes
!= 0)
1054 uintmax_t remain_bytes
;
1056 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
1059 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
1064 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
1065 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
1068 /* broken on lot of little files */
1070 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
1071 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
1076 /* --------------------------------------------------------------------------------------------- */
1079 try_remove_file (file_op_context_t
* ctx
, const vfs_path_t
* vpath
, FileProgressStatus
* status
)
1081 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
1083 *status
= file_error (_("Cannot remove file \"%s\"\n%s"), vfs_path_as_str (vpath
));
1084 if (*status
== FILE_RETRY
)
1086 if (*status
== FILE_SKIPALL
)
1087 ctx
->skip_all
= TRUE
;
1094 /* --------------------------------------------------------------------------------------------- */
1096 /* {{{ Move routines */
1099 * Move single file or one of many files from one location to another.
1101 * @panel pointer to panel in case of single file, NULL otherwise
1102 * @tctx file operation total context object
1103 * @ctx file operation context object
1104 * @s source file name
1105 * @d destination file name
1107 * @return operation result
1109 static FileProgressStatus
1110 move_file_file (const WPanel
* panel
, file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
1111 const char *s
, const char *d
)
1113 struct stat src_stats
, dst_stats
;
1114 FileProgressStatus return_status
= FILE_CONT
;
1115 gboolean copy_done
= FALSE
;
1116 gboolean old_ask_overwrite
;
1117 vfs_path_t
*src_vpath
, *dst_vpath
;
1119 src_vpath
= vfs_path_from_str (s
);
1120 dst_vpath
= vfs_path_from_str (d
);
1122 file_progress_show_source (ctx
, src_vpath
);
1123 file_progress_show_target (ctx
, dst_vpath
);
1125 /* FIXME: do we really need to check buttons in case of single file? */
1126 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1128 return_status
= FILE_ABORT
;
1134 while (mc_lstat (src_vpath
, &src_stats
) != 0)
1136 /* Source doesn't exist */
1138 return_status
= FILE_SKIPALL
;
1141 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
1142 if (return_status
== FILE_SKIPALL
)
1143 ctx
->skip_all
= TRUE
;
1146 if (return_status
!= FILE_RETRY
)
1150 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
1152 if (check_same_file (s
, &src_stats
, d
, &dst_stats
, &return_status
))
1155 if (S_ISDIR (dst_stats
.st_mode
))
1157 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
1159 return_status
= FILE_SKIP
;
1163 if (confirm_overwrite
)
1165 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1166 if (return_status
!= FILE_CONT
)
1169 /* Ok to overwrite */
1172 if (!ctx
->do_append
)
1174 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
1176 return_status
= make_symlink (ctx
, s
, d
);
1177 if (return_status
== FILE_CONT
)
1178 goto retry_src_remove
;
1182 if (mc_rename (src_vpath
, dst_vpath
) == 0)
1184 /* FIXME: do we really need to update progress in case of single file? */
1185 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
1190 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1191 one nfs to the same, but on the server it is on two different
1192 filesystems. Then nfs returns EIO instead of EXDEV.
1193 Hope it will not hurt if we always in case of error try to copy/delete. */
1195 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1200 return_status
= FILE_SKIPALL
;
1203 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
1204 if (return_status
== FILE_SKIPALL
)
1205 ctx
->skip_all
= TRUE
;
1206 if (return_status
== FILE_RETRY
)
1214 /* Failed rename -> copy the file instead */
1217 /* In case of single file, calculate totals. In case of many files,
1218 totals are calcuated already. */
1220 panel_operate_init_totals (panel
, src_vpath
, &src_stats
, ctx
, TRUE
,
1221 FILEGUI_DIALOG_ONE_ITEM
);
1222 if (return_status
!= FILE_CONT
)
1226 old_ask_overwrite
= tctx
->ask_overwrite
;
1227 tctx
->ask_overwrite
= FALSE
;
1228 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
1229 tctx
->ask_overwrite
= old_ask_overwrite
;
1230 if (return_status
!= FILE_CONT
)
1235 /* FIXME: there is no need to update progress and check buttons
1236 at the finish of single file operation. */
1239 file_progress_show_source (ctx
, NULL
);
1240 file_progress_show (ctx
, 0, 0, "", FALSE
);
1242 return_status
= check_progress_buttons (ctx
);
1243 if (return_status
!= FILE_CONT
)
1250 if (!try_remove_file (ctx
, src_vpath
, &return_status
) && panel
== NULL
)
1254 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
1257 vfs_path_free (src_vpath
);
1258 vfs_path_free (dst_vpath
);
1260 return return_status
;
1265 /* --------------------------------------------------------------------------------------------- */
1266 /* {{{ Erase routines */
1267 /** Don't update progress status if progress_count==NULL */
1269 static FileProgressStatus
1270 erase_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1273 FileProgressStatus return_status
;
1275 file_progress_show_deleting (ctx
, vfs_path_as_str (vpath
), &tctx
->progress_count
);
1276 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1277 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1282 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
1284 /* ignore, most likely the mc_unlink fails, too */
1288 if (!try_remove_file (ctx
, vpath
, &return_status
) && return_status
== FILE_ABORT
)
1291 if (tctx
->progress_count
== 0)
1294 return check_progress_buttons (ctx
);
1297 /* --------------------------------------------------------------------------------------------- */
1299 static FileProgressStatus
1300 try_erase_dir (file_op_context_t
* ctx
, const char *dir
)
1302 FileProgressStatus return_status
= FILE_CONT
;
1304 while (my_rmdir (dir
) != 0 && !ctx
->skip_all
)
1306 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), dir
);
1307 if (return_status
== FILE_SKIPALL
)
1308 ctx
->skip_all
= TRUE
;
1309 if (return_status
!= FILE_RETRY
)
1313 return return_status
;
1316 /* --------------------------------------------------------------------------------------------- */
1319 Recursive remove of files
1321 skip ->warn every level, gets default
1322 skipall->remove as much as possible
1324 static FileProgressStatus
1325 recursive_erase (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1327 struct dirent
*next
;
1330 FileProgressStatus return_status
= FILE_CONT
;
1332 reading
= mc_opendir (vpath
);
1333 if (reading
== NULL
)
1336 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1338 vfs_path_t
*tmp_vpath
;
1341 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
1344 tmp_vpath
= vfs_path_append_new (vpath
, next
->d_name
, (char *) NULL
);
1345 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1347 mc_closedir (reading
);
1348 vfs_path_free (tmp_vpath
);
1351 if (S_ISDIR (buf
.st_mode
))
1352 return_status
= recursive_erase (tctx
, ctx
, tmp_vpath
);
1354 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
1355 vfs_path_free (tmp_vpath
);
1357 mc_closedir (reading
);
1359 if (return_status
== FILE_ABORT
)
1362 s
= vfs_path_as_str (vpath
);
1364 file_progress_show_deleting (ctx
, s
, NULL
);
1365 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1366 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1371 return try_erase_dir (ctx
, s
);
1374 /* --------------------------------------------------------------------------------------------- */
1375 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1376 in the directory path points to, 0 else. */
1379 check_dir_is_empty (const vfs_path_t
* vpath
)
1385 dir
= mc_opendir (vpath
);
1389 for (d
= mc_readdir (dir
); d
!= NULL
; d
= mc_readdir (dir
))
1390 if (!DIR_IS_DOT (d
->d_name
) && !DIR_IS_DOTDOT (d
->d_name
))
1400 /* --------------------------------------------------------------------------------------------- */
1402 static FileProgressStatus
1403 erase_dir_iff_empty (file_op_context_t
* ctx
, const vfs_path_t
* vpath
, size_t count
)
1407 s
= vfs_path_as_str (vpath
);
1409 file_progress_show_deleting (ctx
, s
, NULL
);
1410 file_progress_show_count (ctx
, count
, ctx
->progress_count
);
1411 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1416 if (check_dir_is_empty (vpath
) != 1)
1419 /* not empty or error */
1420 return try_erase_dir (ctx
, s
);
1424 /* --------------------------------------------------------------------------------------------- */
1427 erase_dir_after_copy (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
1428 const vfs_path_t
* vpath
, FileProgressStatus
* status
)
1430 if (ctx
->erase_at_end
)
1432 /* Reset progress count before delete to avoid counting files twice */
1433 tctx
->progress_count
= tctx
->prev_progress_count
;
1435 while (erase_list
!= NULL
&& *status
!= FILE_ABORT
)
1437 struct link
*lp
= (struct link
*) erase_list
->data
;
1439 if (S_ISDIR (lp
->st_mode
))
1440 *status
= erase_dir_iff_empty (ctx
, lp
->src_vpath
, tctx
->progress_count
);
1442 *status
= erase_file (tctx
, ctx
, lp
->src_vpath
);
1444 erase_list
= g_slist_remove (erase_list
, lp
);
1448 /* Save progress counter before move next directory */
1449 tctx
->prev_progress_count
= tctx
->progress_count
;
1452 erase_dir_iff_empty (ctx
, vpath
, tctx
->progress_count
);
1457 /* --------------------------------------------------------------------------------------------- */
1460 * Move single directory or one of many directories from one location to another.
1462 * @panel pointer to panel in case of single directory, NULL otherwise
1463 * @tctx file operation total context object
1464 * @ctx file operation context object
1465 * @s source directory name
1466 * @d destination directory name
1468 * @return operation result
1470 static FileProgressStatus
1471 do_move_dir_dir (const WPanel
* panel
, file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
1472 const char *s
, const char *d
)
1474 struct stat sbuf
, dbuf
;
1475 FileProgressStatus return_status
= FILE_CONT
;
1476 gboolean move_over
= FALSE
;
1478 vfs_path_t
*src_vpath
, *dst_vpath
;
1480 src_vpath
= vfs_path_from_str (s
);
1481 dst_vpath
= vfs_path_from_str (d
);
1483 file_progress_show_source (ctx
, src_vpath
);
1484 file_progress_show_target (ctx
, dst_vpath
);
1486 /* FIXME: do we really need to check buttons in case of single directory? */
1487 if (panel
!= NULL
&& check_progress_buttons (ctx
) == FILE_ABORT
)
1489 return_status
= FILE_ABORT
;
1495 mc_stat (src_vpath
, &sbuf
);
1497 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
1499 if (dstat_ok
&& check_same_file (s
, &sbuf
, d
, &dbuf
, &return_status
))
1503 ; /* destination doesn't exist */
1504 else if (!ctx
->dive_into_subdirs
)
1511 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), (char *) NULL
);
1512 vfs_path_free (tmp
);
1515 d
= vfs_path_as_str (dst_vpath
);
1517 /* Check if the user inputted an existing dir */
1519 if (mc_stat (dst_vpath
, &dbuf
) == 0)
1523 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, TRUE
, TRUE
, NULL
);
1525 if (return_status
!= FILE_CONT
)
1529 else if (ctx
->skip_all
)
1530 return_status
= FILE_SKIPALL
;
1533 if (S_ISDIR (dbuf
.st_mode
))
1534 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), d
);
1536 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), d
);
1537 if (return_status
== FILE_SKIPALL
)
1538 ctx
->skip_all
= TRUE
;
1539 if (return_status
== FILE_RETRY
)
1540 goto retry_dst_stat
;
1547 if (mc_rename (src_vpath
, dst_vpath
) == 0)
1549 return_status
= FILE_CONT
;
1557 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
1558 if (return_status
== FILE_SKIPALL
)
1559 ctx
->skip_all
= TRUE
;
1560 if (return_status
== FILE_RETRY
)
1566 /* Failed because of filesystem boundary -> copy dir instead */
1569 /* In case of single directory, calculate totals. In case of many directories,
1570 totals are calcuated already. */
1572 panel_operate_init_totals (panel
, src_vpath
, &sbuf
, ctx
, FALSE
,
1573 FILEGUI_DIALOG_ONE_ITEM
);
1574 if (return_status
!= FILE_CONT
)
1578 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, FALSE
, TRUE
, NULL
);
1580 if (return_status
!= FILE_CONT
)
1584 /* FIXME: there is no need to update progress and check buttons
1585 at the finish of single directory operation. */
1588 file_progress_show_source (ctx
, NULL
);
1589 file_progress_show_target (ctx
, NULL
);
1590 file_progress_show (ctx
, 0, 0, "", FALSE
);
1592 return_status
= check_progress_buttons (ctx
);
1593 if (return_status
!= FILE_CONT
)
1599 erase_dir_after_copy (tctx
, ctx
, src_vpath
, &return_status
);
1602 erase_list
= free_linklist (erase_list
);
1604 vfs_path_free (src_vpath
);
1605 vfs_path_free (dst_vpath
);
1606 return return_status
;
1609 /* --------------------------------------------------------------------------------------------- */
1611 /* {{{ Panel operate routines */
1614 * Return currently selected entry name or the name of the first marked
1615 * entry if there is one.
1619 panel_get_file (const WPanel
* panel
)
1621 if (get_current_type () == view_tree
)
1624 const vfs_path_t
*selected_name
;
1626 tree
= (WTree
*) get_panel_widget (get_current_index ());
1627 selected_name
= tree_selected_name (tree
);
1628 return vfs_path_as_str (selected_name
);
1631 if (panel
->marked
!= 0)
1635 for (i
= 0; i
< panel
->dir
.len
; i
++)
1636 if (panel
->dir
.list
[i
].f
.marked
)
1637 return panel
->dir
.list
[i
].fname
;
1640 return panel
->dir
.list
[panel
->selected
].fname
;
1643 /* --------------------------------------------------------------------------------------------- */
1646 check_single_entry (const WPanel
* panel
, gboolean force_single
, struct stat
*src_stat
)
1652 source
= selection (panel
)->fname
;
1654 source
= panel_get_file (panel
);
1656 ok
= !DIR_IS_DOTDOT (source
);
1659 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
1662 vfs_path_t
*source_vpath
;
1664 source_vpath
= vfs_path_from_str (source
);
1666 /* Update stat to get actual info */
1667 ok
= mc_lstat (source_vpath
, src_stat
) == 0;
1670 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
1671 path_trunc (source
, 30), unix_error_string (errno
));
1673 /* Directory was changed outside MC. Reload it forced */
1674 if (!panel
->is_panelized
)
1676 panel_update_flags_t flags
= UP_RELOAD
;
1678 /* don't update panelized panel */
1679 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
1680 flags
|= UP_ONLY_CURRENT
;
1682 update_panels (flags
, UP_KEEPSEL
);
1686 vfs_path_free (source_vpath
);
1689 return ok
? source
: NULL
;
1692 /* --------------------------------------------------------------------------------------------- */
1694 * Generate user prompt for panel operation.
1695 * src_stat must be not NULL for single source, and NULL for multiple sources
1699 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1700 const struct stat
*src_stat
)
1703 char *format_string
;
1706 static gboolean i18n_flag
= FALSE
;
1711 for (i
= G_N_ELEMENTS (op_names1
); i
-- != 0;)
1712 op_names1
[i
] = Q_ (op_names1
[i
]);
1715 for (i
= G_N_ELEMENTS (prompt_parts
); i
-- != 0;)
1716 prompt_parts
[i
] = _(prompt_parts
[i
]);
1718 one_format
= _(one_format
);
1719 many_format
= _(many_format
);
1720 #endif /* ENABLE_NLS */
1724 /* Possible prompts:
1726 * "Copy file \"%s\" with source mask:"
1727 * "Copy %d files with source mask:"
1728 * "Copy directory \"%s\" with source mask:"
1729 * "Copy %d directories with source mask:"
1730 * "Copy %d files/directories with source mask:"
1732 * "Move file \"%s\" with source mask:"
1733 * "Move %d files with source mask:"
1734 * "Move directory \"%s\" with source mask:"
1735 * "Move %d directories with source mask:"
1736 * "Move %d files/directories with source mask:"
1738 * "Delete file \"%s\"?"
1739 * "Delete %d files?"
1740 * "Delete directory \"%s\"?"
1741 * "Delete %d directories?"
1742 * "Delete %d files/directories?"
1745 cp
= (src_stat
!= NULL
? one_format
: many_format
);
1747 /* 1. Substitute %o */
1748 format_string
= str_replace_all (cp
, "%o", op_names1
[(int) operation
]);
1750 /* 2. Substitute %n */
1751 cp
= operation
== OP_DELETE
? "\n" : " ";
1753 format_string
= str_replace_all (sp
, "%n", cp
);
1756 /* 3. Substitute %f */
1757 if (src_stat
!= NULL
)
1758 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1759 else if (panel
->marked
== panel
->dirs_marked
)
1760 cp
= prompt_parts
[3];
1762 cp
= panel
->dirs_marked
!= 0 ? prompt_parts
[4] : prompt_parts
[1];
1765 format_string
= str_replace_all (sp
, "%f", cp
);
1768 /* 4. Substitute %m */
1769 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1771 format_string
= str_replace_all (sp
, "%m", cp
);
1774 return format_string
;
1777 /* --------------------------------------------------------------------------------------------- */
1780 do_confirm_copy_move (const WPanel
* panel
, FileOperation operation
, gboolean force_single
,
1781 const char *source
, struct stat
*src_stat
, file_op_context_t
* ctx
,
1784 const char *tmp_dest_dir
;
1789 /* Forced single operations default to the original name */
1791 tmp_dest_dir
= source
;
1792 else if (get_other_type () == view_listing
)
1793 tmp_dest_dir
= vfs_path_as_str (other_panel
->cwd_vpath
);
1795 tmp_dest_dir
= vfs_path_as_str (panel
->cwd_vpath
);
1798 * Add trailing backslash only when do non-local ops.
1799 * It saves user from occasional file renames (when destination
1802 if (!force_single
&& tmp_dest_dir
!= NULL
&& tmp_dest_dir
[0] != '\0'
1803 && !IS_PATH_SEP (tmp_dest_dir
[strlen (tmp_dest_dir
) - 1]))
1805 /* add trailing separator */
1806 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
1811 dest_dir
= g_strdup (tmp_dest_dir
);
1814 if (dest_dir
== NULL
)
1820 /* Generate confirmation prompt */
1821 format
= panel_operate_generate_prompt (panel
, operation
, src_stat
);
1823 ret
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
1824 source
!= NULL
? source
: (const void *) &panel
->marked
, dest_dir
,
1833 /* --------------------------------------------------------------------------------------------- */
1836 do_confirm_erase (const WPanel
* panel
, const char *source
, struct stat
*src_stat
)
1840 char fmd_buf
[BUF_MEDIUM
];
1845 /* Generate confirmation prompt */
1846 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, src_stat
);
1849 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
1852 const int fmd_xlen
= 64;
1854 i
= fmd_xlen
- str_term_width1 (format
) - 4;
1855 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
1863 i
= query_dialog (op_names
[OP_DELETE
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
1868 /* --------------------------------------------------------------------------------------------- */
1870 static FileProgressStatus
1871 operate_single_file (const WPanel
* panel
, FileOperation operation
, file_op_total_context_t
* tctx
,
1872 file_op_context_t
* ctx
, const char *src
, struct stat
*src_stat
,
1873 const char *dest
, filegui_dialog_type_t dialog_type
)
1875 FileProgressStatus value
;
1876 vfs_path_t
*src_vpath
;
1879 if (g_path_is_absolute (src
))
1880 src_vpath
= vfs_path_from_str (src
);
1882 src_vpath
= vfs_path_append_new (panel
->cwd_vpath
, src
, (char *) NULL
);
1884 is_file
= !S_ISDIR (src_stat
->st_mode
);
1885 /* Is link to directory? */
1890 is_link
= file_is_symlink_to_dir (src_vpath
, src_stat
, NULL
);
1891 is_file
= !(is_link
&& ctx
->follow_links
);
1895 if (operation
== OP_DELETE
)
1897 value
= panel_operate_init_totals (panel
, src_vpath
, src_stat
, ctx
, !is_file
, dialog_type
);
1898 if (value
== FILE_CONT
)
1901 value
= erase_file (tctx
, ctx
, src_vpath
);
1903 value
= erase_dir (tctx
, ctx
, src_vpath
);
1910 src
= vfs_path_as_str (src_vpath
);
1912 temp
= build_dest (ctx
, src
, dest
, &value
);
1920 /* we use file_mask_op_follow_links only with OP_COPY */
1921 ctx
->stat_func (src_vpath
, src_stat
);
1924 panel_operate_init_totals (panel
, src_vpath
, src_stat
, ctx
, !is_file
,
1926 if (value
== FILE_CONT
)
1928 is_file
= !S_ISDIR (src_stat
->st_mode
);
1929 /* Is link to directory? */
1934 is_link
= file_is_symlink_to_dir (src_vpath
, src_stat
, NULL
);
1935 is_file
= !(is_link
&& ctx
->follow_links
);
1939 value
= copy_file_file (tctx
, ctx
, src
, dest
);
1941 value
= copy_dir_dir (tctx
, ctx
, src
, dest
, TRUE
, FALSE
, FALSE
, NULL
);
1946 #ifdef ENABLE_BACKGROUND
1947 /* create UI to show confirmation dialog */
1948 if (!mc_global
.we_are_background
)
1949 file_op_context_create_ui (ctx
, TRUE
, FILEGUI_DIALOG_ONE_ITEM
);
1952 value
= move_file_file (panel
, tctx
, ctx
, src
, dest
);
1954 value
= do_move_dir_dir (panel
, tctx
, ctx
, src
, dest
);
1958 /* Unknown file operation */
1966 vfs_path_free (src_vpath
);
1971 /* --------------------------------------------------------------------------------------------- */
1973 static FileProgressStatus
1974 operate_one_file (const WPanel
* panel
, FileOperation operation
, file_op_total_context_t
* tctx
,
1975 file_op_context_t
* ctx
, const char *src
, struct stat
*src_stat
, const char *dest
)
1977 FileProgressStatus value
= FILE_CONT
;
1978 vfs_path_t
*src_vpath
;
1981 if (g_path_is_absolute (src
))
1982 src_vpath
= vfs_path_from_str (src
);
1984 src_vpath
= vfs_path_append_new (panel
->cwd_vpath
, src
, (char *) NULL
);
1986 is_file
= !S_ISDIR (src_stat
->st_mode
);
1988 if (operation
== OP_DELETE
)
1991 value
= erase_file (tctx
, ctx
, src_vpath
);
1993 value
= erase_dir (tctx
, ctx
, src_vpath
);
1999 src
= vfs_path_as_str (src_vpath
);
2001 temp
= build_dest (ctx
, src
, dest
, &value
);
2009 /* we use file_mask_op_follow_links only with OP_COPY */
2010 ctx
->stat_func (src_vpath
, src_stat
);
2011 is_file
= !S_ISDIR (src_stat
->st_mode
);
2014 value
= copy_file_file (tctx
, ctx
, src
, dest
);
2016 value
= copy_dir_dir (tctx
, ctx
, src
, dest
, TRUE
, FALSE
, FALSE
, NULL
);
2017 dest_dirs
= free_linklist (dest_dirs
);
2022 value
= move_file_file (NULL
, tctx
, ctx
, src
, dest
);
2024 value
= do_move_dir_dir (NULL
, tctx
, ctx
, src
, dest
);
2028 /* Unknown file operation */
2036 vfs_path_free (src_vpath
);
2041 /* --------------------------------------------------------------------------------------------- */
2043 #ifdef ENABLE_BACKGROUND
2045 end_bg_process (file_op_context_t
* ctx
, enum OperationMode mode
)
2052 unregister_task_with_pid (pid
);
2053 /* file_op_context_destroy(ctx); */
2059 /* --------------------------------------------------------------------------------------------- */
2060 /*** public functions ****************************************************************************/
2061 /* --------------------------------------------------------------------------------------------- */
2063 /* Is file symlink to directory or not.
2065 * @param path file or directory
2066 * @param st result of mc_lstat(vpath). If NULL, mc_lstat(vpath) is performed here
2067 * @param stale_link TRUE if file is stale link to directory
2069 * @return TRUE if file symlink to directory, ELSE otherwise.
2072 file_is_symlink_to_dir (const vfs_path_t
* vpath
, struct stat
* st
, gboolean
* stale_link
)
2075 gboolean stale
= FALSE
;
2076 gboolean res
= FALSE
;
2082 if (mc_lstat (vpath
, st
) != 0)
2086 if (S_ISLNK (st
->st_mode
))
2090 stale
= (mc_stat (vpath
, &st3
) != 0);
2093 res
= (S_ISDIR (st3
.st_mode
) != 0);
2097 if (stale_link
!= NULL
)
2098 *stale_link
= stale
;
2103 /* --------------------------------------------------------------------------------------------- */
2106 copy_file_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
2107 const char *src_path
, const char *dst_path
)
2109 uid_t src_uid
= (uid_t
) (-1);
2110 gid_t src_gid
= (gid_t
) (-1);
2112 int src_desc
, dest_desc
= -1;
2113 mode_t src_mode
= 0; /* The mode of the source file */
2114 struct stat src_stat
, dst_stat
;
2115 mc_timesbuf_t times
;
2116 gboolean dst_exists
= FALSE
, appending
= FALSE
;
2117 off_t file_size
= -1;
2118 FileProgressStatus return_status
, temp_status
;
2119 struct timeval tv_transfer_start
;
2120 dest_status_t dst_status
= DEST_NONE
;
2122 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
2125 /* FIXME: We should not be using global variables! */
2127 return_status
= FILE_RETRY
;
2129 dst_vpath
= vfs_path_from_str (dst_path
);
2130 src_vpath
= vfs_path_from_str (src_path
);
2132 file_progress_show_source (ctx
, src_vpath
);
2133 file_progress_show_target (ctx
, dst_vpath
);
2135 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2137 return_status
= FILE_ABORT
;
2143 while (mc_stat (dst_vpath
, &dst_stat
) == 0)
2145 if (S_ISDIR (dst_stat
.st_mode
))
2148 return_status
= FILE_SKIPALL
;
2151 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
2152 if (return_status
== FILE_SKIPALL
)
2153 ctx
->skip_all
= TRUE
;
2154 if (return_status
== FILE_RETRY
)
2164 while ((*ctx
->stat_func
) (src_vpath
, &src_stat
) != 0)
2167 return_status
= FILE_SKIPALL
;
2170 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
2171 if (return_status
== FILE_SKIPALL
)
2172 ctx
->skip_all
= TRUE
;
2175 if (return_status
!= FILE_RETRY
)
2181 /* Destination already exists */
2182 if (check_same_file (src_path
, &src_stat
, dst_path
, &dst_stat
, &return_status
))
2185 /* Should we replace destination? */
2186 if (tctx
->ask_overwrite
)
2189 return_status
= query_replace (ctx
, dst_path
, &src_stat
, &dst_stat
);
2190 if (return_status
!= FILE_CONT
)
2195 if (!ctx
->do_append
)
2197 /* Check the hardlinks */
2198 if (!ctx
->follow_links
&& check_hardlinks (src_vpath
, &src_stat
, dst_vpath
))
2200 /* We have made a hardlink - no more processing is necessary */
2201 return_status
= FILE_CONT
;
2205 if (S_ISLNK (src_stat
.st_mode
))
2207 return_status
= make_symlink (ctx
, src_path
, dst_path
);
2211 if (S_ISCHR (src_stat
.st_mode
) || S_ISBLK (src_stat
.st_mode
) || S_ISFIFO (src_stat
.st_mode
)
2212 || S_ISNAM (src_stat
.st_mode
) || S_ISSOCK (src_stat
.st_mode
))
2216 #ifdef HAVE_STRUCT_STAT_ST_RDEV
2217 rdev
= src_stat
.st_rdev
;
2220 while (mc_mknod (dst_vpath
, src_stat
.st_mode
& ctx
->umask_kill
, rdev
) < 0
2223 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
2224 if (return_status
== FILE_RETRY
)
2226 if (return_status
== FILE_SKIPALL
)
2227 ctx
->skip_all
= TRUE
;
2232 while (ctx
->preserve_uidgid
2233 && mc_chown (dst_vpath
, src_stat
.st_uid
, src_stat
.st_gid
) != 0 && !ctx
->skip_all
)
2235 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
2236 if (temp_status
== FILE_SKIP
)
2238 if (temp_status
== FILE_SKIPALL
)
2239 ctx
->skip_all
= TRUE
;
2240 if (temp_status
!= FILE_RETRY
)
2242 return_status
= temp_status
;
2247 while (ctx
->preserve
&& mc_chmod (dst_vpath
, src_stat
.st_mode
& ctx
->umask_kill
) != 0
2250 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
2251 if (temp_status
== FILE_SKIP
)
2253 if (temp_status
== FILE_SKIPALL
)
2254 ctx
->skip_all
= TRUE
;
2255 if (temp_status
!= FILE_RETRY
)
2257 return_status
= temp_status
;
2262 return_status
= FILE_CONT
;
2267 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
2269 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
2271 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
2272 if (return_status
== FILE_RETRY
)
2274 if (return_status
== FILE_SKIPALL
)
2275 ctx
->skip_all
= TRUE
;
2276 if (return_status
== FILE_SKIP
)
2278 ctx
->do_append
= FALSE
;
2282 if (ctx
->do_reget
!= 0)
2284 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
2286 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
2288 ctx
->do_append
= FALSE
;
2292 while (mc_fstat (src_desc
, &src_stat
) != 0)
2295 return_status
= FILE_SKIPALL
;
2298 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
2299 if (return_status
== FILE_RETRY
)
2301 if (return_status
== FILE_SKIPALL
)
2302 ctx
->skip_all
= TRUE
;
2303 ctx
->do_append
= FALSE
;
2308 src_mode
= src_stat
.st_mode
;
2309 src_uid
= src_stat
.st_uid
;
2310 src_gid
= src_stat
.st_gid
;
2311 get_times (&src_stat
, ×
);
2312 file_size
= src_stat
.st_size
;
2314 open_flags
= O_WRONLY
;
2318 open_flags
|= O_APPEND
;
2320 open_flags
|= O_CREAT
| O_TRUNC
;
2324 open_flags
|= O_CREAT
| O_EXCL
;
2327 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
2329 if (errno
!= EEXIST
)
2332 return_status
= FILE_SKIPALL
;
2335 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
2336 if (return_status
== FILE_RETRY
)
2338 if (return_status
== FILE_SKIPALL
)
2339 ctx
->skip_all
= TRUE
;
2340 ctx
->do_append
= FALSE
;
2345 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
2347 appending
= ctx
->do_append
;
2348 ctx
->do_append
= FALSE
;
2350 /* Try clone the file first. */
2351 if (vfs_clone_file (dest_desc
, src_desc
) == 0)
2353 dst_status
= DEST_FULL
;
2354 return_status
= FILE_CONT
;
2358 /* Find out the optimal buffer size. */
2359 while (mc_fstat (dest_desc
, &dst_stat
) != 0)
2362 return_status
= FILE_SKIPALL
;
2365 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
2366 if (return_status
== FILE_RETRY
)
2368 if (return_status
== FILE_SKIPALL
)
2369 ctx
->skip_all
= TRUE
;
2374 /* try preallocate space; if fail, try copy anyway */
2375 while (mc_global
.vfs
.preallocate_space
&&
2376 vfs_preallocate (dest_desc
, file_size
, appending
? dst_stat
.st_size
: 0) != 0)
2380 /* cannot allocate, start the file copying anyway */
2381 return_status
= FILE_CONT
;
2386 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
2388 if (return_status
== FILE_SKIPALL
)
2389 ctx
->skip_all
= TRUE
;
2391 if (ctx
->skip_all
|| return_status
== FILE_SKIP
)
2393 /* skip the space allocation error, start file copying */
2394 return_status
= FILE_CONT
;
2398 if (return_status
== FILE_ABORT
)
2400 mc_close (dest_desc
);
2402 mc_unlink (dst_vpath
);
2403 dst_status
= DEST_NONE
;
2407 /* return_status == FILE_RETRY -- try allocate space again */
2410 ctx
->eta_secs
= 0.0;
2413 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
2414 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
2416 file_progress_show (ctx
, 1, 1, "", TRUE
);
2417 return_status
= check_progress_buttons (ctx
);
2420 if (return_status
== FILE_CONT
)
2423 off_t n_read_total
= 0;
2424 struct timeval tv_current
, tv_last_update
, tv_last_input
;
2425 int secs
, update_secs
;
2426 const char *stalled_msg
= "";
2427 gboolean is_first_time
= TRUE
;
2429 tv_last_update
= tv_transfer_start
;
2431 bufsize
= io_blksize (dst_stat
);
2432 buf
= g_malloc (bufsize
);
2436 ssize_t n_read
= -1, n_written
;
2439 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0) == 0)
2440 while ((n_read
= mc_read (src_desc
, buf
, bufsize
)) < 0 && !ctx
->skip_all
)
2442 return_status
= file_error (_("Cannot read source file \"%s\"\n%s"), src_path
);
2443 if (return_status
== FILE_RETRY
)
2445 if (return_status
== FILE_SKIPALL
)
2446 ctx
->skip_all
= TRUE
;
2453 gettimeofday (&tv_current
, NULL
);
2459 n_read_total
+= n_read
;
2461 /* Windows NT ftp servers report that files have no
2462 * permissions: -------, so if we happen to have actually
2463 * read something, we should fix the permissions.
2465 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
2466 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
2467 gettimeofday (&tv_last_input
, NULL
);
2470 while ((n_written
= mc_write (dest_desc
, t
, (size_t) n_read
)) < n_read
)
2472 gboolean write_errno_nospace
;
2476 n_read
-= n_written
;
2481 write_errno_nospace
= (n_written
< 0 && errno
== ENOSPC
);
2484 return_status
= FILE_SKIPALL
;
2487 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
2489 if (return_status
== FILE_SKIP
)
2491 if (write_errno_nospace
)
2495 if (return_status
== FILE_SKIPALL
)
2497 ctx
->skip_all
= TRUE
;
2498 if (write_errno_nospace
)
2501 if (return_status
!= FILE_RETRY
)
2506 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
2508 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
2509 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
2511 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
2513 copy_file_file_display_progress (tctx
, ctx
,
2515 tv_transfer_start
, file_size
, n_read_total
);
2516 tv_last_update
= tv_current
;
2518 is_first_time
= FALSE
;
2520 if (update_secs
> FILEOP_STALLING_INTERVAL
)
2522 stalled_msg
= _("(stalled)");
2526 gboolean force_update
;
2529 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
2531 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
2533 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2534 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
2537 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
2542 return_status
= check_progress_buttons (ctx
);
2544 if (return_status
!= FILE_CONT
)
2551 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
2557 rotate_dash (FALSE
);
2558 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
2560 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
2561 if (temp_status
== FILE_RETRY
)
2563 if (temp_status
== FILE_ABORT
)
2564 return_status
= temp_status
;
2565 if (temp_status
== FILE_SKIPALL
)
2566 ctx
->skip_all
= TRUE
;
2570 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
2572 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
2573 if (temp_status
== FILE_RETRY
)
2575 if (temp_status
== FILE_SKIPALL
)
2576 ctx
->skip_all
= TRUE
;
2577 return_status
= temp_status
;
2581 if (dst_status
== DEST_SHORT
)
2583 /* Query to remove short file */
2584 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"),
2585 D_ERROR
, 2, _("&Delete"), _("&Keep")) == 0)
2586 mc_unlink (dst_vpath
);
2588 else if (dst_status
== DEST_FULL
)
2590 /* Copy has succeeded */
2591 if (!appending
&& ctx
->preserve_uidgid
)
2593 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
2595 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
2596 if (temp_status
== FILE_RETRY
)
2598 if (temp_status
== FILE_SKIPALL
)
2600 ctx
->skip_all
= TRUE
;
2601 return_status
= FILE_CONT
;
2603 if (temp_status
== FILE_SKIP
)
2604 return_status
= FILE_CONT
;
2613 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
2615 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
2616 if (temp_status
== FILE_RETRY
)
2618 if (temp_status
== FILE_SKIPALL
)
2620 ctx
->skip_all
= TRUE
;
2621 return_status
= FILE_CONT
;
2623 if (temp_status
== FILE_SKIP
)
2624 return_status
= FILE_CONT
;
2628 else if (!dst_exists
)
2630 src_mode
= umask (-1);
2632 src_mode
= 0100666 & ~src_mode
;
2633 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
2635 mc_utime (dst_vpath
, ×
);
2639 if (return_status
== FILE_CONT
)
2640 return_status
= progress_update_one (tctx
, ctx
, file_size
);
2643 vfs_path_free (src_vpath
);
2644 vfs_path_free (dst_vpath
);
2645 return return_status
;
2648 /* --------------------------------------------------------------------------------------------- */
2650 * I think these copy_*_* functions should have a return type.
2651 * anyway, this function *must* have two directories as arguments.
2653 /* FIXME: This function needs to check the return values of the
2657 copy_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
,
2658 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, GSList
* parent_dirs
)
2660 struct dirent
*next
;
2661 struct stat buf
, cbuf
;
2663 FileProgressStatus return_status
= FILE_CONT
;
2665 vfs_path_t
*src_vpath
, *dst_vpath
;
2666 gboolean do_mkdir
= TRUE
;
2668 src_vpath
= vfs_path_from_str (s
);
2669 dst_vpath
= vfs_path_from_str (d
);
2671 /* First get the mode of the source dir */
2674 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
2677 return_status
= FILE_SKIPALL
;
2680 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
2681 if (return_status
== FILE_RETRY
)
2682 goto retry_src_stat
;
2683 if (return_status
== FILE_SKIPALL
)
2684 ctx
->skip_all
= TRUE
;
2689 if (is_in_linklist (dest_dirs
, src_vpath
, &cbuf
))
2691 /* Don't copy a directory we created before (we don't want to copy
2692 infinitely if a directory is copied into itself) */
2693 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2694 return_status
= FILE_CONT
;
2698 /* Hmm, hardlink to directory??? - Norbert */
2699 /* FIXME: In this step we should do something in case the destination already exist */
2700 /* Check the hardlinks */
2701 if (ctx
->preserve
&& check_hardlinks (src_vpath
, &cbuf
, dst_vpath
))
2703 /* We have made a hardlink - no more processing is necessary */
2707 if (!S_ISDIR (cbuf
.st_mode
))
2710 return_status
= FILE_SKIPALL
;
2713 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
2714 if (return_status
== FILE_RETRY
)
2715 goto retry_src_stat
;
2716 if (return_status
== FILE_SKIPALL
)
2717 ctx
->skip_all
= TRUE
;
2722 if (is_in_linklist (parent_dirs
, src_vpath
, &cbuf
))
2724 /* we found a cyclic symbolic link */
2725 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
2726 return_status
= FILE_SKIP
;
2730 lp
= g_new0 (struct link
, 1);
2731 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
2732 lp
->ino
= cbuf
.st_ino
;
2733 lp
->dev
= cbuf
.st_dev
;
2734 parent_dirs
= g_slist_prepend (parent_dirs
, lp
);
2737 /* Now, check if the dest dir exists, if not, create it. */
2738 if (mc_stat (dst_vpath
, &buf
) != 0)
2740 /* Here the dir doesn't exist : make it ! */
2741 if (move_over
&& mc_rename (src_vpath
, dst_vpath
) == 0)
2743 return_status
= FILE_CONT
;
2750 * If the destination directory exists, we want to copy the whole
2751 * directory, but we only want this to happen once.
2753 * Escape sequences added to the * to compiler warnings.
2754 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2755 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2757 if (!S_ISDIR (buf
.st_mode
))
2760 return_status
= FILE_SKIPALL
;
2763 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2764 if (return_status
== FILE_SKIPALL
)
2765 ctx
->skip_all
= TRUE
;
2766 if (return_status
== FILE_RETRY
)
2767 goto retry_dst_stat
;
2771 /* Dive into subdir if exists */
2772 if (toplevel
&& ctx
->dive_into_subdirs
)
2777 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), (char *) NULL
);
2778 vfs_path_free (tmp
);
2785 d
= vfs_path_as_str (dst_vpath
);
2789 while (my_mkdir (dst_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2792 return_status
= FILE_SKIPALL
;
2795 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), d
);
2796 if (return_status
== FILE_SKIPALL
)
2797 ctx
->skip_all
= TRUE
;
2799 if (return_status
!= FILE_RETRY
)
2803 lp
= g_new0 (struct link
, 1);
2804 mc_stat (dst_vpath
, &buf
);
2805 lp
->vfs
= vfs_path_get_by_index (dst_vpath
, -1)->class;
2806 lp
->ino
= buf
.st_ino
;
2807 lp
->dev
= buf
.st_dev
;
2808 dest_dirs
= g_slist_prepend (dest_dirs
, lp
);
2811 if (ctx
->preserve_uidgid
)
2813 while (mc_chown (dst_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2816 return_status
= FILE_SKIPALL
;
2819 return_status
= file_error (_("Cannot chown target directory \"%s\"\n%s"), d
);
2820 if (return_status
== FILE_SKIPALL
)
2821 ctx
->skip_all
= TRUE
;
2823 if (return_status
!= FILE_RETRY
)
2828 /* open the source dir for reading */
2829 reading
= mc_opendir (src_vpath
);
2830 if (reading
== NULL
)
2833 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2836 vfs_path_t
*tmp_vpath
;
2839 * Now, we don't want '.' and '..' to be created / copied at any time
2841 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
2844 /* get the filename and add it to the src directory */
2845 path
= mc_build_filename (s
, next
->d_name
, (char *) NULL
);
2846 tmp_vpath
= vfs_path_from_str (path
);
2848 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2849 if (S_ISDIR (buf
.st_mode
))
2853 mdpath
= mc_build_filename (d
, next
->d_name
, (char *) NULL
);
2855 * From here, we just intend to recursively copy subdirs, not
2856 * the double functionality of copying different when the target
2857 * dir already exists. So, we give the recursive call the flag 0
2858 * meaning no toplevel.
2861 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2868 dest_file
= mc_build_filename (d
, x_basename (path
), (char *) NULL
);
2869 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2875 if (do_delete
&& return_status
== FILE_CONT
)
2877 if (ctx
->erase_at_end
)
2879 lp
= g_new0 (struct link
, 1);
2880 lp
->src_vpath
= tmp_vpath
;
2881 lp
->st_mode
= buf
.st_mode
;
2882 erase_list
= g_slist_append (erase_list
, lp
);
2885 else if (S_ISDIR (buf
.st_mode
))
2886 return_status
= erase_dir_iff_empty (ctx
, tmp_vpath
, tctx
->progress_count
);
2888 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
2890 vfs_path_free (tmp_vpath
);
2892 mc_closedir (reading
);
2896 mc_timesbuf_t times
;
2898 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2899 get_times (&cbuf
, ×
);
2900 mc_utime (dst_vpath
, ×
);
2904 cbuf
.st_mode
= umask (-1);
2905 umask (cbuf
.st_mode
);
2906 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2907 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2911 free_link (parent_dirs
->data
);
2912 g_slist_free_1 (parent_dirs
);
2914 vfs_path_free (src_vpath
);
2915 vfs_path_free (dst_vpath
);
2916 return return_status
;
2921 /* --------------------------------------------------------------------------------------------- */
2922 /* {{{ Move routines */
2925 move_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
)
2927 return do_move_dir_dir (NULL
, tctx
, ctx
, s
, d
);
2932 /* --------------------------------------------------------------------------------------------- */
2933 /* {{{ Erase routines */
2936 erase_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* s_vpath
)
2938 file_progress_show_deleting (ctx
, vfs_path_as_str (s_vpath
), NULL
);
2939 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2940 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2945 /* The old way to detect a non empty directory was:
2946 error = my_rmdir (s);
2947 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2948 For the linux user space nfs server (nfs-server-2.2beta29-2)
2949 we would have to check also for EIO. I hope the new way is
2950 fool proof. (Norbert)
2952 if (check_dir_is_empty (s_vpath
) == 0)
2954 FileProgressStatus error
;
2956 error
= query_recursive (ctx
, vfs_path_as_str (s_vpath
));
2957 if (error
== FILE_CONT
)
2958 error
= recursive_erase (tctx
, ctx
, s_vpath
);
2962 return try_erase_dir (ctx
, vfs_path_as_str (s_vpath
));
2967 /* --------------------------------------------------------------------------------------------- */
2968 /* {{{ Panel operate routines */
2971 dirsize_status_init_cb (status_msg_t
* sm
)
2973 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
2974 Widget
*wd
= WIDGET (sm
->dlg
);
2976 const char *b1_name
= N_("&Abort");
2977 const char *b2_name
= N_("&Skip");
2978 int b_width
, ui_width
;
2981 b1_name
= _(b1_name
);
2982 b2_name
= _(b2_name
);
2985 b_width
= str_term_width1 (b1_name
) + 4;
2986 if (dsm
->allow_skip
)
2987 b_width
+= str_term_width1 (b2_name
) + 4 + 1;
2989 ui_width
= MAX (COLS
/ 2, b_width
+ 6);
2990 dsm
->dirname
= label_new (2, 3, "");
2991 add_widget (sm
->dlg
, dsm
->dirname
);
2992 dsm
->count_size
= label_new (3, 3, "");
2993 add_widget (sm
->dlg
, dsm
->count_size
);
2994 add_widget (sm
->dlg
, hline_new (4, -1, -1));
2996 dsm
->abort_button
= WIDGET (button_new (5, 3, FILE_ABORT
, NORMAL_BUTTON
, b1_name
, NULL
));
2997 add_widget (sm
->dlg
, dsm
->abort_button
);
2998 if (dsm
->allow_skip
)
3000 dsm
->skip_button
= WIDGET (button_new (5, 3, FILE_SKIP
, NORMAL_BUTTON
, b2_name
, NULL
));
3001 add_widget (sm
->dlg
, dsm
->skip_button
);
3002 widget_select (dsm
->skip_button
);
3005 widget_set_size (wd
, wd
->y
, wd
->x
, 8, ui_width
);
3006 dirsize_status_locate_buttons (dsm
);
3009 /* --------------------------------------------------------------------------------------------- */
3012 dirsize_status_update_cb (status_msg_t
* sm
)
3014 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
3015 Widget
*wd
= WIDGET (sm
->dlg
);
3017 /* update second (longer label) */
3018 label_set_textv (dsm
->count_size
, _("Directories: %zu, total size: %s"),
3019 dsm
->dir_count
, size_trunc_sep (dsm
->total_size
, panels_options
.kilobyte_si
));
3021 /* enlarge dialog if required */
3022 if (WIDGET (dsm
->count_size
)->cols
+ 6 > wd
->cols
)
3024 dlg_set_size (sm
->dlg
, wd
->lines
, WIDGET (dsm
->count_size
)->cols
+ 6);
3025 dirsize_status_locate_buttons (dsm
);
3026 dlg_redraw (sm
->dlg
);
3029 /* adjust first label */
3030 label_set_text (dsm
->dirname
, str_trunc (vfs_path_as_str (dsm
->dirname_vpath
), wd
->cols
- 6));
3032 switch (status_msg_common_update (sm
))
3044 /* --------------------------------------------------------------------------------------------- */
3047 dirsize_status_deinit_cb (status_msg_t
* sm
)
3051 /* schedule to update passive panel */
3052 if (get_other_type () == view_listing
)
3053 other_panel
->dirty
= 1;
3056 /* --------------------------------------------------------------------------------------------- */
3060 * Computes the number of bytes used by the files in a directory
3064 compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* sm
,
3065 size_t * ret_dir_count
, size_t * ret_marked_count
, uintmax_t * ret_total
,
3066 gboolean compute_symlinks
)
3068 return do_compute_dir_size (dirname_vpath
, sm
, ret_dir_count
, ret_marked_count
, ret_total
,
3072 /* --------------------------------------------------------------------------------------------- */
3076 * Performs one of the operations on the selection on the source_panel
3077 * (copy, delete, move).
3079 * Returns TRUE if did change the directory
3080 * structure, Returns FALSE if user aborted
3082 * force_single forces operation on the current entry and affects
3083 * default destination. Current filename is used as default.
3087 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
3089 WPanel
*panel
= PANEL (source_panel
);
3090 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
3091 || (get_current_type () == view_tree
);
3093 const char *source
= NULL
;
3095 vfs_path_t
*dest_vpath
= NULL
;
3096 char *save_cwd
= NULL
, *save_dest
= NULL
;
3097 struct stat src_stat
;
3098 gboolean ret_val
= TRUE
;
3100 FileProgressStatus value
;
3101 file_op_context_t
*ctx
;
3102 file_op_total_context_t
*tctx
;
3103 vfs_path_t
*tmp_vpath
;
3104 filegui_dialog_type_t dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
3106 gboolean do_bg
= FALSE
; /* do background operation? */
3108 static gboolean i18n_flag
= FALSE
;
3111 for (i
= G_N_ELEMENTS (op_names
); i
-- != 0;)
3112 op_names
[i
] = Q_ (op_names
[i
]);
3116 linklist
= free_linklist (linklist
);
3117 dest_dirs
= free_linklist (dest_dirs
);
3121 source
= check_single_entry (panel
, force_single
, &src_stat
);
3127 ctx
= file_op_context_new (operation
);
3129 /* Show confirmation dialog */
3130 if (operation
!= OP_DELETE
)
3133 do_confirm_copy_move (panel
, operation
, force_single
, source
, &src_stat
, ctx
, &do_bg
);
3141 dest_vpath
= vfs_path_from_str (dest
);
3143 else if (confirm_delete
&& !do_confirm_erase (panel
, source
, &src_stat
))
3149 tctx
= file_op_total_context_new ();
3150 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
3152 #ifdef ENABLE_BACKGROUND
3153 /* Did the user select to do a background operation? */
3158 v
= do_background (ctx
,
3159 g_strconcat (op_names
[operation
], ": ",
3160 vfs_path_as_str (panel
->cwd_vpath
), (char *) NULL
));
3162 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
3164 /* If we are the parent */
3167 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
3169 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
3170 vfs_path_free (dest_vpath
);
3172 /* file_op_context_destroy (ctx); */
3177 #endif /* ENABLE_BACKGROUND */
3179 if (operation
== OP_DELETE
)
3180 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
3181 else if (single_entry
&& S_ISDIR (selection (panel
)->st
.st_mode
))
3182 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
3183 else if (single_entry
|| force_single
)
3184 dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
3186 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
3189 /* Initialize things */
3190 /* We do not want to trash cache every time file is
3191 created/touched. However, this will make our cache contain
3194 && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
3195 save_dest
= g_strdup (dest
);
3197 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
3198 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
3199 save_cwd
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
3201 /* Now, let's do the job */
3203 /* This code is only called by the tree and panel code */
3206 /* We now have ETA in all cases */
3208 /* One file: FIXME mc_chdir will take user out of any vfs */
3209 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
3214 vpath
= vfs_path_from_str (PATH_SEP_STR
);
3215 chdir_retcode
= mc_chdir (vpath
);
3216 vfs_path_free (vpath
);
3217 if (chdir_retcode
< 0)
3225 operate_single_file (panel
, operation
, tctx
, ctx
, source
, &src_stat
, dest
, dialog_type
);
3227 if ((value
== FILE_CONT
) && !force_single
)
3228 unmark_files (panel
);
3234 /* Check destination for copy or move operation */
3235 while (operation
!= OP_DELETE
)
3238 struct stat dst_stat
;
3240 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
3242 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
3246 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
3250 /* TODO: the good way is required to skip directories scanning in case of rename/move
3251 * of several directories. Since reqular expression can be used for destination,
3252 * some directory movements can be a cross-filesystem and directory scanning is useful
3253 * for those directories only. */
3255 if (panel_operate_init_totals (panel
, NULL
, NULL
, ctx
, file_op_compute_totals
, dialog_type
)
3258 /* Loop for every file, perform the actual copy operation */
3259 for (i
= 0; i
< panel
->dir
.len
; i
++)
3261 const char *source2
;
3263 if (!panel
->dir
.list
[i
].f
.marked
)
3264 continue; /* Skip the unmarked ones */
3266 source2
= panel
->dir
.list
[i
].fname
;
3267 src_stat
= panel
->dir
.list
[i
].st
;
3269 value
= operate_one_file (panel
, operation
, tctx
, ctx
, source2
, &src_stat
, dest
);
3271 if (value
== FILE_ABORT
)
3274 if (value
== FILE_CONT
)
3275 do_file_mark (panel
, i
, 0);
3277 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3279 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3280 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3283 if (operation
!= OP_DELETE
)
3284 file_progress_show (ctx
, 0, 0, "", FALSE
);
3286 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3290 } /* Loop for every file */
3292 } /* Many entries */
3296 if (save_cwd
!= NULL
)
3298 tmp_vpath
= vfs_path_from_str (save_cwd
);
3299 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3300 vfs_path_free (tmp_vpath
);
3304 if (save_dest
!= NULL
)
3306 tmp_vpath
= vfs_path_from_str (save_dest
);
3307 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3308 vfs_path_free (tmp_vpath
);
3312 linklist
= free_linklist (linklist
);
3313 dest_dirs
= free_linklist (dest_dirs
);
3315 vfs_path_free (dest_vpath
);
3316 MC_PTR_FREE (ctx
->dest_mask
);
3318 #ifdef ENABLE_BACKGROUND
3319 /* Let our parent know we are saying bye bye */
3320 if (mc_global
.we_are_background
)
3322 int cur_pid
= getpid ();
3323 /* Send pid to parent with child context, it is fork and
3324 don't modify real parent ctx */
3326 parent_call ((void *) end_bg_process
, ctx
, 0);
3329 my_exit (EXIT_SUCCESS
);
3331 #endif /* ENABLE_BACKGROUND */
3333 file_op_total_context_destroy (tctx
);
3335 file_op_context_destroy (ctx
);
3342 /* --------------------------------------------------------------------------------------------- */
3343 /* {{{ Query/status report routines */
3344 /** Report error with one file */
3346 file_error (const char *format
, const char *file
)
3348 char buf
[BUF_MEDIUM
];
3350 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3352 return do_file_error (buf
);
3355 /* --------------------------------------------------------------------------------------------- */
3358 Cause emacs to enter folding mode for this file: