4 Copyright (C) 1994-2016
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() */
90 /*** global variables ****************************************************************************/
92 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
93 const char *op_names
[3] = {
94 N_("DialogTitle|Copy"),
95 N_("DialogTitle|Move"),
96 N_("DialogTitle|Delete")
99 /*** file scope macro definitions ****************************************************************/
101 /* Hack: the vfs code should not rely on this */
102 #define WITH_FULL_PATHS 1
104 #define FILEOP_UPDATE_INTERVAL 2
105 #define FILEOP_STALLING_INTERVAL 4
107 /*** file scope type declarations ****************************************************************/
109 /* This is a hard link cache */
112 const struct vfs_class
*vfs
;
117 vfs_path_t
*src_vpath
;
118 vfs_path_t
*dst_vpath
;
121 /* Status of the destination file */
124 DEST_NONE
= 0, /* Not created */
125 DEST_SHORT
= 1, /* Created, not fully copied */
126 DEST_FULL
= 2 /* Created, fully copied */
130 * This array introduced to avoid translation problems. The former (op_names)
131 * is assumed to be nouns, suitable in dialog box titles; this one should
132 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
133 * (I don't use spaces around the words, because someday they could be
134 * dropped, when widgets get smarter)
137 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
138 static const char *op_names1
[] = {
139 N_("FileOperation|Copy"),
140 N_("FileOperation|Move"),
141 N_("FileOperation|Delete")
145 * These are formats for building a prompt. Parts encoded as follows:
146 * %o - operation from op_names1
147 * %f - file/files or files/directories, as appropriate
148 * %m - "with source mask" or question mark for delete
149 * %s - source name (truncated)
150 * %d - number of marked files
151 * %n - the '\n' symbol to form two-line prompt for delete or space for other operations
153 /* xgettext:no-c-format */
154 static const char *one_format
= N_("%o %f%n\"%s\"%m");
155 /* xgettext:no-c-format */
156 static const char *many_format
= N_("%o %d %f%m");
158 static const char *prompt_parts
[] = {
163 N_("files/directories"),
164 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
165 N_(" with source mask:")
168 /*** file scope variables ************************************************************************/
170 /* the hard link cache */
171 static GSList
*linklist
= NULL
;
173 /* the files-to-be-erased list */
174 static GSList
*erase_list
= NULL
;
177 * In copy_dir_dir we use two additional single linked lists: The first -
178 * variable name 'parent_dirs' - holds information about already copied
179 * directories and is used to detect cyclic symbolic links.
180 * The second ('dest_dirs' below) holds information about just created
181 * target directories and is used to detect when an directory is copied
182 * into itself (we don't want to copy infinitly).
183 * Both lists don't use the linkcount and name structure members of struct
186 static GSList
*dest_dirs
= NULL
;
188 static FileProgressStatus transform_error
= FILE_CONT
;
190 /* --------------------------------------------------------------------------------------------- */
191 /*** file scope functions ************************************************************************/
192 /* --------------------------------------------------------------------------------------------- */
195 dirsize_status_locate_buttons (dirsize_status_msg_t
* dsm
)
197 status_msg_t
*sm
= STATUS_MSG (dsm
);
198 Widget
*wd
= WIDGET (sm
->dlg
);
204 if (!dsm
->allow_skip
)
206 /* single button: "Abort" */
207 x
+= (wd
->cols
- dsm
->abort_button
->cols
) / 2;
208 widget_set_size (dsm
->abort_button
, y
, x
,
209 dsm
->abort_button
->lines
, dsm
->abort_button
->cols
);
213 /* two buttons: "Abort" and "Skip" */
216 cols
= dsm
->abort_button
->cols
+ dsm
->skip_button
->cols
+ 1;
217 x
+= (wd
->cols
- cols
) / 2;
218 widget_set_size (dsm
->abort_button
, y
, x
, dsm
->abort_button
->lines
,
219 dsm
->abort_button
->cols
);
220 x
+= dsm
->abort_button
->cols
+ 1;
221 widget_set_size (dsm
->skip_button
, y
, x
, dsm
->skip_button
->lines
, dsm
->skip_button
->cols
);
225 /* --------------------------------------------------------------------------------------------- */
228 transform_source (file_op_context_t
* ctx
, const vfs_path_t
* source_vpath
)
233 s
= g_strdup (vfs_path_as_str (source_vpath
));
235 /* We remove \n from the filename since regex routines would use \n as an anchor */
236 /* this is just to be allowed to maniupulate file names with \n on it */
237 for (q
= s
; *q
!= '\0'; q
++)
241 fnsource
= (char *) x_basename (s
);
243 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
245 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
246 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
248 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
250 transform_error
= FILE_ABORT
;
256 transform_error
= FILE_SKIP
;
263 /* --------------------------------------------------------------------------------------------- */
266 free_link (void *data
)
268 struct link
*lp
= (struct link
*) data
;
270 vfs_path_free (lp
->src_vpath
);
271 vfs_path_free (lp
->dst_vpath
);
275 /* --------------------------------------------------------------------------------------------- */
278 free_linklist (GSList
* lp
)
280 g_slist_free_full (lp
, free_link
);
285 /* --------------------------------------------------------------------------------------------- */
288 is_in_linklist (const GSList
* lp
, const vfs_path_t
* vpath
, const struct stat
*sb
)
290 const struct vfs_class
*class;
291 ino_t ino
= sb
->st_ino
;
292 dev_t dev
= sb
->st_dev
;
294 class = vfs_path_get_last_path_vfs (vpath
);
296 for (; lp
!= NULL
; lp
= g_slist_next (lp
))
298 const struct link
*lnk
= (const struct link
*) lp
->data
;
300 if (lnk
->vfs
== class && lnk
->ino
== ino
&& lnk
->dev
== dev
)
306 /* --------------------------------------------------------------------------------------------- */
308 * Check and made hardlink
310 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
311 * and a hardlink was successfully made
315 check_hardlinks (const vfs_path_t
* src_vpath
, const vfs_path_t
* dst_vpath
, struct stat
*pstat
)
320 const struct vfs_class
*my_vfs
;
321 ino_t ino
= pstat
->st_ino
;
322 dev_t dev
= pstat
->st_dev
;
323 struct stat link_stat
;
325 if ((vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
328 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
330 for (lp
= linklist
; lp
!= NULL
; lp
= g_slist_next (lp
))
332 lnk
= (struct link
*) lp
->data
;
334 if (lnk
->vfs
== my_vfs
&& lnk
->ino
== ino
&& lnk
->dev
== dev
)
336 const struct vfs_class
*lp_name_class
;
339 lp_name_class
= vfs_path_get_last_path_vfs (lnk
->src_vpath
);
340 stat_result
= mc_stat (lnk
->src_vpath
, &link_stat
);
342 if (stat_result
== 0 && link_stat
.st_ino
== ino
343 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
345 const struct vfs_class
*p_class
, *dst_name_class
;
347 dst_name_class
= vfs_path_get_last_path_vfs (dst_vpath
);
348 p_class
= vfs_path_get_last_path_vfs (lnk
->dst_vpath
);
350 if (dst_name_class
== p_class
&&
351 mc_stat (lnk
->dst_vpath
, &link_stat
) == 0 &&
352 mc_link (lnk
->dst_vpath
, dst_vpath
) == 0)
356 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
361 lnk
= g_new0 (struct link
, 1);
367 lnk
->src_vpath
= vfs_path_clone (src_vpath
);
368 lnk
->dst_vpath
= vfs_path_clone (dst_vpath
);
369 linklist
= g_slist_prepend (linklist
, lnk
);
375 /* --------------------------------------------------------------------------------------------- */
377 * Duplicate the contents of the symbolic link src_path in dst_path.
378 * Try to make a stable symlink if the option "stable symlink" was
379 * set in the file mask dialog.
380 * If dst_path is an existing symlink it will be deleted silently
381 * (upper levels take already care of existing files at dst_path).
384 static FileProgressStatus
385 make_symlink (file_op_context_t
* ctx
, const char *src_path
, const char *dst_path
)
387 char link_target
[MC_MAXPATHLEN
];
389 FileProgressStatus return_status
;
391 vfs_path_t
*src_vpath
;
392 vfs_path_t
*dst_vpath
;
393 gboolean dst_is_symlink
;
394 vfs_path_t
*link_target_vpath
= NULL
;
396 src_vpath
= vfs_path_from_str (src_path
);
397 dst_vpath
= vfs_path_from_str (dst_path
);
398 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
401 len
= mc_readlink (src_vpath
, link_target
, MC_MAXPATHLEN
- 1);
405 return_status
= FILE_SKIPALL
;
408 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
409 if (return_status
== FILE_SKIPALL
)
410 ctx
->skip_all
= TRUE
;
411 if (return_status
== FILE_RETRY
)
412 goto retry_src_readlink
;
416 link_target
[len
] = 0;
418 if (ctx
->stable_symlinks
)
421 if (!vfs_file_is_local (src_vpath
) || !vfs_file_is_local (dst_vpath
))
423 message (D_ERROR
, MSG_ERROR
,
424 _("Cannot make stable symlinks across"
425 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
426 ctx
->stable_symlinks
= FALSE
;
430 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
432 const char *r
= strrchr (src_path
, PATH_SEP
);
439 p
= g_strndup (src_path
, r
- src_path
+ 1);
440 if (g_path_is_absolute (dst_path
))
441 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
443 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
445 if (vfs_path_tokens_count (q
) > 1)
448 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
450 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
451 s
= g_strconcat (p
, link_target
, (char *) NULL
);
453 g_strlcpy (link_target
, s
, sizeof (link_target
));
455 tmp_vpath2
= vfs_path_from_str (link_target
);
456 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
457 vfs_path_free (tmp_vpath1
);
458 vfs_path_free (tmp_vpath2
);
461 g_strlcpy (link_target
, s
, sizeof (link_target
));
470 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
473 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
476 return_status
= FILE_CONT
;
480 * if dst_exists, it is obvious that this had failed.
481 * We can delete the old symlink and try again...
485 if (mc_unlink (dst_vpath
) == 0)
486 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
489 return_status
= FILE_CONT
;
494 return_status
= FILE_SKIPALL
;
497 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
498 if (return_status
== FILE_SKIPALL
)
499 ctx
->skip_all
= TRUE
;
500 if (return_status
== FILE_RETRY
)
501 goto retry_dst_symlink
;
505 vfs_path_free (src_vpath
);
506 vfs_path_free (dst_vpath
);
507 vfs_path_free (link_target_vpath
);
508 return return_status
;
511 /* --------------------------------------------------------------------------------------------- */
513 * do_compute_dir_size:
515 * Computes the number of bytes used by the files in a directory
518 static FileProgressStatus
519 do_compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* dsm
,
520 size_t * dir_count
, size_t * ret_marked
, uintmax_t * ret_total
,
521 gboolean compute_symlinks
)
523 static guint64 timestamp
= 0;
524 /* update with 25 FPS rate */
525 static const guint64 delay
= G_USEC_PER_SEC
/ 25;
527 status_msg_t
*sm
= STATUS_MSG (dsm
);
531 struct dirent
*dirent
;
532 FileProgressStatus ret
= FILE_CONT
;
534 if (!compute_symlinks
)
536 res
= mc_lstat (dirname_vpath
, &s
);
540 /* don't scan symlink to directory */
541 if (S_ISLNK (s
.st_mode
))
544 *ret_total
+= (uintmax_t) s
.st_size
;
551 dir
= mc_opendir (dirname_vpath
);
555 while (ret
== FILE_CONT
&& (dirent
= mc_readdir (dir
)) != NULL
)
557 vfs_path_t
*tmp_vpath
;
559 if (DIR_IS_DOT (dirent
->d_name
) || DIR_IS_DOTDOT (dirent
->d_name
))
562 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, NULL
);
564 res
= mc_lstat (tmp_vpath
, &s
);
567 if (S_ISDIR (s
.st_mode
))
569 do_compute_dir_size (tmp_vpath
, dsm
, dir_count
, ret_marked
, ret_total
,
576 *ret_total
+= (uintmax_t) s
.st_size
;
579 if (ret
== FILE_CONT
&& sm
->update
!= NULL
&& mc_time_elapsed (×tamp
, delay
))
581 dsm
->dirname_vpath
= tmp_vpath
;
582 dsm
->dir_count
= *dir_count
;
583 dsm
->total_size
= *ret_total
;
584 ret
= sm
->update (sm
);
588 vfs_path_free (tmp_vpath
);
595 /* --------------------------------------------------------------------------------------------- */
597 static FileProgressStatus
598 progress_update_one (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, off_t add
)
600 struct timeval tv_current
;
601 static struct timeval tv_start
= { 0, 0 };
603 tctx
->progress_count
++;
604 tctx
->progress_bytes
+= (uintmax_t) add
;
606 if (tv_start
.tv_sec
== 0)
608 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
610 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
611 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
613 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
615 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
616 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
618 tv_start
.tv_sec
= tv_current
.tv_sec
;
621 return check_progress_buttons (ctx
);
624 /* --------------------------------------------------------------------------------------------- */
626 static FileProgressStatus
627 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
631 const char *head_msg
;
633 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
635 msg
= g_strdup_printf (fmt
, a
, b
);
636 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
640 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
643 /* --------------------------------------------------------------------------------------------- */
645 static FileProgressStatus
646 warn_same_file (const char *fmt
, const char *a
, const char *b
)
648 #ifdef ENABLE_BACKGROUND
653 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
, const char *a
, const char *b
);
657 pntr
.f
= real_warn_same_file
;
659 if (mc_global
.we_are_background
)
660 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
662 return real_warn_same_file (Foreground
, fmt
, a
, b
);
665 /* --------------------------------------------------------------------------------------------- */
666 /* {{{ Query/status report routines */
668 static FileProgressStatus
669 real_do_file_error (enum OperationMode mode
, const char *error
)
674 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
676 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
698 /* --------------------------------------------------------------------------------------------- */
700 static FileProgressStatus
701 real_query_recursive (file_op_context_t
* ctx
, enum OperationMode mode
, const char *s
)
703 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
708 msg
= mode
== Foreground
709 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
710 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
711 text
= g_strdup_printf (msg
, path_trunc (s
, 30));
716 ctx
->recursive_result
=
717 query_dialog (op_names
[OP_DELETE
], text
, D_ERROR
, 5, _("&Yes"), _("&No"), _("A&ll"),
718 _("Non&e"), _("&Abort"));
721 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
725 switch (ctx
->recursive_result
)
728 case RECURSIVE_ALWAYS
:
732 case RECURSIVE_NEVER
:
735 case RECURSIVE_ABORT
:
741 /* --------------------------------------------------------------------------------------------- */
743 #ifdef ENABLE_BACKGROUND
744 static FileProgressStatus
745 do_file_error (const char *str
)
751 FileProgressStatus (*f
) (enum OperationMode
, const char *);
755 pntr
.f
= real_do_file_error
;
757 if (mc_global
.we_are_background
)
758 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
760 return real_do_file_error (Foreground
, str
);
763 /* --------------------------------------------------------------------------------------------- */
765 static FileProgressStatus
766 query_recursive (file_op_context_t
* ctx
, const char *s
)
772 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *);
776 pntr
.f
= real_query_recursive
;
778 if (mc_global
.we_are_background
)
779 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
781 return real_query_recursive (ctx
, Foreground
, s
);
784 /* --------------------------------------------------------------------------------------------- */
786 static FileProgressStatus
787 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
788 struct stat
*_d_stat
)
794 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *,
795 struct stat
*, struct stat
*);
799 pntr
.f
= file_progress_real_query_replace
;
801 if (mc_global
.we_are_background
)
802 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
803 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
805 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
809 /* --------------------------------------------------------------------------------------------- */
811 static FileProgressStatus
812 do_file_error (const char *str
)
814 return real_do_file_error (Foreground
, str
);
817 /* --------------------------------------------------------------------------------------------- */
819 static FileProgressStatus
820 query_recursive (file_op_context_t
* ctx
, const char *s
)
822 return real_query_recursive (ctx
, Foreground
, s
);
825 /* --------------------------------------------------------------------------------------------- */
827 static FileProgressStatus
828 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
829 struct stat
*_d_stat
)
831 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
834 #endif /* !ENABLE_BACKGROUND */
836 /* --------------------------------------------------------------------------------------------- */
837 /** Report error with two files */
839 static FileProgressStatus
840 files_error (const char *format
, const char *file1
, const char *file2
)
842 char buf
[BUF_MEDIUM
];
843 char *nfile1
= g_strdup (path_trunc (file1
, 15));
844 char *nfile2
= g_strdup (path_trunc (file2
, 15));
846 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
851 return do_file_error (buf
);
856 /* --------------------------------------------------------------------------------------------- */
859 copy_file_file_display_progress (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
860 struct timeval tv_current
, struct timeval tv_transfer_start
,
861 off_t file_size
, off_t n_read_total
)
865 /* 1. Update rotating dash after some time */
869 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
871 if (n_read_total
== 0)
875 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
876 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
879 /* 4. Compute BPS rate */
880 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
881 if (ctx
->bps_time
< 1)
883 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
885 /* 5. Compute total ETA and BPS */
886 if (ctx
->progress_bytes
!= 0)
888 uintmax_t remain_bytes
;
890 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
893 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
898 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
899 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
902 /* broken on lot of little files */
904 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
905 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
910 /* --------------------------------------------------------------------------------------------- */
912 /* {{{ Move routines */
913 static FileProgressStatus
914 move_file_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
,
917 struct stat src_stats
, dst_stats
;
918 FileProgressStatus return_status
= FILE_CONT
;
919 gboolean copy_done
= FALSE
;
920 gboolean old_ask_overwrite
;
921 vfs_path_t
*src_vpath
, *dst_vpath
;
923 src_vpath
= vfs_path_from_str (s
);
924 dst_vpath
= vfs_path_from_str (d
);
926 file_progress_show_source (ctx
, src_vpath
);
927 file_progress_show_target (ctx
, dst_vpath
);
929 if (check_progress_buttons (ctx
) == FILE_ABORT
)
931 return_status
= FILE_ABORT
;
937 while (mc_lstat (src_vpath
, &src_stats
) != 0)
939 /* Source doesn't exist */
941 return_status
= FILE_SKIPALL
;
944 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
945 if (return_status
== FILE_SKIPALL
)
946 ctx
->skip_all
= TRUE
;
949 if (return_status
!= FILE_RETRY
)
953 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
955 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
957 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
961 if (S_ISDIR (dst_stats
.st_mode
))
963 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
965 return_status
= FILE_SKIP
;
969 if (confirm_overwrite
)
971 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
972 if (return_status
!= FILE_CONT
)
975 /* Ok to overwrite */
980 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
982 return_status
= make_symlink (ctx
, s
, d
);
983 if (return_status
== FILE_CONT
)
984 goto retry_src_remove
;
988 if (mc_rename (src_vpath
, dst_vpath
) == 0)
990 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
995 /* Comparison to EXDEV seems not to work in nfs if you're moving from
996 one nfs to the same, but on the server it is on two different
997 filesystems. Then nfs returns EIO instead of EXDEV.
998 Hope it will not hurt if we always in case of error try to copy/delete. */
1000 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1005 return_status
= FILE_SKIPALL
;
1008 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
1009 if (return_status
== FILE_SKIPALL
)
1010 ctx
->skip_all
= TRUE
;
1011 if (return_status
== FILE_RETRY
)
1019 /* Failed because filesystem boundary -> copy the file instead */
1020 old_ask_overwrite
= tctx
->ask_overwrite
;
1021 tctx
->ask_overwrite
= FALSE
;
1022 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
1023 tctx
->ask_overwrite
= old_ask_overwrite
;
1024 if (return_status
!= FILE_CONT
)
1029 file_progress_show_source (ctx
, NULL
);
1030 file_progress_show (ctx
, 0, 0, "", FALSE
);
1032 return_status
= check_progress_buttons (ctx
);
1033 if (return_status
!= FILE_CONT
)
1038 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
1040 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
1041 if (return_status
== FILE_RETRY
)
1042 goto retry_src_remove
;
1043 if (return_status
== FILE_SKIPALL
)
1044 ctx
->skip_all
= TRUE
;
1049 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
1052 vfs_path_free (src_vpath
);
1053 vfs_path_free (dst_vpath
);
1055 return return_status
;
1060 /* --------------------------------------------------------------------------------------------- */
1061 /* {{{ Erase routines */
1062 /** Don't update progress status if progress_count==NULL */
1064 static FileProgressStatus
1065 erase_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1069 file_progress_show_deleting (ctx
, vfs_path_as_str (vpath
), &tctx
->progress_count
);
1070 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1071 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1076 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
1078 /* ignore, most likely the mc_unlink fails, too */
1082 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
1086 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), vfs_path_as_str (vpath
));
1087 if (return_status
== FILE_ABORT
)
1088 return return_status
;
1089 if (return_status
== FILE_RETRY
)
1091 if (return_status
== FILE_SKIPALL
)
1092 ctx
->skip_all
= TRUE
;
1096 if (tctx
->progress_count
== 0)
1099 return check_progress_buttons (ctx
);
1102 /* --------------------------------------------------------------------------------------------- */
1105 Recursive remove of files
1107 skip ->warn every level, gets default
1108 skipall->remove as much as possible
1110 static FileProgressStatus
1111 recursive_erase (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1113 struct dirent
*next
;
1116 FileProgressStatus return_status
= FILE_CONT
;
1118 reading
= mc_opendir (vpath
);
1119 if (reading
== NULL
)
1122 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1124 vfs_path_t
*tmp_vpath
;
1127 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
1130 tmp_vpath
= vfs_path_append_new (vpath
, next
->d_name
, NULL
);
1131 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1133 mc_closedir (reading
);
1134 vfs_path_free (tmp_vpath
);
1137 if (S_ISDIR (buf
.st_mode
))
1138 return_status
= recursive_erase (tctx
, ctx
, tmp_vpath
);
1140 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
1141 vfs_path_free (tmp_vpath
);
1143 mc_closedir (reading
);
1145 if (return_status
== FILE_ABORT
)
1148 s
= vfs_path_as_str (vpath
);
1150 file_progress_show_deleting (ctx
, s
, NULL
);
1151 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1152 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1157 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1159 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1160 if (return_status
== FILE_RETRY
)
1162 if (return_status
== FILE_ABORT
)
1164 if (return_status
== FILE_SKIPALL
)
1165 ctx
->skip_all
= TRUE
;
1169 return return_status
;
1172 /* --------------------------------------------------------------------------------------------- */
1173 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1174 in the directory path points to, 0 else. */
1177 check_dir_is_empty (const vfs_path_t
* vpath
)
1183 dir
= mc_opendir (vpath
);
1187 for (d
= mc_readdir (dir
); d
!= NULL
; d
= mc_readdir (dir
))
1188 if (!DIR_IS_DOT (d
->d_name
) && !DIR_IS_DOTDOT (d
->d_name
))
1198 /* --------------------------------------------------------------------------------------------- */
1200 static FileProgressStatus
1201 erase_dir_iff_empty (file_op_context_t
* ctx
, const vfs_path_t
* vpath
, size_t count
)
1203 FileProgressStatus error
= FILE_CONT
;
1206 s
= vfs_path_as_str (vpath
);
1208 file_progress_show_deleting (ctx
, s
, NULL
);
1209 file_progress_show_count (ctx
, count
, ctx
->progress_count
);
1210 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1215 if (check_dir_is_empty (vpath
) == 1) /* not empty or error */
1217 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1219 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1220 if (error
== FILE_SKIPALL
)
1221 ctx
->skip_all
= TRUE
;
1222 if (error
!= FILE_RETRY
)
1232 /* --------------------------------------------------------------------------------------------- */
1233 /* {{{ Panel operate routines */
1236 * Return currently selected entry name or the name of the first marked
1237 * entry if there is one.
1241 panel_get_file (WPanel
* panel
)
1243 if (get_current_type () == view_tree
)
1246 vfs_path_t
*selected_name
;
1248 tree
= (WTree
*) get_panel_widget (get_current_index ());
1249 selected_name
= tree_selected_name (tree
);
1250 return g_strdup (vfs_path_as_str (selected_name
));
1253 if (panel
->marked
!= 0)
1257 for (i
= 0; i
< panel
->dir
.len
; i
++)
1258 if (panel
->dir
.list
[i
].f
.marked
)
1259 return g_strdup (panel
->dir
.list
[i
].fname
);
1261 return g_strdup (panel
->dir
.list
[panel
->selected
].fname
);
1264 /* --------------------------------------------------------------------------------------------- */
1266 * panel_compute_totals:
1268 * compute the number of files and the number of bytes
1269 * used up by the whole selection, recursing directories
1270 * as required. In addition, it checks to see if it will
1271 * overwrite any files by doing the copy.
1274 static FileProgressStatus
1275 panel_compute_totals (const WPanel
* panel
, dirsize_status_msg_t
* sm
, size_t * ret_count
,
1276 uintmax_t * ret_total
, gboolean compute_symlinks
)
1279 size_t dir_count
= 0;
1281 for (i
= 0; i
< panel
->dir
.len
; i
++)
1285 if (!panel
->dir
.list
[i
].f
.marked
)
1288 s
= &panel
->dir
.list
[i
].st
;
1290 if (S_ISDIR (s
->st_mode
))
1293 FileProgressStatus status
;
1295 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, NULL
);
1296 status
= compute_dir_size (p
, sm
, &dir_count
, ret_count
, ret_total
, compute_symlinks
);
1299 if (status
!= FILE_CONT
)
1305 *ret_total
+= (uintmax_t) s
->st_size
;
1312 /* --------------------------------------------------------------------------------------------- */
1314 /** Initialize variables for progress bars */
1315 static FileProgressStatus
1316 panel_operate_init_totals (const WPanel
* panel
, const char *source
, file_op_context_t
* ctx
,
1317 filegui_dialog_type_t dialog_type
)
1319 FileProgressStatus status
;
1321 #ifdef ENABLE_BACKGROUND
1322 if (mc_global
.we_are_background
)
1326 if (verbose
&& file_op_compute_totals
)
1328 dirsize_status_msg_t dsm
;
1330 memset (&dsm
, 0, sizeof (dsm
));
1331 dsm
.allow_skip
= TRUE
;
1332 status_msg_init (STATUS_MSG (&dsm
), _("Directory scanning"), 0, dirsize_status_init_cb
,
1333 dirsize_status_update_cb
, dirsize_status_deinit_cb
);
1335 ctx
->progress_count
= 0;
1336 ctx
->progress_bytes
= 0;
1339 status
= panel_compute_totals (panel
, &dsm
, &ctx
->progress_count
, &ctx
->progress_bytes
,
1344 size_t dir_count
= 0;
1346 p
= vfs_path_from_str (source
);
1347 status
= compute_dir_size (p
, &dsm
, &dir_count
, &ctx
->progress_count
,
1348 &ctx
->progress_bytes
, ctx
->follow_links
);
1352 status_msg_deinit (STATUS_MSG (&dsm
));
1354 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1356 if (status
== FILE_SKIP
)
1362 ctx
->progress_count
= panel
->marked
;
1363 ctx
->progress_bytes
= panel
->total
;
1364 ctx
->progress_totals_computed
= FALSE
;
1367 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
1372 /* --------------------------------------------------------------------------------------------- */
1374 * Generate user prompt for panel operation.
1375 * src_stat must be not NULL for single source, and NULL for multiple sources
1379 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1380 const struct stat
*src_stat
)
1383 char *format_string
;
1386 static gboolean i18n_flag
= FALSE
;
1391 for (i
= G_N_ELEMENTS (op_names1
); i
-- != 0;)
1392 op_names1
[i
] = Q_ (op_names1
[i
]);
1395 for (i
= G_N_ELEMENTS (prompt_parts
); i
-- != 0;)
1396 prompt_parts
[i
] = _(prompt_parts
[i
]);
1398 one_format
= _(one_format
);
1399 many_format
= _(many_format
);
1400 #endif /* ENABLE_NLS */
1404 /* Possible prompts:
1406 * "Copy file \"%s\" with source mask:"
1407 * "Copy %d files with source mask:"
1408 * "Copy directory \"%s\" with source mask:"
1409 * "Copy %d directories with source mask:"
1410 * "Copy %d files/directories with source mask:"
1412 * "Move file \"%s\" with source mask:"
1413 * "Move %d files with source mask:"
1414 * "Move directory \"%s\" with source mask:"
1415 * "Move %d directories with source mask:"
1416 * "Move %d files/directories with source mask:"
1418 * "Delete file \"%s\"?"
1419 * "Delete %d files?"
1420 * "Delete directory \"%s\"?"
1421 * "Delete %d directories?"
1422 * "Delete %d files/directories?"
1425 sp
= (char *) (src_stat
!= NULL
? one_format
: many_format
);
1427 /* 1. Substitute %o */
1428 format_string
= str_replace_all (sp
, "%o", op_names1
[(int) operation
]);
1430 /* 2. Substitute %n */
1431 cp
= operation
== OP_DELETE
? "\n" : " ";
1433 format_string
= str_replace_all (sp
, "%n", cp
);
1436 /* 3. Substitute %f */
1437 if (src_stat
!= NULL
)
1438 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1439 else if (panel
->marked
== panel
->dirs_marked
)
1440 cp
= prompt_parts
[3];
1442 cp
= panel
->dirs_marked
!= 0 ? prompt_parts
[4] : prompt_parts
[1];
1445 format_string
= str_replace_all (sp
, "%f", cp
);
1448 /* 4. Substitute %m */
1449 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1451 format_string
= str_replace_all (sp
, "%m", cp
);
1454 return format_string
;
1457 /* --------------------------------------------------------------------------------------------- */
1459 #ifdef ENABLE_BACKGROUND
1461 end_bg_process (file_op_context_t
* ctx
, enum OperationMode mode
)
1468 unregister_task_with_pid (pid
);
1469 /* file_op_context_destroy(ctx); */
1475 /* --------------------------------------------------------------------------------------------- */
1476 /*** public functions ****************************************************************************/
1477 /* --------------------------------------------------------------------------------------------- */
1480 copy_file_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
1481 const char *src_path
, const char *dst_path
)
1483 uid_t src_uid
= (uid_t
) (-1);
1484 gid_t src_gid
= (gid_t
) (-1);
1486 int src_desc
, dest_desc
= -1;
1487 int n_read
, n_written
;
1488 mode_t src_mode
= 0; /* The mode of the source file */
1489 struct stat sb
, sb2
;
1491 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1492 off_t file_size
= -1;
1493 FileProgressStatus return_status
, temp_status
;
1494 struct timeval tv_transfer_start
;
1495 dest_status_t dst_status
= DEST_NONE
;
1497 gboolean is_first_time
= TRUE
;
1498 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1500 /* FIXME: We should not be using global variables! */
1502 return_status
= FILE_RETRY
;
1504 dst_vpath
= vfs_path_from_str (dst_path
);
1505 src_vpath
= vfs_path_from_str (src_path
);
1507 file_progress_show_source (ctx
, src_vpath
);
1508 file_progress_show_target (ctx
, dst_vpath
);
1510 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1512 return_status
= FILE_ABORT
;
1518 while (mc_stat (dst_vpath
, &sb2
) == 0)
1520 if (S_ISDIR (sb2
.st_mode
))
1523 return_status
= FILE_SKIPALL
;
1526 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1527 if (return_status
== FILE_SKIPALL
)
1528 ctx
->skip_all
= TRUE
;
1529 if (return_status
== FILE_RETRY
)
1539 while ((*ctx
->stat_func
) (src_vpath
, &sb
) != 0)
1542 return_status
= FILE_SKIPALL
;
1545 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1546 if (return_status
== FILE_SKIPALL
)
1547 ctx
->skip_all
= TRUE
;
1550 if (return_status
!= FILE_RETRY
)
1556 /* Destination already exists */
1557 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1559 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1560 src_path
, dst_path
);
1564 /* Should we replace destination? */
1565 if (tctx
->ask_overwrite
)
1568 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1569 if (return_status
!= FILE_CONT
)
1574 if (!ctx
->do_append
)
1576 /* Check the hardlinks */
1577 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &sb
))
1579 /* We have made a hardlink - no more processing is necessary */
1580 return_status
= FILE_CONT
;
1584 if (S_ISLNK (sb
.st_mode
))
1586 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1590 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1591 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1593 while (mc_mknod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0
1596 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1597 if (return_status
== FILE_RETRY
)
1599 if (return_status
== FILE_SKIPALL
)
1600 ctx
->skip_all
= TRUE
;
1605 while (ctx
->preserve_uidgid
&& mc_chown (dst_vpath
, sb
.st_uid
, sb
.st_gid
) != 0
1608 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1609 if (temp_status
== FILE_SKIP
)
1611 if (temp_status
== FILE_SKIPALL
)
1612 ctx
->skip_all
= TRUE
;
1613 if (temp_status
!= FILE_RETRY
)
1615 return_status
= temp_status
;
1620 while (ctx
->preserve
&& mc_chmod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
) != 0
1623 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1624 if (temp_status
== FILE_SKIP
)
1626 if (temp_status
== FILE_SKIPALL
)
1627 ctx
->skip_all
= TRUE
;
1628 if (temp_status
!= FILE_RETRY
)
1630 return_status
= temp_status
;
1635 return_status
= FILE_CONT
;
1640 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1642 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1644 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1645 if (return_status
== FILE_RETRY
)
1647 if (return_status
== FILE_SKIPALL
)
1648 ctx
->skip_all
= TRUE
;
1649 if (return_status
== FILE_SKIP
)
1651 ctx
->do_append
= FALSE
;
1655 if (ctx
->do_reget
!= 0)
1657 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1659 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1661 ctx
->do_append
= FALSE
;
1665 while (mc_fstat (src_desc
, &sb
) != 0)
1668 return_status
= FILE_SKIPALL
;
1671 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1672 if (return_status
== FILE_RETRY
)
1674 if (return_status
== FILE_SKIPALL
)
1675 ctx
->skip_all
= TRUE
;
1676 ctx
->do_append
= FALSE
;
1681 src_mode
= sb
.st_mode
;
1682 src_uid
= sb
.st_uid
;
1683 src_gid
= sb
.st_gid
;
1684 utb
.actime
= sb
.st_atime
;
1685 utb
.modtime
= sb
.st_mtime
;
1686 file_size
= sb
.st_size
;
1688 open_flags
= O_WRONLY
;
1692 open_flags
|= O_APPEND
;
1694 open_flags
|= O_CREAT
| O_TRUNC
;
1698 open_flags
|= O_CREAT
| O_EXCL
;
1701 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1703 if (errno
!= EEXIST
)
1706 return_status
= FILE_SKIPALL
;
1709 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1710 if (return_status
== FILE_RETRY
)
1712 if (return_status
== FILE_SKIPALL
)
1713 ctx
->skip_all
= TRUE
;
1714 ctx
->do_append
= FALSE
;
1719 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1721 appending
= ctx
->do_append
;
1722 ctx
->do_append
= FALSE
;
1724 /* Find out the optimal buffer size. */
1725 while (mc_fstat (dest_desc
, &sb
) != 0)
1728 return_status
= FILE_SKIPALL
;
1731 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1732 if (return_status
== FILE_RETRY
)
1734 if (return_status
== FILE_SKIPALL
)
1735 ctx
->skip_all
= TRUE
;
1740 /* try preallocate space; if fail, try copy anyway */
1741 while (vfs_preallocate (dest_desc
, file_size
, appending
? sb
.st_size
: 0) != 0)
1745 /* cannot allocate, start the file copying anyway */
1746 return_status
= FILE_CONT
;
1751 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1753 if (return_status
== FILE_SKIPALL
)
1754 ctx
->skip_all
= TRUE
;
1756 if (ctx
->skip_all
|| return_status
== FILE_SKIP
)
1758 /* skip the space allocation error, start file copying */
1759 return_status
= FILE_CONT
;
1763 if (return_status
== FILE_ABORT
)
1765 mc_close (dest_desc
);
1767 mc_unlink (dst_vpath
);
1768 dst_status
= DEST_NONE
;
1772 /* return_status == FILE_RETRY -- try allocate space again */
1775 ctx
->eta_secs
= 0.0;
1778 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1779 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1781 file_progress_show (ctx
, 1, 1, "", TRUE
);
1782 return_status
= check_progress_buttons (ctx
);
1785 if (return_status
!= FILE_CONT
)
1789 off_t n_read_total
= 0;
1790 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1791 int secs
, update_secs
;
1792 const char *stalled_msg
= "";
1794 tv_last_update
= tv_transfer_start
;
1801 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1804 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0 && !ctx
->skip_all
)
1806 return_status
= file_error (_("Cannot read source file \"%s\"\n%s"), src_path
);
1807 if (return_status
== FILE_RETRY
)
1809 if (return_status
== FILE_SKIPALL
)
1810 ctx
->skip_all
= TRUE
;
1816 gettimeofday (&tv_current
, NULL
);
1821 n_read_total
+= n_read
;
1823 /* Windows NT ftp servers report that files have no
1824 * permissions: -------, so if we happen to have actually
1825 * read something, we should fix the permissions.
1827 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1828 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1829 gettimeofday (&tv_last_input
, NULL
);
1832 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
)
1834 gboolean write_errno_nospace
;
1838 n_read
-= n_written
;
1843 write_errno_nospace
= (n_written
< 0 && errno
== ENOSPC
);
1846 return_status
= FILE_SKIPALL
;
1849 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1851 if (return_status
== FILE_SKIP
)
1853 if (write_errno_nospace
)
1857 if (return_status
== FILE_SKIPALL
)
1859 ctx
->skip_all
= TRUE
;
1860 if (write_errno_nospace
)
1863 if (return_status
!= FILE_RETRY
)
1868 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1870 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1871 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1873 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1875 copy_file_file_display_progress (tctx
, ctx
,
1877 tv_transfer_start
, file_size
, n_read_total
);
1878 tv_last_update
= tv_current
;
1880 is_first_time
= FALSE
;
1882 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1884 stalled_msg
= _("(stalled)");
1888 gboolean force_update
;
1891 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1893 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1895 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1896 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1899 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1904 return_status
= check_progress_buttons (ctx
);
1906 if (return_status
!= FILE_CONT
)
1914 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1917 rotate_dash (FALSE
);
1918 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1920 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1921 if (temp_status
== FILE_RETRY
)
1923 if (temp_status
== FILE_ABORT
)
1924 return_status
= temp_status
;
1925 if (temp_status
== FILE_SKIPALL
)
1926 ctx
->skip_all
= TRUE
;
1930 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1932 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1933 if (temp_status
== FILE_RETRY
)
1935 if (temp_status
== FILE_SKIPALL
)
1936 ctx
->skip_all
= TRUE
;
1937 return_status
= temp_status
;
1941 if (dst_status
== DEST_SHORT
)
1943 /* Query to remove short file */
1944 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"),
1945 D_ERROR
, 2, _("&Delete"), _("&Keep")) == 0)
1946 mc_unlink (dst_vpath
);
1948 else if (dst_status
== DEST_FULL
)
1950 /* Copy has succeeded */
1951 if (!appending
&& ctx
->preserve_uidgid
)
1953 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1955 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1956 if (temp_status
== FILE_RETRY
)
1958 if (temp_status
== FILE_SKIPALL
)
1960 ctx
->skip_all
= TRUE
;
1961 return_status
= FILE_CONT
;
1963 if (temp_status
== FILE_SKIP
)
1964 return_status
= FILE_CONT
;
1973 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1975 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1976 if (temp_status
== FILE_RETRY
)
1978 if (temp_status
== FILE_SKIPALL
)
1980 ctx
->skip_all
= TRUE
;
1981 return_status
= FILE_CONT
;
1983 if (temp_status
== FILE_SKIP
)
1984 return_status
= FILE_CONT
;
1988 else if (!dst_exists
)
1990 src_mode
= umask (-1);
1992 src_mode
= 0100666 & ~src_mode
;
1993 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
1995 mc_utime (dst_vpath
, &utb
);
1999 if (return_status
== FILE_CONT
)
2000 return_status
= progress_update_one (tctx
, ctx
, file_size
);
2003 vfs_path_free (src_vpath
);
2004 vfs_path_free (dst_vpath
);
2005 return return_status
;
2008 /* --------------------------------------------------------------------------------------------- */
2010 * I think these copy_*_* functions should have a return type.
2011 * anyway, this function *must* have two directories as arguments.
2013 /* FIXME: This function needs to check the return values of the
2017 copy_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
,
2018 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, GSList
* parent_dirs
)
2020 struct dirent
*next
;
2021 struct stat buf
, cbuf
;
2023 FileProgressStatus return_status
= FILE_CONT
;
2025 vfs_path_t
*src_vpath
, *dst_vpath
;
2026 gboolean do_mkdir
= TRUE
;
2028 src_vpath
= vfs_path_from_str (s
);
2029 dst_vpath
= vfs_path_from_str (d
);
2031 /* First get the mode of the source dir */
2034 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
2037 return_status
= FILE_SKIPALL
;
2040 return_status
= file_error (_("Cannot stat source directory \"%s\"\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 (dest_dirs
, src_vpath
, &cbuf
))
2051 /* Don't copy a directory we created before (we don't want to copy
2052 infinitely if a directory is copied into itself) */
2053 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2054 return_status
= FILE_CONT
;
2058 /* Hmm, hardlink to directory??? - Norbert */
2059 /* FIXME: In this step we should do something
2060 in case the destination already exist */
2061 /* Check the hardlinks */
2062 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &cbuf
))
2064 /* We have made a hardlink - no more processing is necessary */
2068 if (!S_ISDIR (cbuf
.st_mode
))
2071 return_status
= FILE_SKIPALL
;
2074 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
2075 if (return_status
== FILE_RETRY
)
2076 goto retry_src_stat
;
2077 if (return_status
== FILE_SKIPALL
)
2078 ctx
->skip_all
= TRUE
;
2083 if (is_in_linklist (parent_dirs
, src_vpath
, &cbuf
))
2085 /* we found a cyclic symbolic link */
2086 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
2087 return_status
= FILE_SKIP
;
2091 lp
= g_new0 (struct link
, 1);
2092 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
2093 lp
->ino
= cbuf
.st_ino
;
2094 lp
->dev
= cbuf
.st_dev
;
2095 parent_dirs
= g_slist_prepend (parent_dirs
, lp
);
2098 /* Now, check if the dest dir exists, if not, create it. */
2099 if (mc_stat (dst_vpath
, &buf
) != 0)
2101 /* Here the dir doesn't exist : make it ! */
2102 if (move_over
&& mc_rename (src_vpath
, dst_vpath
) == 0)
2104 return_status
= FILE_CONT
;
2111 * If the destination directory exists, we want to copy the whole
2112 * directory, but we only want this to happen once.
2114 * Escape sequences added to the * to compiler warnings.
2115 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2116 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2118 if (!S_ISDIR (buf
.st_mode
))
2121 return_status
= FILE_SKIPALL
;
2124 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2125 if (return_status
== FILE_SKIPALL
)
2126 ctx
->skip_all
= TRUE
;
2127 if (return_status
== FILE_RETRY
)
2128 goto retry_dst_stat
;
2132 /* Dive into subdir if exists */
2133 if (toplevel
&& ctx
->dive_into_subdirs
)
2138 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), NULL
);
2139 vfs_path_free (tmp
);
2146 d
= vfs_path_as_str (dst_vpath
);
2150 while (my_mkdir (dst_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2153 return_status
= FILE_SKIPALL
;
2156 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), d
);
2157 if (return_status
== FILE_SKIPALL
)
2158 ctx
->skip_all
= TRUE
;
2160 if (return_status
!= FILE_RETRY
)
2164 lp
= g_new0 (struct link
, 1);
2165 mc_stat (dst_vpath
, &buf
);
2166 lp
->vfs
= vfs_path_get_by_index (dst_vpath
, -1)->class;
2167 lp
->ino
= buf
.st_ino
;
2168 lp
->dev
= buf
.st_dev
;
2169 dest_dirs
= g_slist_prepend (dest_dirs
, lp
);
2172 if (ctx
->preserve_uidgid
)
2174 while (mc_chown (dst_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2177 return_status
= FILE_SKIPALL
;
2180 return_status
= file_error (_("Cannot chown target directory \"%s\"\n%s"), d
);
2181 if (return_status
== FILE_SKIPALL
)
2182 ctx
->skip_all
= TRUE
;
2184 if (return_status
!= FILE_RETRY
)
2189 /* open the source dir for reading */
2190 reading
= mc_opendir (src_vpath
);
2191 if (reading
== NULL
)
2194 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2197 vfs_path_t
*tmp_vpath
;
2200 * Now, we don't want '.' and '..' to be created / copied at any time
2202 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
2205 /* get the filename and add it to the src directory */
2206 path
= mc_build_filename (s
, next
->d_name
, NULL
);
2207 tmp_vpath
= vfs_path_from_str (path
);
2209 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2210 if (S_ISDIR (buf
.st_mode
))
2214 mdpath
= mc_build_filename (d
, next
->d_name
, NULL
);
2216 * From here, we just intend to recursively copy subdirs, not
2217 * the double functionality of copying different when the target
2218 * dir already exists. So, we give the recursive call the flag 0
2219 * meaning no toplevel.
2222 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2229 dest_file
= mc_build_filename (d
, x_basename (path
), NULL
);
2230 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2236 if (do_delete
&& return_status
== FILE_CONT
)
2238 if (ctx
->erase_at_end
)
2240 lp
= g_new0 (struct link
, 1);
2241 lp
->src_vpath
= tmp_vpath
;
2242 lp
->st_mode
= buf
.st_mode
;
2243 erase_list
= g_slist_append (erase_list
, lp
);
2246 else if (S_ISDIR (buf
.st_mode
))
2247 return_status
= erase_dir_iff_empty (ctx
, tmp_vpath
, tctx
->progress_count
);
2249 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
2251 vfs_path_free (tmp_vpath
);
2253 mc_closedir (reading
);
2259 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2260 utb
.actime
= cbuf
.st_atime
;
2261 utb
.modtime
= cbuf
.st_mtime
;
2262 mc_utime (dst_vpath
, &utb
);
2266 cbuf
.st_mode
= umask (-1);
2267 umask (cbuf
.st_mode
);
2268 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2269 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2273 free_link (parent_dirs
->data
);
2274 g_slist_free_1 (parent_dirs
);
2276 vfs_path_free (src_vpath
);
2277 vfs_path_free (dst_vpath
);
2278 return return_status
;
2283 /* --------------------------------------------------------------------------------------------- */
2284 /* {{{ Move routines */
2287 move_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
)
2289 struct stat sbuf
, dbuf
;
2290 FileProgressStatus return_status
= FILE_CONT
;
2291 gboolean move_over
= FALSE
;
2293 vfs_path_t
*src_vpath
, *dst_vpath
;
2295 src_vpath
= vfs_path_from_str (s
);
2296 dst_vpath
= vfs_path_from_str (d
);
2298 file_progress_show_source (ctx
, src_vpath
);
2299 file_progress_show_target (ctx
, dst_vpath
);
2301 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2303 return_status
= FILE_ABORT
;
2309 mc_stat (src_vpath
, &sbuf
);
2311 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2312 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2314 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2319 ; /* destination doesn't exist */
2320 else if (!ctx
->dive_into_subdirs
)
2327 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), NULL
);
2328 vfs_path_free (tmp
);
2331 d
= vfs_path_as_str (dst_vpath
);
2333 /* Check if the user inputted an existing dir */
2335 if (mc_stat (dst_vpath
, &dbuf
) == 0)
2339 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, TRUE
, TRUE
, NULL
);
2341 if (return_status
!= FILE_CONT
)
2345 else if (ctx
->skip_all
)
2346 return_status
= FILE_SKIPALL
;
2349 if (S_ISDIR (dbuf
.st_mode
))
2350 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), d
);
2352 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), d
);
2353 if (return_status
== FILE_SKIPALL
)
2354 ctx
->skip_all
= TRUE
;
2355 if (return_status
== FILE_RETRY
)
2356 goto retry_dst_stat
;
2363 if (mc_rename (src_vpath
, dst_vpath
) == 0)
2365 return_status
= FILE_CONT
;
2373 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2374 if (return_status
== FILE_SKIPALL
)
2375 ctx
->skip_all
= TRUE
;
2376 if (return_status
== FILE_RETRY
)
2381 /* Failed because of filesystem boundary -> copy dir instead */
2382 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, FALSE
, TRUE
, NULL
);
2384 if (return_status
!= FILE_CONT
)
2387 file_progress_show_source (ctx
, NULL
);
2388 file_progress_show_target (ctx
, NULL
);
2389 file_progress_show (ctx
, 0, 0, "", FALSE
);
2391 return_status
= check_progress_buttons (ctx
);
2392 if (return_status
!= FILE_CONT
)
2396 if (ctx
->erase_at_end
)
2398 /* Reset progress count before delete to avoid counting files twice */
2399 tctx
->progress_count
= tctx
->prev_progress_count
;
2401 while (erase_list
!= NULL
&& return_status
!= FILE_ABORT
)
2403 struct link
*lp
= (struct link
*) erase_list
->data
;
2405 if (S_ISDIR (lp
->st_mode
))
2406 return_status
= erase_dir_iff_empty (ctx
, lp
->src_vpath
, tctx
->progress_count
);
2408 return_status
= erase_file (tctx
, ctx
, lp
->src_vpath
);
2410 erase_list
= g_slist_remove (erase_list
, lp
);
2414 /* Save progress counter before move next directory */
2415 tctx
->prev_progress_count
= tctx
->progress_count
;
2417 erase_dir_iff_empty (ctx
, src_vpath
, tctx
->progress_count
);
2420 erase_list
= free_linklist (erase_list
);
2422 vfs_path_free (src_vpath
);
2423 vfs_path_free (dst_vpath
);
2424 return return_status
;
2429 /* --------------------------------------------------------------------------------------------- */
2430 /* {{{ Erase routines */
2433 erase_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* s_vpath
)
2435 FileProgressStatus error
;
2437 file_progress_show_deleting (ctx
, vfs_path_as_str (s_vpath
), NULL
);
2438 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2439 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2444 /* The old way to detect a non empty directory was:
2445 error = my_rmdir (s);
2446 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2447 For the linux user space nfs server (nfs-server-2.2beta29-2)
2448 we would have to check also for EIO. I hope the new way is
2449 fool proof. (Norbert)
2451 error
= check_dir_is_empty (s_vpath
);
2454 error
= query_recursive (ctx
, vfs_path_as_str (s_vpath
));
2455 if (error
== FILE_CONT
)
2456 error
= recursive_erase (tctx
, ctx
, s_vpath
);
2460 while (my_rmdir (vfs_path_as_str (s_vpath
)) == -1 && !ctx
->skip_all
)
2462 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), vfs_path_as_str (s_vpath
));
2463 if (error
!= FILE_RETRY
)
2472 /* --------------------------------------------------------------------------------------------- */
2473 /* {{{ Panel operate routines */
2476 dirsize_status_init_cb (status_msg_t
* sm
)
2478 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
2479 Widget
*wd
= WIDGET (sm
->dlg
);
2481 const char *b1_name
= N_("&Abort");
2482 const char *b2_name
= N_("&Skip");
2483 int b_width
, ui_width
;
2486 b1_name
= _(b1_name
);
2487 b2_name
= _(b2_name
);
2490 b_width
= str_term_width1 (b1_name
) + 4;
2491 if (dsm
->allow_skip
)
2492 b_width
+= str_term_width1 (b2_name
) + 4 + 1;
2494 ui_width
= max (COLS
/ 2, b_width
+ 6);
2495 dsm
->dirname
= label_new (2, 3, "");
2496 add_widget (sm
->dlg
, dsm
->dirname
);
2497 dsm
->count_size
= label_new (3, 3, "");
2498 add_widget (sm
->dlg
, dsm
->count_size
);
2499 add_widget (sm
->dlg
, hline_new (4, -1, -1));
2501 dsm
->abort_button
= WIDGET (button_new (5, 3, FILE_ABORT
, NORMAL_BUTTON
, b1_name
, NULL
));
2502 add_widget (sm
->dlg
, dsm
->abort_button
);
2503 if (dsm
->allow_skip
)
2505 dsm
->skip_button
= WIDGET (button_new (5, 3, FILE_SKIP
, NORMAL_BUTTON
, b2_name
, NULL
));
2506 add_widget (sm
->dlg
, dsm
->skip_button
);
2507 dlg_select_widget (dsm
->skip_button
);
2510 widget_set_size (wd
, wd
->y
, wd
->x
, 8, ui_width
);
2511 dirsize_status_locate_buttons (dsm
);
2514 /* --------------------------------------------------------------------------------------------- */
2517 dirsize_status_update_cb (status_msg_t
* sm
)
2519 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
2520 Widget
*wd
= WIDGET (sm
->dlg
);
2522 /* update second (longer label) */
2523 label_set_textv (dsm
->count_size
, _("Directories: %zd, total size: %s"),
2524 dsm
->dir_count
, size_trunc_sep (dsm
->total_size
, panels_options
.kilobyte_si
));
2526 /* enlarge dialog if required */
2527 if (WIDGET (dsm
->count_size
)->cols
+ 6 > wd
->cols
)
2529 dlg_set_size (sm
->dlg
, wd
->lines
, WIDGET (dsm
->count_size
)->cols
+ 6);
2530 dirsize_status_locate_buttons (dsm
);
2531 dlg_redraw (sm
->dlg
);
2534 /* adjust first label */
2535 label_set_text (dsm
->dirname
, str_trunc (vfs_path_as_str (dsm
->dirname_vpath
), wd
->cols
- 6));
2537 switch (status_msg_common_update (sm
))
2549 /* --------------------------------------------------------------------------------------------- */
2552 dirsize_status_deinit_cb (status_msg_t
* sm
)
2556 /* schedule to update passive panel */
2557 if (get_other_type () == view_listing
)
2558 other_panel
->dirty
= 1;
2561 /* --------------------------------------------------------------------------------------------- */
2565 * Computes the number of bytes used by the files in a directory
2569 compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* sm
,
2570 size_t * ret_dir_count
, size_t * ret_marked_count
, uintmax_t * ret_total
,
2571 gboolean compute_symlinks
)
2573 return do_compute_dir_size (dirname_vpath
, sm
, ret_dir_count
, ret_marked_count
, ret_total
,
2577 /* --------------------------------------------------------------------------------------------- */
2581 * Performs one of the operations on the selection on the source_panel
2582 * (copy, delete, move).
2584 * Returns TRUE if did change the directory
2585 * structure, Returns FALSE if user aborted
2587 * force_single forces operation on the current entry and affects
2588 * default destination. Current filename is used as default.
2592 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2594 WPanel
*panel
= PANEL (source_panel
);
2595 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2596 || (get_current_type () == view_tree
);
2598 char *source
= NULL
;
2599 #ifdef WITH_FULL_PATHS
2600 vfs_path_t
*source_with_vpath
= NULL
;
2602 #define source_with_path source
2603 #endif /* !WITH_FULL_PATHS */
2605 vfs_path_t
*dest_vpath
= NULL
;
2607 char *save_cwd
= NULL
, *save_dest
= NULL
;
2608 struct stat src_stat
;
2609 gboolean ret_val
= TRUE
;
2611 FileProgressStatus value
;
2612 file_op_context_t
*ctx
;
2613 file_op_total_context_t
*tctx
;
2614 vfs_path_t
*tmp_vpath
;
2615 filegui_dialog_type_t dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2617 gboolean do_bg
= FALSE
; /* do background operation? */
2619 static gboolean i18n_flag
= FALSE
;
2622 for (i
= G_N_ELEMENTS (op_names
); i
-- != 0;)
2623 op_names
[i
] = Q_ (op_names
[i
]);
2627 linklist
= free_linklist (linklist
);
2628 dest_dirs
= free_linklist (dest_dirs
);
2632 vfs_path_t
*source_vpath
;
2635 source
= g_strdup (selection (panel
)->fname
);
2637 source
= panel_get_file (panel
);
2639 if (DIR_IS_DOTDOT (source
))
2642 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2646 source_vpath
= vfs_path_from_str (source
);
2647 /* Update stat to get actual info */
2648 if (mc_lstat (source_vpath
, &src_stat
) != 0)
2650 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2651 path_trunc (source
, 30), unix_error_string (errno
));
2653 /* Directory was changed outside MC. Reload it forced */
2654 if (!panel
->is_panelized
)
2656 panel_update_flags_t flags
= UP_RELOAD
;
2658 /* don't update panelized panel */
2659 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2660 flags
|= UP_ONLY_CURRENT
;
2662 update_panels (flags
, UP_KEEPSEL
);
2664 vfs_path_free (source_vpath
);
2667 vfs_path_free (source_vpath
);
2670 ctx
= file_op_context_new (operation
);
2672 /* Show confirmation dialog */
2673 if (operation
!= OP_DELETE
)
2675 char *tmp_dest_dir
, *dest_dir
;
2678 /* Forced single operations default to the original name */
2680 tmp_dest_dir
= g_strdup (source
);
2681 else if (get_other_type () == view_listing
)
2682 tmp_dest_dir
= g_strdup (vfs_path_as_str (other_panel
->cwd_vpath
));
2684 tmp_dest_dir
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
2686 * Add trailing backslash only when do non-local ops.
2687 * It saves user from occasional file renames (when destination
2690 if (!force_single
&& tmp_dest_dir
[0] != '\0'
2691 && !IS_PATH_SEP (tmp_dest_dir
[strlen (tmp_dest_dir
) - 1]))
2693 /* add trailing separator */
2694 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2695 g_free (tmp_dest_dir
);
2700 dest_dir
= tmp_dest_dir
;
2702 if (dest_dir
== NULL
)
2708 /* Generate confirmation prompt */
2710 panel_operate_generate_prompt (panel
, operation
, source
!= NULL
? &src_stat
: NULL
);
2712 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2713 source
!= NULL
? (void *) source
2714 : (void *) &panel
->marked
, dest_dir
, &do_bg
);
2719 if (dest
== NULL
|| dest
[0] == '\0')
2725 dest_vpath
= vfs_path_from_str (dest
);
2727 else if (confirm_delete
)
2730 char fmd_buf
[BUF_MEDIUM
];
2732 /* Generate confirmation prompt */
2734 panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
? &src_stat
: NULL
);
2737 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2740 const int fmd_xlen
= 64;
2741 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2742 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2750 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2759 tctx
= file_op_total_context_new ();
2760 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2762 #ifdef ENABLE_BACKGROUND
2763 /* Did the user select to do a background operation? */
2768 v
= do_background (ctx
,
2769 g_strconcat (op_names
[operation
], ": ",
2770 vfs_path_as_str (panel
->cwd_vpath
), (char *) NULL
));
2772 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2774 /* If we are the parent */
2777 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2779 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2780 vfs_path_free (dest_vpath
);
2782 /* file_op_context_destroy (ctx); */
2787 #endif /* ENABLE_BACKGROUND */
2789 if (operation
== OP_DELETE
)
2790 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2791 else if (single_entry
&& S_ISDIR (selection (panel
)->st
.st_mode
))
2792 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2793 else if (single_entry
|| force_single
)
2794 dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2796 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2799 /* Initialize things */
2800 /* We do not want to trash cache every time file is
2801 created/touched. However, this will make our cache contain
2804 && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2805 save_dest
= g_strdup (dest
);
2807 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2808 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2809 save_cwd
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
2811 /* Now, let's do the job */
2813 /* This code is only called by the tree and panel code */
2816 /* We now have ETA in all cases */
2818 /* One file: FIXME mc_chdir will take user out of any vfs */
2819 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2824 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2825 chdir_retcode
= mc_chdir (vpath
);
2826 vfs_path_free (vpath
);
2827 if (chdir_retcode
< 0)
2834 /* The source and src_stat variables have been initialized before */
2835 #ifdef WITH_FULL_PATHS
2836 if (g_path_is_absolute (source
))
2837 source_with_vpath
= vfs_path_from_str (source
);
2839 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2840 #endif /* WITH_FULL_PATHS */
2841 if (panel_operate_init_totals (panel
, vfs_path_as_str (source_with_vpath
), ctx
, dialog_type
)
2844 if (operation
== OP_DELETE
)
2846 if (S_ISDIR (src_stat
.st_mode
))
2847 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2849 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2853 temp
= transform_source (ctx
, source_with_vpath
);
2855 value
= transform_error
;
2858 char *repl_dest
, *temp2
;
2860 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2861 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2863 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2868 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2872 vfs_path_free (dest_vpath
);
2874 dest_vpath
= vfs_path_from_str (dest
);
2879 /* we use file_mask_op_follow_links only with OP_COPY */
2880 ctx
->stat_func (source_with_vpath
, &src_stat
);
2882 if (S_ISDIR (src_stat
.st_mode
))
2884 copy_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2885 dest
, TRUE
, FALSE
, FALSE
, NULL
);
2888 copy_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2893 if (S_ISDIR (src_stat
.st_mode
))
2895 move_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
), dest
);
2898 move_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2903 /* Unknown file operation */
2907 } /* Copy or move operation */
2909 if ((value
== FILE_CONT
) && !force_single
)
2910 unmark_files (panel
);
2917 /* Check destination for copy or move operation */
2918 while (operation
!= OP_DELETE
)
2921 struct stat dst_stat
;
2923 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2925 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2929 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2933 if (panel_operate_init_totals (panel
, NULL
, ctx
, dialog_type
) == FILE_CONT
)
2935 /* Loop for every file, perform the actual copy operation */
2936 for (i
= 0; i
< panel
->dir
.len
; i
++)
2938 const char *source2
;
2940 if (!panel
->dir
.list
[i
].f
.marked
)
2941 continue; /* Skip the unmarked ones */
2943 source2
= panel
->dir
.list
[i
].fname
;
2944 src_stat
= panel
->dir
.list
[i
].st
;
2946 #ifdef WITH_FULL_PATHS
2947 vfs_path_free (source_with_vpath
);
2948 if (g_path_is_absolute (source2
))
2949 source_with_vpath
= vfs_path_from_str (source2
);
2952 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2953 #endif /* WITH_FULL_PATHS */
2955 if (operation
== OP_DELETE
)
2957 if (S_ISDIR (src_stat
.st_mode
))
2958 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2960 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2964 temp
= transform_source (ctx
, source_with_vpath
);
2966 value
= transform_error
;
2969 char *temp2
, *repl_dest
, *source_with_path_str
;
2971 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2972 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2974 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2979 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2982 source_with_path_str
=
2983 strutils_shell_unescape (vfs_path_as_str (source_with_vpath
));
2984 temp
= strutils_shell_unescape (temp2
);
2990 /* we use file_mask_op_follow_links only with OP_COPY */
2994 vpath
= vfs_path_from_str (source_with_path_str
);
2995 ctx
->stat_func (vpath
, &src_stat
);
2996 vfs_path_free (vpath
);
2998 if (S_ISDIR (src_stat
.st_mode
))
2999 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp
,
3000 TRUE
, FALSE
, FALSE
, NULL
);
3002 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp
);
3003 dest_dirs
= free_linklist (dest_dirs
);
3007 if (S_ISDIR (src_stat
.st_mode
))
3008 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp
);
3010 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp
);
3014 /* Unknown file operation */
3018 g_free (source_with_path_str
);
3021 } /* Copy or move operation */
3023 if (value
== FILE_ABORT
)
3026 if (value
== FILE_CONT
)
3027 do_file_mark (panel
, i
, 0);
3029 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3031 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3032 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3035 if (operation
!= OP_DELETE
)
3036 file_progress_show (ctx
, 0, 0, "", FALSE
);
3038 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3042 } /* Loop for every file */
3044 } /* Many entries */
3048 if (save_cwd
!= NULL
)
3050 tmp_vpath
= vfs_path_from_str (save_cwd
);
3051 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3052 vfs_path_free (tmp_vpath
);
3056 if (save_dest
!= NULL
)
3058 tmp_vpath
= vfs_path_from_str (save_dest
);
3059 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3060 vfs_path_free (tmp_vpath
);
3064 linklist
= free_linklist (linklist
);
3065 dest_dirs
= free_linklist (dest_dirs
);
3066 #ifdef WITH_FULL_PATHS
3067 vfs_path_free (source_with_vpath
);
3068 #endif /* WITH_FULL_PATHS */
3070 vfs_path_free (dest_vpath
);
3071 MC_PTR_FREE (ctx
->dest_mask
);
3073 #ifdef ENABLE_BACKGROUND
3074 /* Let our parent know we are saying bye bye */
3075 if (mc_global
.we_are_background
)
3077 int cur_pid
= getpid ();
3078 /* Send pid to parent with child context, it is fork and
3079 don't modify real parent ctx */
3081 parent_call ((void *) end_bg_process
, ctx
, 0);
3084 my_exit (EXIT_SUCCESS
);
3086 #endif /* ENABLE_BACKGROUND */
3088 file_op_total_context_destroy (tctx
);
3090 file_op_context_destroy (ctx
);
3098 /* --------------------------------------------------------------------------------------------- */
3099 /* {{{ Query/status report routines */
3100 /** Report error with one file */
3102 file_error (const char *format
, const char *file
)
3104 char buf
[BUF_MEDIUM
];
3106 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3108 return do_file_error (buf
);
3111 /* --------------------------------------------------------------------------------------------- */
3114 Cause emacs to enter folding mode for this file: