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() */
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 /* Hack: the vfs code should not rely on this */
103 #define WITH_FULL_PATHS 1
105 #define FILEOP_UPDATE_INTERVAL 2
106 #define FILEOP_STALLING_INTERVAL 4
108 /*** file scope type declarations ****************************************************************/
110 /* This is a hard link cache */
113 const struct vfs_class
*vfs
;
118 vfs_path_t
*src_vpath
;
119 vfs_path_t
*dst_vpath
;
122 /* Status of the destination file */
125 DEST_NONE
= 0, /* Not created */
126 DEST_SHORT
= 1, /* Created, not fully copied */
127 DEST_FULL
= 2 /* Created, fully copied */
131 * This array introduced to avoid translation problems. The former (op_names)
132 * is assumed to be nouns, suitable in dialog box titles; this one should
133 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
134 * (I don't use spaces around the words, because someday they could be
135 * dropped, when widgets get smarter)
138 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
139 static const char *op_names1
[] = {
140 N_("FileOperation|Copy"),
141 N_("FileOperation|Move"),
142 N_("FileOperation|Delete")
146 * These are formats for building a prompt. Parts encoded as follows:
147 * %o - operation from op_names1
148 * %f - file/files or files/directories, as appropriate
149 * %m - "with source mask" or question mark for delete
150 * %s - source name (truncated)
151 * %d - number of marked files
152 * %n - the '\n' symbol to form two-line prompt for delete or space for other operations
154 /* xgettext:no-c-format */
155 static const char *one_format
= N_("%o %f%n\"%s\"%m");
156 /* xgettext:no-c-format */
157 static const char *many_format
= N_("%o %d %f%m");
159 static const char *prompt_parts
[] = {
164 N_("files/directories"),
165 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
166 N_(" with source mask:")
169 /*** file scope variables ************************************************************************/
171 /* the hard link cache */
172 static GSList
*linklist
= NULL
;
174 /* the files-to-be-erased list */
175 static GSList
*erase_list
= NULL
;
178 * In copy_dir_dir we use two additional single linked lists: The first -
179 * variable name 'parent_dirs' - holds information about already copied
180 * directories and is used to detect cyclic symbolic links.
181 * The second ('dest_dirs' below) holds information about just created
182 * target directories and is used to detect when an directory is copied
183 * into itself (we don't want to copy infinitly).
184 * Both lists don't use the linkcount and name structure members of struct
187 static GSList
*dest_dirs
= NULL
;
189 static FileProgressStatus transform_error
= FILE_CONT
;
191 /* --------------------------------------------------------------------------------------------- */
192 /*** file scope functions ************************************************************************/
193 /* --------------------------------------------------------------------------------------------- */
196 dirsize_status_locate_buttons (dirsize_status_msg_t
* dsm
)
198 status_msg_t
*sm
= STATUS_MSG (dsm
);
199 Widget
*wd
= WIDGET (sm
->dlg
);
205 if (!dsm
->allow_skip
)
207 /* single button: "Abort" */
208 x
+= (wd
->cols
- dsm
->abort_button
->cols
) / 2;
209 widget_set_size (dsm
->abort_button
, y
, x
,
210 dsm
->abort_button
->lines
, dsm
->abort_button
->cols
);
214 /* two buttons: "Abort" and "Skip" */
217 cols
= dsm
->abort_button
->cols
+ dsm
->skip_button
->cols
+ 1;
218 x
+= (wd
->cols
- cols
) / 2;
219 widget_set_size (dsm
->abort_button
, y
, x
, dsm
->abort_button
->lines
,
220 dsm
->abort_button
->cols
);
221 x
+= dsm
->abort_button
->cols
+ 1;
222 widget_set_size (dsm
->skip_button
, y
, x
, dsm
->skip_button
->lines
, dsm
->skip_button
->cols
);
226 /* --------------------------------------------------------------------------------------------- */
229 transform_source (file_op_context_t
* ctx
, const vfs_path_t
* source_vpath
)
232 const char *fnsource
;
234 s
= g_strdup (vfs_path_as_str (source_vpath
));
236 /* We remove \n from the filename since regex routines would use \n as an anchor */
237 /* this is just to be allowed to maniupulate file names with \n on it */
238 for (q
= s
; *q
!= '\0'; q
++)
242 fnsource
= x_basename (s
);
244 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
246 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
247 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
249 if (ctx
->search_handle
->error_str
!= NULL
)
250 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
253 transform_error
= FILE_ABORT
;
259 transform_error
= FILE_SKIP
;
266 /* --------------------------------------------------------------------------------------------- */
269 free_link (void *data
)
271 struct link
*lp
= (struct link
*) data
;
273 vfs_path_free (lp
->src_vpath
);
274 vfs_path_free (lp
->dst_vpath
);
278 /* --------------------------------------------------------------------------------------------- */
281 free_linklist (GSList
* lp
)
283 g_slist_free_full (lp
, free_link
);
288 /* --------------------------------------------------------------------------------------------- */
291 is_in_linklist (const GSList
* lp
, const vfs_path_t
* vpath
, const struct stat
*sb
)
293 const struct vfs_class
*class;
294 ino_t ino
= sb
->st_ino
;
295 dev_t dev
= sb
->st_dev
;
297 class = vfs_path_get_last_path_vfs (vpath
);
299 for (; lp
!= NULL
; lp
= (const GSList
*) g_slist_next (lp
))
301 const struct link
*lnk
= (const struct link
*) lp
->data
;
303 if (lnk
->vfs
== class && lnk
->ino
== ino
&& lnk
->dev
== dev
)
309 /* --------------------------------------------------------------------------------------------- */
311 * Check and made hardlink
313 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
314 * and a hardlink was successfully made
318 check_hardlinks (const vfs_path_t
* src_vpath
, const vfs_path_t
* dst_vpath
, struct stat
*pstat
)
323 const struct vfs_class
*my_vfs
;
324 ino_t ino
= pstat
->st_ino
;
325 dev_t dev
= pstat
->st_dev
;
326 struct stat link_stat
;
328 if ((vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
331 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
333 for (lp
= linklist
; lp
!= NULL
; lp
= g_slist_next (lp
))
335 lnk
= (struct link
*) lp
->data
;
337 if (lnk
->vfs
== my_vfs
&& lnk
->ino
== ino
&& lnk
->dev
== dev
)
339 const struct vfs_class
*lp_name_class
;
342 lp_name_class
= vfs_path_get_last_path_vfs (lnk
->src_vpath
);
343 stat_result
= mc_stat (lnk
->src_vpath
, &link_stat
);
345 if (stat_result
== 0 && link_stat
.st_ino
== ino
346 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
348 const struct vfs_class
*p_class
, *dst_name_class
;
350 dst_name_class
= vfs_path_get_last_path_vfs (dst_vpath
);
351 p_class
= vfs_path_get_last_path_vfs (lnk
->dst_vpath
);
353 if (dst_name_class
== p_class
&&
354 mc_stat (lnk
->dst_vpath
, &link_stat
) == 0 &&
355 mc_link (lnk
->dst_vpath
, dst_vpath
) == 0)
359 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
364 lnk
= g_try_new0 (struct link
, 1);
370 lnk
->src_vpath
= vfs_path_clone (src_vpath
);
371 lnk
->dst_vpath
= vfs_path_clone (dst_vpath
);
372 linklist
= g_slist_prepend (linklist
, lnk
);
378 /* --------------------------------------------------------------------------------------------- */
380 * Duplicate the contents of the symbolic link src_path in dst_path.
381 * Try to make a stable symlink if the option "stable symlink" was
382 * set in the file mask dialog.
383 * If dst_path is an existing symlink it will be deleted silently
384 * (upper levels take already care of existing files at dst_path).
387 static FileProgressStatus
388 make_symlink (file_op_context_t
* ctx
, const char *src_path
, const char *dst_path
)
390 char link_target
[MC_MAXPATHLEN
];
392 FileProgressStatus return_status
;
394 vfs_path_t
*src_vpath
;
395 vfs_path_t
*dst_vpath
;
396 gboolean dst_is_symlink
;
397 vfs_path_t
*link_target_vpath
= NULL
;
399 src_vpath
= vfs_path_from_str (src_path
);
400 dst_vpath
= vfs_path_from_str (dst_path
);
401 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
404 len
= mc_readlink (src_vpath
, link_target
, sizeof (link_target
) - 1);
408 return_status
= FILE_SKIPALL
;
411 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
412 if (return_status
== FILE_SKIPALL
)
413 ctx
->skip_all
= TRUE
;
414 if (return_status
== FILE_RETRY
)
415 goto retry_src_readlink
;
420 link_target
[len
] = '\0';
422 if (ctx
->stable_symlinks
&& !(vfs_file_is_local (src_vpath
) && vfs_file_is_local (dst_vpath
)))
424 message (D_ERROR
, MSG_ERROR
,
425 _("Cannot make stable symlinks across"
426 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
427 ctx
->stable_symlinks
= FALSE
;
430 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
434 r
= strrchr (src_path
, PATH_SEP
);
440 p
= g_strndup (src_path
, r
- src_path
+ 1);
441 if (g_path_is_absolute (dst_path
))
442 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
444 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
446 if (vfs_path_tokens_count (q
) > 1)
449 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
451 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
452 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
));
469 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
472 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
475 return_status
= FILE_CONT
;
479 * if dst_exists, it is obvious that this had failed.
480 * We can delete the old symlink and try again...
482 if (dst_is_symlink
&& mc_unlink (dst_vpath
) == 0
483 && mc_symlink (link_target_vpath
, dst_vpath
) == 0)
486 return_status
= FILE_CONT
;
491 return_status
= FILE_SKIPALL
;
494 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
495 if (return_status
== FILE_SKIPALL
)
496 ctx
->skip_all
= TRUE
;
497 if (return_status
== FILE_RETRY
)
498 goto retry_dst_symlink
;
502 vfs_path_free (src_vpath
);
503 vfs_path_free (dst_vpath
);
504 vfs_path_free (link_target_vpath
);
505 return return_status
;
508 /* --------------------------------------------------------------------------------------------- */
510 * do_compute_dir_size:
512 * Computes the number of bytes used by the files in a directory
515 static FileProgressStatus
516 do_compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* dsm
,
517 size_t * dir_count
, size_t * ret_marked
, uintmax_t * ret_total
,
518 gboolean compute_symlinks
)
520 static guint64 timestamp
= 0;
521 /* update with 25 FPS rate */
522 static const guint64 delay
= G_USEC_PER_SEC
/ 25;
524 status_msg_t
*sm
= STATUS_MSG (dsm
);
528 struct dirent
*dirent
;
529 FileProgressStatus ret
= FILE_CONT
;
531 if (!compute_symlinks
)
533 res
= mc_lstat (dirname_vpath
, &s
);
537 /* don't scan symlink to directory */
538 if (S_ISLNK (s
.st_mode
))
541 *ret_total
+= (uintmax_t) s
.st_size
;
548 dir
= mc_opendir (dirname_vpath
);
552 while (ret
== FILE_CONT
&& (dirent
= mc_readdir (dir
)) != NULL
)
554 vfs_path_t
*tmp_vpath
;
556 if (DIR_IS_DOT (dirent
->d_name
) || DIR_IS_DOTDOT (dirent
->d_name
))
559 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, (char *) NULL
);
561 res
= mc_lstat (tmp_vpath
, &s
);
564 if (S_ISDIR (s
.st_mode
))
566 do_compute_dir_size (tmp_vpath
, dsm
, dir_count
, ret_marked
, ret_total
,
573 *ret_total
+= (uintmax_t) s
.st_size
;
576 if (ret
== FILE_CONT
&& sm
->update
!= NULL
&& mc_time_elapsed (×tamp
, delay
))
578 dsm
->dirname_vpath
= tmp_vpath
;
579 dsm
->dir_count
= *dir_count
;
580 dsm
->total_size
= *ret_total
;
581 ret
= sm
->update (sm
);
585 vfs_path_free (tmp_vpath
);
592 /* --------------------------------------------------------------------------------------------- */
594 static FileProgressStatus
595 progress_update_one (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, off_t add
)
597 struct timeval tv_current
;
598 static struct timeval tv_start
= { 0, 0 };
600 tctx
->progress_count
++;
601 tctx
->progress_bytes
+= (uintmax_t) add
;
603 if (tv_start
.tv_sec
== 0)
605 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
607 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
608 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
610 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
612 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
613 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
615 tv_start
.tv_sec
= tv_current
.tv_sec
;
618 return check_progress_buttons (ctx
);
621 /* --------------------------------------------------------------------------------------------- */
623 static FileProgressStatus
624 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
628 const char *head_msg
;
630 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
632 msg
= g_strdup_printf (fmt
, a
, b
);
633 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
637 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
640 /* --------------------------------------------------------------------------------------------- */
642 static FileProgressStatus
643 warn_same_file (const char *fmt
, const char *a
, const char *b
)
645 #ifdef ENABLE_BACKGROUND
650 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
, const char *a
, const char *b
);
654 pntr
.f
= real_warn_same_file
;
656 if (mc_global
.we_are_background
)
657 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
659 return real_warn_same_file (Foreground
, fmt
, a
, b
);
662 /* --------------------------------------------------------------------------------------------- */
665 get_times (const struct stat
*sb
, mc_timesbuf_t
* times
)
667 #ifdef HAVE_UTIMENSAT
668 (*times
)[0] = sb
->st_atim
;
669 (*times
)[1] = sb
->st_mtim
;
671 times
->actime
= sb
->st_atime
;
672 times
->modtime
= sb
->st_mtime
;
676 /* --------------------------------------------------------------------------------------------- */
677 /* {{{ Query/status report routines */
679 static FileProgressStatus
680 real_do_file_error (enum OperationMode mode
, const char *error
)
685 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
687 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
709 /* --------------------------------------------------------------------------------------------- */
711 static FileProgressStatus
712 real_query_recursive (file_op_context_t
* ctx
, enum OperationMode mode
, const char *s
)
714 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
719 msg
= mode
== Foreground
720 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
721 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
722 text
= g_strdup_printf (msg
, path_trunc (s
, 30));
727 ctx
->recursive_result
=
728 query_dialog (op_names
[OP_DELETE
], text
, D_ERROR
, 5, _("&Yes"), _("&No"), _("A&ll"),
729 _("Non&e"), _("&Abort"));
732 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
736 switch (ctx
->recursive_result
)
739 case RECURSIVE_ALWAYS
:
743 case RECURSIVE_NEVER
:
746 case RECURSIVE_ABORT
:
752 /* --------------------------------------------------------------------------------------------- */
754 #ifdef ENABLE_BACKGROUND
755 static FileProgressStatus
756 do_file_error (const char *str
)
762 FileProgressStatus (*f
) (enum OperationMode
, const char *);
766 pntr
.f
= real_do_file_error
;
768 if (mc_global
.we_are_background
)
769 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
771 return real_do_file_error (Foreground
, str
);
774 /* --------------------------------------------------------------------------------------------- */
776 static FileProgressStatus
777 query_recursive (file_op_context_t
* ctx
, const char *s
)
783 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *);
787 pntr
.f
= real_query_recursive
;
789 if (mc_global
.we_are_background
)
790 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
792 return real_query_recursive (ctx
, Foreground
, s
);
795 /* --------------------------------------------------------------------------------------------- */
797 static FileProgressStatus
798 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
799 struct stat
*_d_stat
)
805 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *,
806 struct stat
*, struct stat
*);
810 pntr
.f
= file_progress_real_query_replace
;
812 if (mc_global
.we_are_background
)
813 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
814 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
816 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
820 /* --------------------------------------------------------------------------------------------- */
822 static FileProgressStatus
823 do_file_error (const char *str
)
825 return real_do_file_error (Foreground
, str
);
828 /* --------------------------------------------------------------------------------------------- */
830 static FileProgressStatus
831 query_recursive (file_op_context_t
* ctx
, const char *s
)
833 return real_query_recursive (ctx
, Foreground
, s
);
836 /* --------------------------------------------------------------------------------------------- */
838 static FileProgressStatus
839 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
840 struct stat
*_d_stat
)
842 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
845 #endif /* !ENABLE_BACKGROUND */
847 /* --------------------------------------------------------------------------------------------- */
848 /** Report error with two files */
850 static FileProgressStatus
851 files_error (const char *format
, const char *file1
, const char *file2
)
853 char buf
[BUF_MEDIUM
];
854 char *nfile1
= g_strdup (path_trunc (file1
, 15));
855 char *nfile2
= g_strdup (path_trunc (file2
, 15));
857 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
862 return do_file_error (buf
);
867 /* --------------------------------------------------------------------------------------------- */
870 copy_file_file_display_progress (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
871 struct timeval tv_current
, struct timeval tv_transfer_start
,
872 off_t file_size
, off_t n_read_total
)
876 /* 1. Update rotating dash after some time */
880 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
882 if (n_read_total
== 0)
886 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
887 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
890 /* 4. Compute BPS rate */
891 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
892 if (ctx
->bps_time
< 1)
894 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
896 /* 5. Compute total ETA and BPS */
897 if (ctx
->progress_bytes
!= 0)
899 uintmax_t remain_bytes
;
901 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
904 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
909 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
910 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
913 /* broken on lot of little files */
915 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
916 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
921 /* --------------------------------------------------------------------------------------------- */
923 /* {{{ Move routines */
924 static FileProgressStatus
925 move_file_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
,
928 struct stat src_stats
, dst_stats
;
929 FileProgressStatus return_status
= FILE_CONT
;
930 gboolean copy_done
= FALSE
;
931 gboolean old_ask_overwrite
;
932 vfs_path_t
*src_vpath
, *dst_vpath
;
934 src_vpath
= vfs_path_from_str (s
);
935 dst_vpath
= vfs_path_from_str (d
);
937 file_progress_show_source (ctx
, src_vpath
);
938 file_progress_show_target (ctx
, dst_vpath
);
940 if (check_progress_buttons (ctx
) == FILE_ABORT
)
942 return_status
= FILE_ABORT
;
948 while (mc_lstat (src_vpath
, &src_stats
) != 0)
950 /* Source doesn't exist */
952 return_status
= FILE_SKIPALL
;
955 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
956 if (return_status
== FILE_SKIPALL
)
957 ctx
->skip_all
= TRUE
;
960 if (return_status
!= FILE_RETRY
)
964 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
966 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
968 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
972 if (S_ISDIR (dst_stats
.st_mode
))
974 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
976 return_status
= FILE_SKIP
;
980 if (confirm_overwrite
)
982 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
983 if (return_status
!= FILE_CONT
)
986 /* Ok to overwrite */
991 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
993 return_status
= make_symlink (ctx
, s
, d
);
994 if (return_status
== FILE_CONT
)
995 goto retry_src_remove
;
999 if (mc_rename (src_vpath
, dst_vpath
) == 0)
1001 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
1006 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1007 one nfs to the same, but on the server it is on two different
1008 filesystems. Then nfs returns EIO instead of EXDEV.
1009 Hope it will not hurt if we always in case of error try to copy/delete. */
1011 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1016 return_status
= FILE_SKIPALL
;
1019 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
1020 if (return_status
== FILE_SKIPALL
)
1021 ctx
->skip_all
= TRUE
;
1022 if (return_status
== FILE_RETRY
)
1030 /* Failed because filesystem boundary -> copy the file instead */
1031 old_ask_overwrite
= tctx
->ask_overwrite
;
1032 tctx
->ask_overwrite
= FALSE
;
1033 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
1034 tctx
->ask_overwrite
= old_ask_overwrite
;
1035 if (return_status
!= FILE_CONT
)
1040 file_progress_show_source (ctx
, NULL
);
1041 file_progress_show (ctx
, 0, 0, "", FALSE
);
1043 return_status
= check_progress_buttons (ctx
);
1044 if (return_status
!= FILE_CONT
)
1049 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
1051 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
1052 if (return_status
== FILE_RETRY
)
1053 goto retry_src_remove
;
1054 if (return_status
== FILE_SKIPALL
)
1055 ctx
->skip_all
= TRUE
;
1060 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
1063 vfs_path_free (src_vpath
);
1064 vfs_path_free (dst_vpath
);
1066 return return_status
;
1071 /* --------------------------------------------------------------------------------------------- */
1072 /* {{{ Erase routines */
1073 /** Don't update progress status if progress_count==NULL */
1075 static FileProgressStatus
1076 erase_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1080 file_progress_show_deleting (ctx
, vfs_path_as_str (vpath
), &tctx
->progress_count
);
1081 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1082 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1087 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
1089 /* ignore, most likely the mc_unlink fails, too */
1093 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
1097 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), vfs_path_as_str (vpath
));
1098 if (return_status
== FILE_ABORT
)
1099 return return_status
;
1100 if (return_status
== FILE_RETRY
)
1102 if (return_status
== FILE_SKIPALL
)
1103 ctx
->skip_all
= TRUE
;
1107 if (tctx
->progress_count
== 0)
1110 return check_progress_buttons (ctx
);
1113 /* --------------------------------------------------------------------------------------------- */
1116 Recursive remove of files
1118 skip ->warn every level, gets default
1119 skipall->remove as much as possible
1121 static FileProgressStatus
1122 recursive_erase (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1124 struct dirent
*next
;
1127 FileProgressStatus return_status
= FILE_CONT
;
1129 reading
= mc_opendir (vpath
);
1130 if (reading
== NULL
)
1133 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1135 vfs_path_t
*tmp_vpath
;
1138 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
1141 tmp_vpath
= vfs_path_append_new (vpath
, next
->d_name
, (char *) NULL
);
1142 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1144 mc_closedir (reading
);
1145 vfs_path_free (tmp_vpath
);
1148 if (S_ISDIR (buf
.st_mode
))
1149 return_status
= recursive_erase (tctx
, ctx
, tmp_vpath
);
1151 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
1152 vfs_path_free (tmp_vpath
);
1154 mc_closedir (reading
);
1156 if (return_status
== FILE_ABORT
)
1159 s
= vfs_path_as_str (vpath
);
1161 file_progress_show_deleting (ctx
, s
, NULL
);
1162 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1163 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1168 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1170 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1171 if (return_status
== FILE_RETRY
)
1173 if (return_status
== FILE_ABORT
)
1175 if (return_status
== FILE_SKIPALL
)
1176 ctx
->skip_all
= TRUE
;
1180 return return_status
;
1183 /* --------------------------------------------------------------------------------------------- */
1184 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1185 in the directory path points to, 0 else. */
1188 check_dir_is_empty (const vfs_path_t
* vpath
)
1194 dir
= mc_opendir (vpath
);
1198 for (d
= mc_readdir (dir
); d
!= NULL
; d
= mc_readdir (dir
))
1199 if (!DIR_IS_DOT (d
->d_name
) && !DIR_IS_DOTDOT (d
->d_name
))
1209 /* --------------------------------------------------------------------------------------------- */
1211 static FileProgressStatus
1212 erase_dir_iff_empty (file_op_context_t
* ctx
, const vfs_path_t
* vpath
, size_t count
)
1214 FileProgressStatus error
= FILE_CONT
;
1217 s
= vfs_path_as_str (vpath
);
1219 file_progress_show_deleting (ctx
, s
, NULL
);
1220 file_progress_show_count (ctx
, count
, ctx
->progress_count
);
1221 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1226 if (check_dir_is_empty (vpath
) == 1) /* not empty or error */
1228 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1230 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1231 if (error
== FILE_SKIPALL
)
1232 ctx
->skip_all
= TRUE
;
1233 if (error
!= FILE_RETRY
)
1243 /* --------------------------------------------------------------------------------------------- */
1244 /* {{{ Panel operate routines */
1247 * Return currently selected entry name or the name of the first marked
1248 * entry if there is one.
1252 panel_get_file (WPanel
* panel
)
1254 if (get_current_type () == view_tree
)
1257 const vfs_path_t
*selected_name
;
1259 tree
= (WTree
*) get_panel_widget (get_current_index ());
1260 selected_name
= tree_selected_name (tree
);
1261 return vfs_path_as_str (selected_name
);
1264 if (panel
->marked
!= 0)
1268 for (i
= 0; i
< panel
->dir
.len
; i
++)
1269 if (panel
->dir
.list
[i
].f
.marked
)
1270 return panel
->dir
.list
[i
].fname
;
1273 return panel
->dir
.list
[panel
->selected
].fname
;
1276 /* --------------------------------------------------------------------------------------------- */
1278 * panel_compute_totals:
1280 * compute the number of files and the number of bytes
1281 * used up by the whole selection, recursing directories
1282 * as required. In addition, it checks to see if it will
1283 * overwrite any files by doing the copy.
1286 static FileProgressStatus
1287 panel_compute_totals (const WPanel
* panel
, dirsize_status_msg_t
* sm
, size_t * ret_count
,
1288 uintmax_t * ret_total
, gboolean compute_symlinks
)
1291 size_t dir_count
= 0;
1293 for (i
= 0; i
< panel
->dir
.len
; i
++)
1297 if (!panel
->dir
.list
[i
].f
.marked
)
1300 s
= &panel
->dir
.list
[i
].st
;
1302 if (S_ISDIR (s
->st_mode
))
1305 FileProgressStatus status
;
1307 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, (char *) NULL
);
1308 status
= compute_dir_size (p
, sm
, &dir_count
, ret_count
, ret_total
, compute_symlinks
);
1311 if (status
!= FILE_CONT
)
1317 *ret_total
+= (uintmax_t) s
->st_size
;
1324 /* --------------------------------------------------------------------------------------------- */
1326 /** Initialize variables for progress bars */
1327 static FileProgressStatus
1328 panel_operate_init_totals (const WPanel
* panel
, const char *source
, file_op_context_t
* ctx
,
1329 filegui_dialog_type_t dialog_type
)
1331 FileProgressStatus status
;
1333 #ifdef ENABLE_BACKGROUND
1334 if (mc_global
.we_are_background
)
1338 if (verbose
&& file_op_compute_totals
)
1340 dirsize_status_msg_t dsm
;
1342 memset (&dsm
, 0, sizeof (dsm
));
1343 dsm
.allow_skip
= TRUE
;
1344 status_msg_init (STATUS_MSG (&dsm
), _("Directory scanning"), 0, dirsize_status_init_cb
,
1345 dirsize_status_update_cb
, dirsize_status_deinit_cb
);
1347 ctx
->progress_count
= 0;
1348 ctx
->progress_bytes
= 0;
1351 status
= panel_compute_totals (panel
, &dsm
, &ctx
->progress_count
, &ctx
->progress_bytes
,
1356 size_t dir_count
= 0;
1358 p
= vfs_path_from_str (source
);
1359 status
= compute_dir_size (p
, &dsm
, &dir_count
, &ctx
->progress_count
,
1360 &ctx
->progress_bytes
, ctx
->follow_links
);
1364 status_msg_deinit (STATUS_MSG (&dsm
));
1366 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1368 if (status
== FILE_SKIP
)
1374 ctx
->progress_count
= panel
->marked
;
1375 ctx
->progress_bytes
= panel
->total
;
1376 ctx
->progress_totals_computed
= FALSE
;
1379 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
1384 /* --------------------------------------------------------------------------------------------- */
1386 * Generate user prompt for panel operation.
1387 * src_stat must be not NULL for single source, and NULL for multiple sources
1391 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1392 const struct stat
*src_stat
)
1395 char *format_string
;
1398 static gboolean i18n_flag
= FALSE
;
1403 for (i
= G_N_ELEMENTS (op_names1
); i
-- != 0;)
1404 op_names1
[i
] = Q_ (op_names1
[i
]);
1407 for (i
= G_N_ELEMENTS (prompt_parts
); i
-- != 0;)
1408 prompt_parts
[i
] = _(prompt_parts
[i
]);
1410 one_format
= _(one_format
);
1411 many_format
= _(many_format
);
1412 #endif /* ENABLE_NLS */
1416 /* Possible prompts:
1418 * "Copy file \"%s\" with source mask:"
1419 * "Copy %d files with source mask:"
1420 * "Copy directory \"%s\" with source mask:"
1421 * "Copy %d directories with source mask:"
1422 * "Copy %d files/directories with source mask:"
1424 * "Move file \"%s\" with source mask:"
1425 * "Move %d files with source mask:"
1426 * "Move directory \"%s\" with source mask:"
1427 * "Move %d directories with source mask:"
1428 * "Move %d files/directories with source mask:"
1430 * "Delete file \"%s\"?"
1431 * "Delete %d files?"
1432 * "Delete directory \"%s\"?"
1433 * "Delete %d directories?"
1434 * "Delete %d files/directories?"
1437 cp
= (src_stat
!= NULL
? one_format
: many_format
);
1439 /* 1. Substitute %o */
1440 format_string
= str_replace_all (cp
, "%o", op_names1
[(int) operation
]);
1442 /* 2. Substitute %n */
1443 cp
= operation
== OP_DELETE
? "\n" : " ";
1445 format_string
= str_replace_all (sp
, "%n", cp
);
1448 /* 3. Substitute %f */
1449 if (src_stat
!= NULL
)
1450 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1451 else if (panel
->marked
== panel
->dirs_marked
)
1452 cp
= prompt_parts
[3];
1454 cp
= panel
->dirs_marked
!= 0 ? prompt_parts
[4] : prompt_parts
[1];
1457 format_string
= str_replace_all (sp
, "%f", cp
);
1460 /* 4. Substitute %m */
1461 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1463 format_string
= str_replace_all (sp
, "%m", cp
);
1466 return format_string
;
1469 /* --------------------------------------------------------------------------------------------- */
1471 #ifdef ENABLE_BACKGROUND
1473 end_bg_process (file_op_context_t
* ctx
, enum OperationMode mode
)
1480 unregister_task_with_pid (pid
);
1481 /* file_op_context_destroy(ctx); */
1487 /* --------------------------------------------------------------------------------------------- */
1488 /*** public functions ****************************************************************************/
1489 /* --------------------------------------------------------------------------------------------- */
1492 copy_file_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
1493 const char *src_path
, const char *dst_path
)
1495 uid_t src_uid
= (uid_t
) (-1);
1496 gid_t src_gid
= (gid_t
) (-1);
1498 int src_desc
, dest_desc
= -1;
1499 mode_t src_mode
= 0; /* The mode of the source file */
1500 struct stat src_stat
, dst_stat
;
1501 mc_timesbuf_t times
;
1502 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1503 off_t file_size
= -1;
1504 FileProgressStatus return_status
, temp_status
;
1505 struct timeval tv_transfer_start
;
1506 dest_status_t dst_status
= DEST_NONE
;
1508 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1511 /* FIXME: We should not be using global variables! */
1513 return_status
= FILE_RETRY
;
1515 dst_vpath
= vfs_path_from_str (dst_path
);
1516 src_vpath
= vfs_path_from_str (src_path
);
1518 file_progress_show_source (ctx
, src_vpath
);
1519 file_progress_show_target (ctx
, dst_vpath
);
1521 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1523 return_status
= FILE_ABORT
;
1529 while (mc_stat (dst_vpath
, &dst_stat
) == 0)
1531 if (S_ISDIR (dst_stat
.st_mode
))
1534 return_status
= FILE_SKIPALL
;
1537 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1538 if (return_status
== FILE_SKIPALL
)
1539 ctx
->skip_all
= TRUE
;
1540 if (return_status
== FILE_RETRY
)
1550 while ((*ctx
->stat_func
) (src_vpath
, &src_stat
) != 0)
1553 return_status
= FILE_SKIPALL
;
1556 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1557 if (return_status
== FILE_SKIPALL
)
1558 ctx
->skip_all
= TRUE
;
1561 if (return_status
!= FILE_RETRY
)
1567 /* Destination already exists */
1568 if (src_stat
.st_dev
== dst_stat
.st_dev
&& src_stat
.st_ino
== dst_stat
.st_ino
)
1570 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1571 src_path
, dst_path
);
1575 /* Should we replace destination? */
1576 if (tctx
->ask_overwrite
)
1579 return_status
= query_replace (ctx
, dst_path
, &src_stat
, &dst_stat
);
1580 if (return_status
!= FILE_CONT
)
1585 if (!ctx
->do_append
)
1587 /* Check the hardlinks */
1588 if (!ctx
->follow_links
&& src_stat
.st_nlink
> 1
1589 && check_hardlinks (src_vpath
, dst_vpath
, &src_stat
))
1591 /* We have made a hardlink - no more processing is necessary */
1592 return_status
= FILE_CONT
;
1596 if (S_ISLNK (src_stat
.st_mode
))
1598 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1602 if (S_ISCHR (src_stat
.st_mode
) || S_ISBLK (src_stat
.st_mode
) || S_ISFIFO (src_stat
.st_mode
)
1603 || S_ISNAM (src_stat
.st_mode
) || S_ISSOCK (src_stat
.st_mode
))
1607 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1608 rdev
= src_stat
.st_rdev
;
1611 while (mc_mknod (dst_vpath
, src_stat
.st_mode
& ctx
->umask_kill
, rdev
) < 0
1614 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1615 if (return_status
== FILE_RETRY
)
1617 if (return_status
== FILE_SKIPALL
)
1618 ctx
->skip_all
= TRUE
;
1623 while (ctx
->preserve_uidgid
1624 && mc_chown (dst_vpath
, src_stat
.st_uid
, src_stat
.st_gid
) != 0 && !ctx
->skip_all
)
1626 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1627 if (temp_status
== FILE_SKIP
)
1629 if (temp_status
== FILE_SKIPALL
)
1630 ctx
->skip_all
= TRUE
;
1631 if (temp_status
!= FILE_RETRY
)
1633 return_status
= temp_status
;
1638 while (ctx
->preserve
&& mc_chmod (dst_vpath
, src_stat
.st_mode
& ctx
->umask_kill
) != 0
1641 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1642 if (temp_status
== FILE_SKIP
)
1644 if (temp_status
== FILE_SKIPALL
)
1645 ctx
->skip_all
= TRUE
;
1646 if (temp_status
!= FILE_RETRY
)
1648 return_status
= temp_status
;
1653 return_status
= FILE_CONT
;
1658 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1660 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1662 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1663 if (return_status
== FILE_RETRY
)
1665 if (return_status
== FILE_SKIPALL
)
1666 ctx
->skip_all
= TRUE
;
1667 if (return_status
== FILE_SKIP
)
1669 ctx
->do_append
= FALSE
;
1673 if (ctx
->do_reget
!= 0)
1675 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1677 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1679 ctx
->do_append
= FALSE
;
1683 while (mc_fstat (src_desc
, &src_stat
) != 0)
1686 return_status
= FILE_SKIPALL
;
1689 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1690 if (return_status
== FILE_RETRY
)
1692 if (return_status
== FILE_SKIPALL
)
1693 ctx
->skip_all
= TRUE
;
1694 ctx
->do_append
= FALSE
;
1699 src_mode
= src_stat
.st_mode
;
1700 src_uid
= src_stat
.st_uid
;
1701 src_gid
= src_stat
.st_gid
;
1702 get_times (&src_stat
, ×
);
1703 file_size
= src_stat
.st_size
;
1705 open_flags
= O_WRONLY
;
1709 open_flags
|= O_APPEND
;
1711 open_flags
|= O_CREAT
| O_TRUNC
;
1715 open_flags
|= O_CREAT
| O_EXCL
;
1718 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1720 if (errno
!= EEXIST
)
1723 return_status
= FILE_SKIPALL
;
1726 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1727 if (return_status
== FILE_RETRY
)
1729 if (return_status
== FILE_SKIPALL
)
1730 ctx
->skip_all
= TRUE
;
1731 ctx
->do_append
= FALSE
;
1736 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1738 appending
= ctx
->do_append
;
1739 ctx
->do_append
= FALSE
;
1741 /* Find out the optimal buffer size. */
1742 while (mc_fstat (dest_desc
, &dst_stat
) != 0)
1745 return_status
= FILE_SKIPALL
;
1748 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1749 if (return_status
== FILE_RETRY
)
1751 if (return_status
== FILE_SKIPALL
)
1752 ctx
->skip_all
= TRUE
;
1757 /* try preallocate space; if fail, try copy anyway */
1758 while (vfs_preallocate (dest_desc
, file_size
, appending
? dst_stat
.st_size
: 0) != 0)
1762 /* cannot allocate, start the file copying anyway */
1763 return_status
= FILE_CONT
;
1768 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1770 if (return_status
== FILE_SKIPALL
)
1771 ctx
->skip_all
= TRUE
;
1773 if (ctx
->skip_all
|| return_status
== FILE_SKIP
)
1775 /* skip the space allocation error, start file copying */
1776 return_status
= FILE_CONT
;
1780 if (return_status
== FILE_ABORT
)
1782 mc_close (dest_desc
);
1784 mc_unlink (dst_vpath
);
1785 dst_status
= DEST_NONE
;
1789 /* return_status == FILE_RETRY -- try allocate space again */
1792 ctx
->eta_secs
= 0.0;
1795 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1796 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1798 file_progress_show (ctx
, 1, 1, "", TRUE
);
1799 return_status
= check_progress_buttons (ctx
);
1802 if (return_status
== FILE_CONT
)
1805 off_t n_read_total
= 0;
1806 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1807 int secs
, update_secs
;
1808 const char *stalled_msg
= "";
1809 gboolean is_first_time
= TRUE
;
1811 tv_last_update
= tv_transfer_start
;
1813 bufsize
= io_blksize (dst_stat
);
1814 buf
= g_malloc (bufsize
);
1818 ssize_t n_read
= -1, n_written
;
1821 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0) == 0)
1822 while ((n_read
= mc_read (src_desc
, buf
, bufsize
)) < 0 && !ctx
->skip_all
)
1824 return_status
= file_error (_("Cannot read source file \"%s\"\n%s"), src_path
);
1825 if (return_status
== FILE_RETRY
)
1827 if (return_status
== FILE_SKIPALL
)
1828 ctx
->skip_all
= TRUE
;
1835 gettimeofday (&tv_current
, NULL
);
1841 n_read_total
+= n_read
;
1843 /* Windows NT ftp servers report that files have no
1844 * permissions: -------, so if we happen to have actually
1845 * read something, we should fix the permissions.
1847 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1848 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1849 gettimeofday (&tv_last_input
, NULL
);
1852 while ((n_written
= mc_write (dest_desc
, t
, (size_t) n_read
)) < n_read
)
1854 gboolean write_errno_nospace
;
1858 n_read
-= n_written
;
1863 write_errno_nospace
= (n_written
< 0 && errno
== ENOSPC
);
1866 return_status
= FILE_SKIPALL
;
1869 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1871 if (return_status
== FILE_SKIP
)
1873 if (write_errno_nospace
)
1877 if (return_status
== FILE_SKIPALL
)
1879 ctx
->skip_all
= TRUE
;
1880 if (write_errno_nospace
)
1883 if (return_status
!= FILE_RETRY
)
1888 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1890 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1891 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1893 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1895 copy_file_file_display_progress (tctx
, ctx
,
1897 tv_transfer_start
, file_size
, n_read_total
);
1898 tv_last_update
= tv_current
;
1900 is_first_time
= FALSE
;
1902 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1904 stalled_msg
= _("(stalled)");
1908 gboolean force_update
;
1911 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1913 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1915 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1916 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1919 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1924 return_status
= check_progress_buttons (ctx
);
1926 if (return_status
!= FILE_CONT
)
1933 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1939 rotate_dash (FALSE
);
1940 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1942 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1943 if (temp_status
== FILE_RETRY
)
1945 if (temp_status
== FILE_ABORT
)
1946 return_status
= temp_status
;
1947 if (temp_status
== FILE_SKIPALL
)
1948 ctx
->skip_all
= TRUE
;
1952 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1954 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1955 if (temp_status
== FILE_RETRY
)
1957 if (temp_status
== FILE_SKIPALL
)
1958 ctx
->skip_all
= TRUE
;
1959 return_status
= temp_status
;
1963 if (dst_status
== DEST_SHORT
)
1965 /* Query to remove short file */
1966 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"),
1967 D_ERROR
, 2, _("&Delete"), _("&Keep")) == 0)
1968 mc_unlink (dst_vpath
);
1970 else if (dst_status
== DEST_FULL
)
1972 /* Copy has succeeded */
1973 if (!appending
&& ctx
->preserve_uidgid
)
1975 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1977 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1978 if (temp_status
== FILE_RETRY
)
1980 if (temp_status
== FILE_SKIPALL
)
1982 ctx
->skip_all
= TRUE
;
1983 return_status
= FILE_CONT
;
1985 if (temp_status
== FILE_SKIP
)
1986 return_status
= FILE_CONT
;
1995 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1997 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1998 if (temp_status
== FILE_RETRY
)
2000 if (temp_status
== FILE_SKIPALL
)
2002 ctx
->skip_all
= TRUE
;
2003 return_status
= FILE_CONT
;
2005 if (temp_status
== FILE_SKIP
)
2006 return_status
= FILE_CONT
;
2010 else if (!dst_exists
)
2012 src_mode
= umask (-1);
2014 src_mode
= 0100666 & ~src_mode
;
2015 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
2017 mc_utime (dst_vpath
, ×
);
2021 if (return_status
== FILE_CONT
)
2022 return_status
= progress_update_one (tctx
, ctx
, file_size
);
2025 vfs_path_free (src_vpath
);
2026 vfs_path_free (dst_vpath
);
2027 return return_status
;
2030 /* --------------------------------------------------------------------------------------------- */
2032 * I think these copy_*_* functions should have a return type.
2033 * anyway, this function *must* have two directories as arguments.
2035 /* FIXME: This function needs to check the return values of the
2039 copy_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
,
2040 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, GSList
* parent_dirs
)
2042 struct dirent
*next
;
2043 struct stat buf
, cbuf
;
2045 FileProgressStatus return_status
= FILE_CONT
;
2047 vfs_path_t
*src_vpath
, *dst_vpath
;
2048 gboolean do_mkdir
= TRUE
;
2050 src_vpath
= vfs_path_from_str (s
);
2051 dst_vpath
= vfs_path_from_str (d
);
2053 /* First get the mode of the source dir */
2056 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
2059 return_status
= FILE_SKIPALL
;
2062 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
2063 if (return_status
== FILE_RETRY
)
2064 goto retry_src_stat
;
2065 if (return_status
== FILE_SKIPALL
)
2066 ctx
->skip_all
= TRUE
;
2071 if (is_in_linklist (dest_dirs
, src_vpath
, &cbuf
))
2073 /* Don't copy a directory we created before (we don't want to copy
2074 infinitely if a directory is copied into itself) */
2075 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2076 return_status
= FILE_CONT
;
2080 /* Hmm, hardlink to directory??? - Norbert */
2081 /* FIXME: In this step we should do something
2082 in case the destination already exist */
2083 /* Check the hardlinks */
2084 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &cbuf
))
2086 /* We have made a hardlink - no more processing is necessary */
2090 if (!S_ISDIR (cbuf
.st_mode
))
2093 return_status
= FILE_SKIPALL
;
2096 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
2097 if (return_status
== FILE_RETRY
)
2098 goto retry_src_stat
;
2099 if (return_status
== FILE_SKIPALL
)
2100 ctx
->skip_all
= TRUE
;
2105 if (is_in_linklist (parent_dirs
, src_vpath
, &cbuf
))
2107 /* we found a cyclic symbolic link */
2108 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
2109 return_status
= FILE_SKIP
;
2113 lp
= g_new0 (struct link
, 1);
2114 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
2115 lp
->ino
= cbuf
.st_ino
;
2116 lp
->dev
= cbuf
.st_dev
;
2117 parent_dirs
= g_slist_prepend (parent_dirs
, lp
);
2120 /* Now, check if the dest dir exists, if not, create it. */
2121 if (mc_stat (dst_vpath
, &buf
) != 0)
2123 /* Here the dir doesn't exist : make it ! */
2124 if (move_over
&& mc_rename (src_vpath
, dst_vpath
) == 0)
2126 return_status
= FILE_CONT
;
2133 * If the destination directory exists, we want to copy the whole
2134 * directory, but we only want this to happen once.
2136 * Escape sequences added to the * to compiler warnings.
2137 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2138 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2140 if (!S_ISDIR (buf
.st_mode
))
2143 return_status
= FILE_SKIPALL
;
2146 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2147 if (return_status
== FILE_SKIPALL
)
2148 ctx
->skip_all
= TRUE
;
2149 if (return_status
== FILE_RETRY
)
2150 goto retry_dst_stat
;
2154 /* Dive into subdir if exists */
2155 if (toplevel
&& ctx
->dive_into_subdirs
)
2160 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), (char *) NULL
);
2161 vfs_path_free (tmp
);
2168 d
= vfs_path_as_str (dst_vpath
);
2172 while (my_mkdir (dst_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2175 return_status
= FILE_SKIPALL
;
2178 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), d
);
2179 if (return_status
== FILE_SKIPALL
)
2180 ctx
->skip_all
= TRUE
;
2182 if (return_status
!= FILE_RETRY
)
2186 lp
= g_new0 (struct link
, 1);
2187 mc_stat (dst_vpath
, &buf
);
2188 lp
->vfs
= vfs_path_get_by_index (dst_vpath
, -1)->class;
2189 lp
->ino
= buf
.st_ino
;
2190 lp
->dev
= buf
.st_dev
;
2191 dest_dirs
= g_slist_prepend (dest_dirs
, lp
);
2194 if (ctx
->preserve_uidgid
)
2196 while (mc_chown (dst_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2199 return_status
= FILE_SKIPALL
;
2202 return_status
= file_error (_("Cannot chown target directory \"%s\"\n%s"), d
);
2203 if (return_status
== FILE_SKIPALL
)
2204 ctx
->skip_all
= TRUE
;
2206 if (return_status
!= FILE_RETRY
)
2211 /* open the source dir for reading */
2212 reading
= mc_opendir (src_vpath
);
2213 if (reading
== NULL
)
2216 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2219 vfs_path_t
*tmp_vpath
;
2222 * Now, we don't want '.' and '..' to be created / copied at any time
2224 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
2227 /* get the filename and add it to the src directory */
2228 path
= mc_build_filename (s
, next
->d_name
, (char *) NULL
);
2229 tmp_vpath
= vfs_path_from_str (path
);
2231 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2232 if (S_ISDIR (buf
.st_mode
))
2236 mdpath
= mc_build_filename (d
, next
->d_name
, (char *) NULL
);
2238 * From here, we just intend to recursively copy subdirs, not
2239 * the double functionality of copying different when the target
2240 * dir already exists. So, we give the recursive call the flag 0
2241 * meaning no toplevel.
2244 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2251 dest_file
= mc_build_filename (d
, x_basename (path
), (char *) NULL
);
2252 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2258 if (do_delete
&& return_status
== FILE_CONT
)
2260 if (ctx
->erase_at_end
)
2262 lp
= g_new0 (struct link
, 1);
2263 lp
->src_vpath
= tmp_vpath
;
2264 lp
->st_mode
= buf
.st_mode
;
2265 erase_list
= g_slist_append (erase_list
, lp
);
2268 else if (S_ISDIR (buf
.st_mode
))
2269 return_status
= erase_dir_iff_empty (ctx
, tmp_vpath
, tctx
->progress_count
);
2271 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
2273 vfs_path_free (tmp_vpath
);
2275 mc_closedir (reading
);
2279 mc_timesbuf_t times
;
2281 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2282 get_times (&cbuf
, ×
);
2283 mc_utime (dst_vpath
, ×
);
2287 cbuf
.st_mode
= umask (-1);
2288 umask (cbuf
.st_mode
);
2289 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2290 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2294 free_link (parent_dirs
->data
);
2295 g_slist_free_1 (parent_dirs
);
2297 vfs_path_free (src_vpath
);
2298 vfs_path_free (dst_vpath
);
2299 return return_status
;
2304 /* --------------------------------------------------------------------------------------------- */
2305 /* {{{ Move routines */
2308 move_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
)
2310 struct stat sbuf
, dbuf
;
2311 FileProgressStatus return_status
= FILE_CONT
;
2312 gboolean move_over
= FALSE
;
2314 vfs_path_t
*src_vpath
, *dst_vpath
;
2316 src_vpath
= vfs_path_from_str (s
);
2317 dst_vpath
= vfs_path_from_str (d
);
2319 file_progress_show_source (ctx
, src_vpath
);
2320 file_progress_show_target (ctx
, dst_vpath
);
2322 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2324 return_status
= FILE_ABORT
;
2330 mc_stat (src_vpath
, &sbuf
);
2332 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2333 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2335 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2340 ; /* destination doesn't exist */
2341 else if (!ctx
->dive_into_subdirs
)
2348 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), (char *) NULL
);
2349 vfs_path_free (tmp
);
2352 d
= vfs_path_as_str (dst_vpath
);
2354 /* Check if the user inputted an existing dir */
2356 if (mc_stat (dst_vpath
, &dbuf
) == 0)
2360 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, TRUE
, TRUE
, NULL
);
2362 if (return_status
!= FILE_CONT
)
2366 else if (ctx
->skip_all
)
2367 return_status
= FILE_SKIPALL
;
2370 if (S_ISDIR (dbuf
.st_mode
))
2371 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), d
);
2373 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), d
);
2374 if (return_status
== FILE_SKIPALL
)
2375 ctx
->skip_all
= TRUE
;
2376 if (return_status
== FILE_RETRY
)
2377 goto retry_dst_stat
;
2384 if (mc_rename (src_vpath
, dst_vpath
) == 0)
2386 return_status
= FILE_CONT
;
2394 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2395 if (return_status
== FILE_SKIPALL
)
2396 ctx
->skip_all
= TRUE
;
2397 if (return_status
== FILE_RETRY
)
2402 /* Failed because of filesystem boundary -> copy dir instead */
2403 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, FALSE
, TRUE
, NULL
);
2405 if (return_status
!= FILE_CONT
)
2408 file_progress_show_source (ctx
, NULL
);
2409 file_progress_show_target (ctx
, NULL
);
2410 file_progress_show (ctx
, 0, 0, "", FALSE
);
2412 return_status
= check_progress_buttons (ctx
);
2413 if (return_status
!= FILE_CONT
)
2417 if (ctx
->erase_at_end
)
2419 /* Reset progress count before delete to avoid counting files twice */
2420 tctx
->progress_count
= tctx
->prev_progress_count
;
2422 while (erase_list
!= NULL
&& return_status
!= FILE_ABORT
)
2424 struct link
*lp
= (struct link
*) erase_list
->data
;
2426 if (S_ISDIR (lp
->st_mode
))
2427 return_status
= erase_dir_iff_empty (ctx
, lp
->src_vpath
, tctx
->progress_count
);
2429 return_status
= erase_file (tctx
, ctx
, lp
->src_vpath
);
2431 erase_list
= g_slist_remove (erase_list
, lp
);
2435 /* Save progress counter before move next directory */
2436 tctx
->prev_progress_count
= tctx
->progress_count
;
2438 erase_dir_iff_empty (ctx
, src_vpath
, tctx
->progress_count
);
2441 erase_list
= free_linklist (erase_list
);
2443 vfs_path_free (src_vpath
);
2444 vfs_path_free (dst_vpath
);
2445 return return_status
;
2450 /* --------------------------------------------------------------------------------------------- */
2451 /* {{{ Erase routines */
2454 erase_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* s_vpath
)
2456 FileProgressStatus error
;
2458 file_progress_show_deleting (ctx
, vfs_path_as_str (s_vpath
), NULL
);
2459 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2460 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2465 /* The old way to detect a non empty directory was:
2466 error = my_rmdir (s);
2467 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2468 For the linux user space nfs server (nfs-server-2.2beta29-2)
2469 we would have to check also for EIO. I hope the new way is
2470 fool proof. (Norbert)
2472 error
= check_dir_is_empty (s_vpath
);
2475 error
= query_recursive (ctx
, vfs_path_as_str (s_vpath
));
2476 if (error
== FILE_CONT
)
2477 error
= recursive_erase (tctx
, ctx
, s_vpath
);
2481 while (my_rmdir (vfs_path_as_str (s_vpath
)) == -1 && !ctx
->skip_all
)
2483 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), vfs_path_as_str (s_vpath
));
2484 if (error
!= FILE_RETRY
)
2493 /* --------------------------------------------------------------------------------------------- */
2494 /* {{{ Panel operate routines */
2497 dirsize_status_init_cb (status_msg_t
* sm
)
2499 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
2500 Widget
*wd
= WIDGET (sm
->dlg
);
2502 const char *b1_name
= N_("&Abort");
2503 const char *b2_name
= N_("&Skip");
2504 int b_width
, ui_width
;
2507 b1_name
= _(b1_name
);
2508 b2_name
= _(b2_name
);
2511 b_width
= str_term_width1 (b1_name
) + 4;
2512 if (dsm
->allow_skip
)
2513 b_width
+= str_term_width1 (b2_name
) + 4 + 1;
2515 ui_width
= MAX (COLS
/ 2, b_width
+ 6);
2516 dsm
->dirname
= label_new (2, 3, "");
2517 add_widget (sm
->dlg
, dsm
->dirname
);
2518 dsm
->count_size
= label_new (3, 3, "");
2519 add_widget (sm
->dlg
, dsm
->count_size
);
2520 add_widget (sm
->dlg
, hline_new (4, -1, -1));
2522 dsm
->abort_button
= WIDGET (button_new (5, 3, FILE_ABORT
, NORMAL_BUTTON
, b1_name
, NULL
));
2523 add_widget (sm
->dlg
, dsm
->abort_button
);
2524 if (dsm
->allow_skip
)
2526 dsm
->skip_button
= WIDGET (button_new (5, 3, FILE_SKIP
, NORMAL_BUTTON
, b2_name
, NULL
));
2527 add_widget (sm
->dlg
, dsm
->skip_button
);
2528 widget_select (dsm
->skip_button
);
2531 widget_set_size (wd
, wd
->y
, wd
->x
, 8, ui_width
);
2532 dirsize_status_locate_buttons (dsm
);
2535 /* --------------------------------------------------------------------------------------------- */
2538 dirsize_status_update_cb (status_msg_t
* sm
)
2540 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
2541 Widget
*wd
= WIDGET (sm
->dlg
);
2543 /* update second (longer label) */
2544 label_set_textv (dsm
->count_size
, _("Directories: %zu, total size: %s"),
2545 dsm
->dir_count
, size_trunc_sep (dsm
->total_size
, panels_options
.kilobyte_si
));
2547 /* enlarge dialog if required */
2548 if (WIDGET (dsm
->count_size
)->cols
+ 6 > wd
->cols
)
2550 dlg_set_size (sm
->dlg
, wd
->lines
, WIDGET (dsm
->count_size
)->cols
+ 6);
2551 dirsize_status_locate_buttons (dsm
);
2552 dlg_redraw (sm
->dlg
);
2555 /* adjust first label */
2556 label_set_text (dsm
->dirname
, str_trunc (vfs_path_as_str (dsm
->dirname_vpath
), wd
->cols
- 6));
2558 switch (status_msg_common_update (sm
))
2570 /* --------------------------------------------------------------------------------------------- */
2573 dirsize_status_deinit_cb (status_msg_t
* sm
)
2577 /* schedule to update passive panel */
2578 if (get_other_type () == view_listing
)
2579 other_panel
->dirty
= 1;
2582 /* --------------------------------------------------------------------------------------------- */
2586 * Computes the number of bytes used by the files in a directory
2590 compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* sm
,
2591 size_t * ret_dir_count
, size_t * ret_marked_count
, uintmax_t * ret_total
,
2592 gboolean compute_symlinks
)
2594 return do_compute_dir_size (dirname_vpath
, sm
, ret_dir_count
, ret_marked_count
, ret_total
,
2598 /* --------------------------------------------------------------------------------------------- */
2602 * Performs one of the operations on the selection on the source_panel
2603 * (copy, delete, move).
2605 * Returns TRUE if did change the directory
2606 * structure, Returns FALSE if user aborted
2608 * force_single forces operation on the current entry and affects
2609 * default destination. Current filename is used as default.
2613 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2615 WPanel
*panel
= PANEL (source_panel
);
2616 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2617 || (get_current_type () == view_tree
);
2619 const char *source
= NULL
;
2620 #ifdef WITH_FULL_PATHS
2621 vfs_path_t
*source_with_vpath
= NULL
;
2622 #endif /* WITH_FULL_PATHS */
2624 vfs_path_t
*dest_vpath
= NULL
;
2626 char *save_cwd
= NULL
, *save_dest
= NULL
;
2627 struct stat src_stat
;
2628 gboolean ret_val
= TRUE
;
2630 FileProgressStatus value
;
2631 file_op_context_t
*ctx
;
2632 file_op_total_context_t
*tctx
;
2633 vfs_path_t
*tmp_vpath
;
2634 filegui_dialog_type_t dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2636 gboolean do_bg
= FALSE
; /* do background operation? */
2638 static gboolean i18n_flag
= FALSE
;
2641 for (i
= G_N_ELEMENTS (op_names
); i
-- != 0;)
2642 op_names
[i
] = Q_ (op_names
[i
]);
2646 linklist
= free_linklist (linklist
);
2647 dest_dirs
= free_linklist (dest_dirs
);
2654 source
= selection (panel
)->fname
;
2656 source
= panel_get_file (panel
);
2658 ok
= !DIR_IS_DOTDOT (source
);
2661 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2664 vfs_path_t
*source_vpath
;
2666 source_vpath
= vfs_path_from_str (source
);
2668 /* Update stat to get actual info */
2669 ok
= mc_lstat (source_vpath
, &src_stat
) == 0;
2672 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2673 path_trunc (source
, 30), unix_error_string (errno
));
2675 /* Directory was changed outside MC. Reload it forced */
2676 if (!panel
->is_panelized
)
2678 panel_update_flags_t flags
= UP_RELOAD
;
2680 /* don't update panelized panel */
2681 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2682 flags
|= UP_ONLY_CURRENT
;
2684 update_panels (flags
, UP_KEEPSEL
);
2688 vfs_path_free (source_vpath
);
2695 ctx
= file_op_context_new (operation
);
2697 /* Show confirmation dialog */
2698 if (operation
!= OP_DELETE
)
2700 const char *tmp_dest_dir
;
2704 /* Forced single operations default to the original name */
2706 tmp_dest_dir
= source
;
2707 else if (get_other_type () == view_listing
)
2708 tmp_dest_dir
= vfs_path_as_str (other_panel
->cwd_vpath
);
2710 tmp_dest_dir
= vfs_path_as_str (panel
->cwd_vpath
);
2712 * Add trailing backslash only when do non-local ops.
2713 * It saves user from occasional file renames (when destination
2716 if (!force_single
&& tmp_dest_dir
!= NULL
&& tmp_dest_dir
[0] != '\0'
2717 && !IS_PATH_SEP (tmp_dest_dir
[strlen (tmp_dest_dir
) - 1]))
2719 /* add trailing separator */
2720 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2725 dest_dir
= g_strdup (tmp_dest_dir
);
2727 if (dest_dir
== NULL
)
2733 /* Generate confirmation prompt */
2735 panel_operate_generate_prompt (panel
, operation
, source
!= NULL
? &src_stat
: NULL
);
2738 file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2739 source
!= NULL
? source
: (const void *) &panel
->marked
, dest_dir
,
2745 if (dest
== NULL
|| dest
[0] == '\0')
2751 dest_vpath
= vfs_path_from_str (dest
);
2753 else if (confirm_delete
)
2756 char fmd_buf
[BUF_MEDIUM
];
2758 /* Generate confirmation prompt */
2760 panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
? &src_stat
: NULL
);
2763 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2766 const int fmd_xlen
= 64;
2767 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2768 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2776 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2785 tctx
= file_op_total_context_new ();
2786 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2788 #ifdef ENABLE_BACKGROUND
2789 /* Did the user select to do a background operation? */
2794 v
= do_background (ctx
,
2795 g_strconcat (op_names
[operation
], ": ",
2796 vfs_path_as_str (panel
->cwd_vpath
), (char *) NULL
));
2798 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2800 /* If we are the parent */
2803 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2805 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2806 vfs_path_free (dest_vpath
);
2808 /* file_op_context_destroy (ctx); */
2813 #endif /* ENABLE_BACKGROUND */
2815 if (operation
== OP_DELETE
)
2816 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2817 else if (single_entry
&& S_ISDIR (selection (panel
)->st
.st_mode
))
2818 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2819 else if (single_entry
|| force_single
)
2820 dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2822 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2825 /* Initialize things */
2826 /* We do not want to trash cache every time file is
2827 created/touched. However, this will make our cache contain
2830 && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2831 save_dest
= g_strdup (dest
);
2833 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2834 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2835 save_cwd
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
2837 /* Now, let's do the job */
2839 /* This code is only called by the tree and panel code */
2842 /* We now have ETA in all cases */
2844 /* One file: FIXME mc_chdir will take user out of any vfs */
2845 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2850 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2851 chdir_retcode
= mc_chdir (vpath
);
2852 vfs_path_free (vpath
);
2853 if (chdir_retcode
< 0)
2860 /* The source and src_stat variables have been initialized before */
2861 #ifdef WITH_FULL_PATHS
2862 if (g_path_is_absolute (source
))
2863 source_with_vpath
= vfs_path_from_str (source
);
2865 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2866 #endif /* WITH_FULL_PATHS */
2867 if (panel_operate_init_totals (panel
, vfs_path_as_str (source_with_vpath
), ctx
, dialog_type
)
2870 if (operation
== OP_DELETE
)
2872 if (S_ISDIR (src_stat
.st_mode
))
2873 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2875 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2879 temp
= transform_source (ctx
, source_with_vpath
);
2881 value
= transform_error
;
2884 char *repl_dest
, *temp2
;
2886 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2887 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2889 if (ctx
->search_handle
->error_str
!= NULL
)
2890 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2896 temp2
= mc_build_filename (repl_dest
, temp
, (char *) NULL
);
2900 vfs_path_free (dest_vpath
);
2902 dest_vpath
= vfs_path_from_str (dest
);
2907 /* we use file_mask_op_follow_links only with OP_COPY */
2908 ctx
->stat_func (source_with_vpath
, &src_stat
);
2910 if (S_ISDIR (src_stat
.st_mode
))
2912 copy_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2913 dest
, TRUE
, FALSE
, FALSE
, NULL
);
2916 copy_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2921 if (S_ISDIR (src_stat
.st_mode
))
2923 move_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
), dest
);
2926 move_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2931 /* Unknown file operation */
2935 } /* Copy or move operation */
2937 if ((value
== FILE_CONT
) && !force_single
)
2938 unmark_files (panel
);
2945 /* Check destination for copy or move operation */
2946 while (operation
!= OP_DELETE
)
2949 struct stat dst_stat
;
2951 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2953 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2957 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2961 if (panel_operate_init_totals (panel
, NULL
, ctx
, dialog_type
) == FILE_CONT
)
2963 /* Loop for every file, perform the actual copy operation */
2964 for (i
= 0; i
< panel
->dir
.len
; i
++)
2966 const char *source2
;
2968 if (!panel
->dir
.list
[i
].f
.marked
)
2969 continue; /* Skip the unmarked ones */
2971 source2
= panel
->dir
.list
[i
].fname
;
2972 src_stat
= panel
->dir
.list
[i
].st
;
2974 #ifdef WITH_FULL_PATHS
2975 vfs_path_free (source_with_vpath
);
2976 if (g_path_is_absolute (source2
))
2977 source_with_vpath
= vfs_path_from_str (source2
);
2980 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2981 #endif /* WITH_FULL_PATHS */
2983 if (operation
== OP_DELETE
)
2985 if (S_ISDIR (src_stat
.st_mode
))
2986 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2988 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2992 temp
= transform_source (ctx
, source_with_vpath
);
2994 value
= transform_error
;
2997 char *temp2
, *repl_dest
, *source_with_path_str
;
2999 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
3000 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
3002 if (ctx
->search_handle
->error_str
!= NULL
)
3003 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
3009 temp2
= mc_build_filename (repl_dest
, temp
, (char *) NULL
);
3012 source_with_path_str
=
3013 strutils_shell_unescape (vfs_path_as_str (source_with_vpath
));
3014 temp
= strutils_shell_unescape (temp2
);
3020 /* we use file_mask_op_follow_links only with OP_COPY */
3024 vpath
= vfs_path_from_str (source_with_path_str
);
3025 ctx
->stat_func (vpath
, &src_stat
);
3026 vfs_path_free (vpath
);
3028 if (S_ISDIR (src_stat
.st_mode
))
3029 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp
,
3030 TRUE
, FALSE
, FALSE
, NULL
);
3032 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp
);
3033 dest_dirs
= free_linklist (dest_dirs
);
3037 if (S_ISDIR (src_stat
.st_mode
))
3038 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp
);
3040 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp
);
3044 /* Unknown file operation */
3048 g_free (source_with_path_str
);
3051 } /* Copy or move operation */
3053 if (value
== FILE_ABORT
)
3056 if (value
== FILE_CONT
)
3057 do_file_mark (panel
, i
, 0);
3059 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3061 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3062 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3065 if (operation
!= OP_DELETE
)
3066 file_progress_show (ctx
, 0, 0, "", FALSE
);
3068 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3072 } /* Loop for every file */
3074 } /* Many entries */
3078 if (save_cwd
!= NULL
)
3080 tmp_vpath
= vfs_path_from_str (save_cwd
);
3081 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3082 vfs_path_free (tmp_vpath
);
3086 if (save_dest
!= NULL
)
3088 tmp_vpath
= vfs_path_from_str (save_dest
);
3089 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3090 vfs_path_free (tmp_vpath
);
3094 linklist
= free_linklist (linklist
);
3095 dest_dirs
= free_linklist (dest_dirs
);
3096 #ifdef WITH_FULL_PATHS
3097 vfs_path_free (source_with_vpath
);
3098 #endif /* WITH_FULL_PATHS */
3100 vfs_path_free (dest_vpath
);
3101 MC_PTR_FREE (ctx
->dest_mask
);
3103 #ifdef ENABLE_BACKGROUND
3104 /* Let our parent know we are saying bye bye */
3105 if (mc_global
.we_are_background
)
3107 int cur_pid
= getpid ();
3108 /* Send pid to parent with child context, it is fork and
3109 don't modify real parent ctx */
3111 parent_call ((void *) end_bg_process
, ctx
, 0);
3114 my_exit (EXIT_SUCCESS
);
3116 #endif /* ENABLE_BACKGROUND */
3118 file_op_total_context_destroy (tctx
);
3120 file_op_context_destroy (ctx
);
3127 /* --------------------------------------------------------------------------------------------- */
3128 /* {{{ Query/status report routines */
3129 /** Report error with one file */
3131 file_error (const char *format
, const char *file
)
3133 char buf
[BUF_MEDIUM
];
3135 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3137 return do_file_error (buf
);
3140 /* --------------------------------------------------------------------------------------------- */
3143 Cause emacs to enter folding mode for this file: