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
, MC_MAXPATHLEN
- 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
;
419 link_target
[len
] = 0;
421 if (ctx
->stable_symlinks
)
424 if (!vfs_file_is_local (src_vpath
) || !vfs_file_is_local (dst_vpath
))
426 message (D_ERROR
, MSG_ERROR
,
427 _("Cannot make stable symlinks across"
428 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
429 ctx
->stable_symlinks
= FALSE
;
433 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
435 const char *r
= strrchr (src_path
, PATH_SEP
);
442 p
= g_strndup (src_path
, r
- src_path
+ 1);
443 if (g_path_is_absolute (dst_path
))
444 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
446 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
448 if (vfs_path_tokens_count (q
) > 1)
451 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
453 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
454 s
= g_strconcat (p
, link_target
, (char *) NULL
);
456 g_strlcpy (link_target
, s
, sizeof (link_target
));
458 tmp_vpath2
= vfs_path_from_str (link_target
);
459 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
460 vfs_path_free (tmp_vpath1
);
461 vfs_path_free (tmp_vpath2
);
464 g_strlcpy (link_target
, s
, sizeof (link_target
));
473 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
476 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
479 return_status
= FILE_CONT
;
483 * if dst_exists, it is obvious that this had failed.
484 * We can delete the old symlink and try again...
488 if (mc_unlink (dst_vpath
) == 0)
489 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
492 return_status
= FILE_CONT
;
497 return_status
= FILE_SKIPALL
;
500 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
501 if (return_status
== FILE_SKIPALL
)
502 ctx
->skip_all
= TRUE
;
503 if (return_status
== FILE_RETRY
)
504 goto retry_dst_symlink
;
508 vfs_path_free (src_vpath
);
509 vfs_path_free (dst_vpath
);
510 vfs_path_free (link_target_vpath
);
511 return return_status
;
514 /* --------------------------------------------------------------------------------------------- */
516 * do_compute_dir_size:
518 * Computes the number of bytes used by the files in a directory
521 static FileProgressStatus
522 do_compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* dsm
,
523 size_t * dir_count
, size_t * ret_marked
, uintmax_t * ret_total
,
524 gboolean compute_symlinks
)
526 static guint64 timestamp
= 0;
527 /* update with 25 FPS rate */
528 static const guint64 delay
= G_USEC_PER_SEC
/ 25;
530 status_msg_t
*sm
= STATUS_MSG (dsm
);
534 struct dirent
*dirent
;
535 FileProgressStatus ret
= FILE_CONT
;
537 if (!compute_symlinks
)
539 res
= mc_lstat (dirname_vpath
, &s
);
543 /* don't scan symlink to directory */
544 if (S_ISLNK (s
.st_mode
))
547 *ret_total
+= (uintmax_t) s
.st_size
;
554 dir
= mc_opendir (dirname_vpath
);
558 while (ret
== FILE_CONT
&& (dirent
= mc_readdir (dir
)) != NULL
)
560 vfs_path_t
*tmp_vpath
;
562 if (DIR_IS_DOT (dirent
->d_name
) || DIR_IS_DOTDOT (dirent
->d_name
))
565 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, (char *) NULL
);
567 res
= mc_lstat (tmp_vpath
, &s
);
570 if (S_ISDIR (s
.st_mode
))
572 do_compute_dir_size (tmp_vpath
, dsm
, dir_count
, ret_marked
, ret_total
,
579 *ret_total
+= (uintmax_t) s
.st_size
;
582 if (ret
== FILE_CONT
&& sm
->update
!= NULL
&& mc_time_elapsed (×tamp
, delay
))
584 dsm
->dirname_vpath
= tmp_vpath
;
585 dsm
->dir_count
= *dir_count
;
586 dsm
->total_size
= *ret_total
;
587 ret
= sm
->update (sm
);
591 vfs_path_free (tmp_vpath
);
598 /* --------------------------------------------------------------------------------------------- */
600 static FileProgressStatus
601 progress_update_one (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, off_t add
)
603 struct timeval tv_current
;
604 static struct timeval tv_start
= { 0, 0 };
606 tctx
->progress_count
++;
607 tctx
->progress_bytes
+= (uintmax_t) add
;
609 if (tv_start
.tv_sec
== 0)
611 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
613 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
614 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
616 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
618 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
619 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
621 tv_start
.tv_sec
= tv_current
.tv_sec
;
624 return check_progress_buttons (ctx
);
627 /* --------------------------------------------------------------------------------------------- */
629 static FileProgressStatus
630 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
634 const char *head_msg
;
636 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
638 msg
= g_strdup_printf (fmt
, a
, b
);
639 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
643 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
646 /* --------------------------------------------------------------------------------------------- */
648 static FileProgressStatus
649 warn_same_file (const char *fmt
, const char *a
, const char *b
)
651 #ifdef ENABLE_BACKGROUND
656 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
, const char *a
, const char *b
);
660 pntr
.f
= real_warn_same_file
;
662 if (mc_global
.we_are_background
)
663 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
665 return real_warn_same_file (Foreground
, fmt
, a
, b
);
668 /* --------------------------------------------------------------------------------------------- */
669 /* {{{ Query/status report routines */
671 static FileProgressStatus
672 real_do_file_error (enum OperationMode mode
, const char *error
)
677 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
679 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
701 /* --------------------------------------------------------------------------------------------- */
703 static FileProgressStatus
704 real_query_recursive (file_op_context_t
* ctx
, enum OperationMode mode
, const char *s
)
706 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
711 msg
= mode
== Foreground
712 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
713 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
714 text
= g_strdup_printf (msg
, path_trunc (s
, 30));
719 ctx
->recursive_result
=
720 query_dialog (op_names
[OP_DELETE
], text
, D_ERROR
, 5, _("&Yes"), _("&No"), _("A&ll"),
721 _("Non&e"), _("&Abort"));
724 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
728 switch (ctx
->recursive_result
)
731 case RECURSIVE_ALWAYS
:
735 case RECURSIVE_NEVER
:
738 case RECURSIVE_ABORT
:
744 /* --------------------------------------------------------------------------------------------- */
746 #ifdef ENABLE_BACKGROUND
747 static FileProgressStatus
748 do_file_error (const char *str
)
754 FileProgressStatus (*f
) (enum OperationMode
, const char *);
758 pntr
.f
= real_do_file_error
;
760 if (mc_global
.we_are_background
)
761 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
763 return real_do_file_error (Foreground
, str
);
766 /* --------------------------------------------------------------------------------------------- */
768 static FileProgressStatus
769 query_recursive (file_op_context_t
* ctx
, const char *s
)
775 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *);
779 pntr
.f
= real_query_recursive
;
781 if (mc_global
.we_are_background
)
782 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
784 return real_query_recursive (ctx
, Foreground
, s
);
787 /* --------------------------------------------------------------------------------------------- */
789 static FileProgressStatus
790 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
791 struct stat
*_d_stat
)
797 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *,
798 struct stat
*, struct stat
*);
802 pntr
.f
= file_progress_real_query_replace
;
804 if (mc_global
.we_are_background
)
805 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
806 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
808 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
812 /* --------------------------------------------------------------------------------------------- */
814 static FileProgressStatus
815 do_file_error (const char *str
)
817 return real_do_file_error (Foreground
, str
);
820 /* --------------------------------------------------------------------------------------------- */
822 static FileProgressStatus
823 query_recursive (file_op_context_t
* ctx
, const char *s
)
825 return real_query_recursive (ctx
, Foreground
, s
);
828 /* --------------------------------------------------------------------------------------------- */
830 static FileProgressStatus
831 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
832 struct stat
*_d_stat
)
834 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
837 #endif /* !ENABLE_BACKGROUND */
839 /* --------------------------------------------------------------------------------------------- */
840 /** Report error with two files */
842 static FileProgressStatus
843 files_error (const char *format
, const char *file1
, const char *file2
)
845 char buf
[BUF_MEDIUM
];
846 char *nfile1
= g_strdup (path_trunc (file1
, 15));
847 char *nfile2
= g_strdup (path_trunc (file2
, 15));
849 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
854 return do_file_error (buf
);
859 /* --------------------------------------------------------------------------------------------- */
862 copy_file_file_display_progress (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
863 struct timeval tv_current
, struct timeval tv_transfer_start
,
864 off_t file_size
, off_t n_read_total
)
868 /* 1. Update rotating dash after some time */
872 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
874 if (n_read_total
== 0)
878 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
879 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
882 /* 4. Compute BPS rate */
883 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
884 if (ctx
->bps_time
< 1)
886 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
888 /* 5. Compute total ETA and BPS */
889 if (ctx
->progress_bytes
!= 0)
891 uintmax_t remain_bytes
;
893 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
896 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
901 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
902 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
905 /* broken on lot of little files */
907 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
908 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
913 /* --------------------------------------------------------------------------------------------- */
915 /* {{{ Move routines */
916 static FileProgressStatus
917 move_file_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
,
920 struct stat src_stats
, dst_stats
;
921 FileProgressStatus return_status
= FILE_CONT
;
922 gboolean copy_done
= FALSE
;
923 gboolean old_ask_overwrite
;
924 vfs_path_t
*src_vpath
, *dst_vpath
;
926 src_vpath
= vfs_path_from_str (s
);
927 dst_vpath
= vfs_path_from_str (d
);
929 file_progress_show_source (ctx
, src_vpath
);
930 file_progress_show_target (ctx
, dst_vpath
);
932 if (check_progress_buttons (ctx
) == FILE_ABORT
)
934 return_status
= FILE_ABORT
;
940 while (mc_lstat (src_vpath
, &src_stats
) != 0)
942 /* Source doesn't exist */
944 return_status
= FILE_SKIPALL
;
947 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
948 if (return_status
== FILE_SKIPALL
)
949 ctx
->skip_all
= TRUE
;
952 if (return_status
!= FILE_RETRY
)
956 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
958 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
960 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
964 if (S_ISDIR (dst_stats
.st_mode
))
966 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
968 return_status
= FILE_SKIP
;
972 if (confirm_overwrite
)
974 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
975 if (return_status
!= FILE_CONT
)
978 /* Ok to overwrite */
983 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
985 return_status
= make_symlink (ctx
, s
, d
);
986 if (return_status
== FILE_CONT
)
987 goto retry_src_remove
;
991 if (mc_rename (src_vpath
, dst_vpath
) == 0)
993 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
998 /* Comparison to EXDEV seems not to work in nfs if you're moving from
999 one nfs to the same, but on the server it is on two different
1000 filesystems. Then nfs returns EIO instead of EXDEV.
1001 Hope it will not hurt if we always in case of error try to copy/delete. */
1003 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1008 return_status
= FILE_SKIPALL
;
1011 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
1012 if (return_status
== FILE_SKIPALL
)
1013 ctx
->skip_all
= TRUE
;
1014 if (return_status
== FILE_RETRY
)
1022 /* Failed because filesystem boundary -> copy the file instead */
1023 old_ask_overwrite
= tctx
->ask_overwrite
;
1024 tctx
->ask_overwrite
= FALSE
;
1025 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
1026 tctx
->ask_overwrite
= old_ask_overwrite
;
1027 if (return_status
!= FILE_CONT
)
1032 file_progress_show_source (ctx
, NULL
);
1033 file_progress_show (ctx
, 0, 0, "", FALSE
);
1035 return_status
= check_progress_buttons (ctx
);
1036 if (return_status
!= FILE_CONT
)
1041 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
1043 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
1044 if (return_status
== FILE_RETRY
)
1045 goto retry_src_remove
;
1046 if (return_status
== FILE_SKIPALL
)
1047 ctx
->skip_all
= TRUE
;
1052 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
1055 vfs_path_free (src_vpath
);
1056 vfs_path_free (dst_vpath
);
1058 return return_status
;
1063 /* --------------------------------------------------------------------------------------------- */
1064 /* {{{ Erase routines */
1065 /** Don't update progress status if progress_count==NULL */
1067 static FileProgressStatus
1068 erase_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1072 file_progress_show_deleting (ctx
, vfs_path_as_str (vpath
), &tctx
->progress_count
);
1073 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1074 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1079 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
1081 /* ignore, most likely the mc_unlink fails, too */
1085 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
1089 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), vfs_path_as_str (vpath
));
1090 if (return_status
== FILE_ABORT
)
1091 return return_status
;
1092 if (return_status
== FILE_RETRY
)
1094 if (return_status
== FILE_SKIPALL
)
1095 ctx
->skip_all
= TRUE
;
1099 if (tctx
->progress_count
== 0)
1102 return check_progress_buttons (ctx
);
1105 /* --------------------------------------------------------------------------------------------- */
1108 Recursive remove of files
1110 skip ->warn every level, gets default
1111 skipall->remove as much as possible
1113 static FileProgressStatus
1114 recursive_erase (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1116 struct dirent
*next
;
1119 FileProgressStatus return_status
= FILE_CONT
;
1121 reading
= mc_opendir (vpath
);
1122 if (reading
== NULL
)
1125 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1127 vfs_path_t
*tmp_vpath
;
1130 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
1133 tmp_vpath
= vfs_path_append_new (vpath
, next
->d_name
, (char *) NULL
);
1134 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1136 mc_closedir (reading
);
1137 vfs_path_free (tmp_vpath
);
1140 if (S_ISDIR (buf
.st_mode
))
1141 return_status
= recursive_erase (tctx
, ctx
, tmp_vpath
);
1143 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
1144 vfs_path_free (tmp_vpath
);
1146 mc_closedir (reading
);
1148 if (return_status
== FILE_ABORT
)
1151 s
= vfs_path_as_str (vpath
);
1153 file_progress_show_deleting (ctx
, s
, NULL
);
1154 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1155 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1160 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1162 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1163 if (return_status
== FILE_RETRY
)
1165 if (return_status
== FILE_ABORT
)
1167 if (return_status
== FILE_SKIPALL
)
1168 ctx
->skip_all
= TRUE
;
1172 return return_status
;
1175 /* --------------------------------------------------------------------------------------------- */
1176 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1177 in the directory path points to, 0 else. */
1180 check_dir_is_empty (const vfs_path_t
* vpath
)
1186 dir
= mc_opendir (vpath
);
1190 for (d
= mc_readdir (dir
); d
!= NULL
; d
= mc_readdir (dir
))
1191 if (!DIR_IS_DOT (d
->d_name
) && !DIR_IS_DOTDOT (d
->d_name
))
1201 /* --------------------------------------------------------------------------------------------- */
1203 static FileProgressStatus
1204 erase_dir_iff_empty (file_op_context_t
* ctx
, const vfs_path_t
* vpath
, size_t count
)
1206 FileProgressStatus error
= FILE_CONT
;
1209 s
= vfs_path_as_str (vpath
);
1211 file_progress_show_deleting (ctx
, s
, NULL
);
1212 file_progress_show_count (ctx
, count
, ctx
->progress_count
);
1213 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1218 if (check_dir_is_empty (vpath
) == 1) /* not empty or error */
1220 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1222 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1223 if (error
== FILE_SKIPALL
)
1224 ctx
->skip_all
= TRUE
;
1225 if (error
!= FILE_RETRY
)
1235 /* --------------------------------------------------------------------------------------------- */
1236 /* {{{ Panel operate routines */
1239 * Return currently selected entry name or the name of the first marked
1240 * entry if there is one.
1244 panel_get_file (WPanel
* panel
)
1246 if (get_current_type () == view_tree
)
1249 const vfs_path_t
*selected_name
;
1251 tree
= (WTree
*) get_panel_widget (get_current_index ());
1252 selected_name
= tree_selected_name (tree
);
1253 return vfs_path_as_str (selected_name
);
1256 if (panel
->marked
!= 0)
1260 for (i
= 0; i
< panel
->dir
.len
; i
++)
1261 if (panel
->dir
.list
[i
].f
.marked
)
1262 return panel
->dir
.list
[i
].fname
;
1265 return panel
->dir
.list
[panel
->selected
].fname
;
1268 /* --------------------------------------------------------------------------------------------- */
1270 * panel_compute_totals:
1272 * compute the number of files and the number of bytes
1273 * used up by the whole selection, recursing directories
1274 * as required. In addition, it checks to see if it will
1275 * overwrite any files by doing the copy.
1278 static FileProgressStatus
1279 panel_compute_totals (const WPanel
* panel
, dirsize_status_msg_t
* sm
, size_t * ret_count
,
1280 uintmax_t * ret_total
, gboolean compute_symlinks
)
1283 size_t dir_count
= 0;
1285 for (i
= 0; i
< panel
->dir
.len
; i
++)
1289 if (!panel
->dir
.list
[i
].f
.marked
)
1292 s
= &panel
->dir
.list
[i
].st
;
1294 if (S_ISDIR (s
->st_mode
))
1297 FileProgressStatus status
;
1299 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, (char *) NULL
);
1300 status
= compute_dir_size (p
, sm
, &dir_count
, ret_count
, ret_total
, compute_symlinks
);
1303 if (status
!= FILE_CONT
)
1309 *ret_total
+= (uintmax_t) s
->st_size
;
1316 /* --------------------------------------------------------------------------------------------- */
1318 /** Initialize variables for progress bars */
1319 static FileProgressStatus
1320 panel_operate_init_totals (const WPanel
* panel
, const char *source
, file_op_context_t
* ctx
,
1321 filegui_dialog_type_t dialog_type
)
1323 FileProgressStatus status
;
1325 #ifdef ENABLE_BACKGROUND
1326 if (mc_global
.we_are_background
)
1330 if (verbose
&& file_op_compute_totals
)
1332 dirsize_status_msg_t dsm
;
1334 memset (&dsm
, 0, sizeof (dsm
));
1335 dsm
.allow_skip
= TRUE
;
1336 status_msg_init (STATUS_MSG (&dsm
), _("Directory scanning"), 0, dirsize_status_init_cb
,
1337 dirsize_status_update_cb
, dirsize_status_deinit_cb
);
1339 ctx
->progress_count
= 0;
1340 ctx
->progress_bytes
= 0;
1343 status
= panel_compute_totals (panel
, &dsm
, &ctx
->progress_count
, &ctx
->progress_bytes
,
1348 size_t dir_count
= 0;
1350 p
= vfs_path_from_str (source
);
1351 status
= compute_dir_size (p
, &dsm
, &dir_count
, &ctx
->progress_count
,
1352 &ctx
->progress_bytes
, ctx
->follow_links
);
1356 status_msg_deinit (STATUS_MSG (&dsm
));
1358 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1360 if (status
== FILE_SKIP
)
1366 ctx
->progress_count
= panel
->marked
;
1367 ctx
->progress_bytes
= panel
->total
;
1368 ctx
->progress_totals_computed
= FALSE
;
1371 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
1376 /* --------------------------------------------------------------------------------------------- */
1378 * Generate user prompt for panel operation.
1379 * src_stat must be not NULL for single source, and NULL for multiple sources
1383 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1384 const struct stat
*src_stat
)
1387 char *format_string
;
1390 static gboolean i18n_flag
= FALSE
;
1395 for (i
= G_N_ELEMENTS (op_names1
); i
-- != 0;)
1396 op_names1
[i
] = Q_ (op_names1
[i
]);
1399 for (i
= G_N_ELEMENTS (prompt_parts
); i
-- != 0;)
1400 prompt_parts
[i
] = _(prompt_parts
[i
]);
1402 one_format
= _(one_format
);
1403 many_format
= _(many_format
);
1404 #endif /* ENABLE_NLS */
1408 /* Possible prompts:
1410 * "Copy file \"%s\" with source mask:"
1411 * "Copy %d files with source mask:"
1412 * "Copy directory \"%s\" with source mask:"
1413 * "Copy %d directories with source mask:"
1414 * "Copy %d files/directories with source mask:"
1416 * "Move file \"%s\" with source mask:"
1417 * "Move %d files with source mask:"
1418 * "Move directory \"%s\" with source mask:"
1419 * "Move %d directories with source mask:"
1420 * "Move %d files/directories with source mask:"
1422 * "Delete file \"%s\"?"
1423 * "Delete %d files?"
1424 * "Delete directory \"%s\"?"
1425 * "Delete %d directories?"
1426 * "Delete %d files/directories?"
1429 cp
= (src_stat
!= NULL
? one_format
: many_format
);
1431 /* 1. Substitute %o */
1432 format_string
= str_replace_all (cp
, "%o", op_names1
[(int) operation
]);
1434 /* 2. Substitute %n */
1435 cp
= operation
== OP_DELETE
? "\n" : " ";
1437 format_string
= str_replace_all (sp
, "%n", cp
);
1440 /* 3. Substitute %f */
1441 if (src_stat
!= NULL
)
1442 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1443 else if (panel
->marked
== panel
->dirs_marked
)
1444 cp
= prompt_parts
[3];
1446 cp
= panel
->dirs_marked
!= 0 ? prompt_parts
[4] : prompt_parts
[1];
1449 format_string
= str_replace_all (sp
, "%f", cp
);
1452 /* 4. Substitute %m */
1453 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1455 format_string
= str_replace_all (sp
, "%m", cp
);
1458 return format_string
;
1461 /* --------------------------------------------------------------------------------------------- */
1463 #ifdef ENABLE_BACKGROUND
1465 end_bg_process (file_op_context_t
* ctx
, enum OperationMode mode
)
1472 unregister_task_with_pid (pid
);
1473 /* file_op_context_destroy(ctx); */
1479 /* --------------------------------------------------------------------------------------------- */
1480 /*** public functions ****************************************************************************/
1481 /* --------------------------------------------------------------------------------------------- */
1484 copy_file_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
1485 const char *src_path
, const char *dst_path
)
1487 uid_t src_uid
= (uid_t
) (-1);
1488 gid_t src_gid
= (gid_t
) (-1);
1490 int src_desc
, dest_desc
= -1;
1491 mode_t src_mode
= 0; /* The mode of the source file */
1492 struct stat src_stat
, dst_stat
;
1494 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1495 off_t file_size
= -1;
1496 FileProgressStatus return_status
, temp_status
;
1497 struct timeval tv_transfer_start
;
1498 dest_status_t dst_status
= DEST_NONE
;
1500 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1503 /* FIXME: We should not be using global variables! */
1505 return_status
= FILE_RETRY
;
1507 dst_vpath
= vfs_path_from_str (dst_path
);
1508 src_vpath
= vfs_path_from_str (src_path
);
1510 file_progress_show_source (ctx
, src_vpath
);
1511 file_progress_show_target (ctx
, dst_vpath
);
1513 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1515 return_status
= FILE_ABORT
;
1521 while (mc_stat (dst_vpath
, &dst_stat
) == 0)
1523 if (S_ISDIR (dst_stat
.st_mode
))
1526 return_status
= FILE_SKIPALL
;
1529 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1530 if (return_status
== FILE_SKIPALL
)
1531 ctx
->skip_all
= TRUE
;
1532 if (return_status
== FILE_RETRY
)
1542 while ((*ctx
->stat_func
) (src_vpath
, &src_stat
) != 0)
1545 return_status
= FILE_SKIPALL
;
1548 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1549 if (return_status
== FILE_SKIPALL
)
1550 ctx
->skip_all
= TRUE
;
1553 if (return_status
!= FILE_RETRY
)
1559 /* Destination already exists */
1560 if (src_stat
.st_dev
== dst_stat
.st_dev
&& src_stat
.st_ino
== dst_stat
.st_ino
)
1562 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1563 src_path
, dst_path
);
1567 /* Should we replace destination? */
1568 if (tctx
->ask_overwrite
)
1571 return_status
= query_replace (ctx
, dst_path
, &src_stat
, &dst_stat
);
1572 if (return_status
!= FILE_CONT
)
1577 if (!ctx
->do_append
)
1579 /* Check the hardlinks */
1580 if (!ctx
->follow_links
&& src_stat
.st_nlink
> 1
1581 && check_hardlinks (src_vpath
, dst_vpath
, &src_stat
))
1583 /* We have made a hardlink - no more processing is necessary */
1584 return_status
= FILE_CONT
;
1588 if (S_ISLNK (src_stat
.st_mode
))
1590 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1594 if (S_ISCHR (src_stat
.st_mode
) || S_ISBLK (src_stat
.st_mode
) || S_ISFIFO (src_stat
.st_mode
)
1595 || S_ISNAM (src_stat
.st_mode
) || S_ISSOCK (src_stat
.st_mode
))
1597 while (mc_mknod (dst_vpath
, src_stat
.st_mode
& ctx
->umask_kill
, src_stat
.st_rdev
) < 0
1600 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1601 if (return_status
== FILE_RETRY
)
1603 if (return_status
== FILE_SKIPALL
)
1604 ctx
->skip_all
= TRUE
;
1609 while (ctx
->preserve_uidgid
1610 && mc_chown (dst_vpath
, src_stat
.st_uid
, src_stat
.st_gid
) != 0 && !ctx
->skip_all
)
1612 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1613 if (temp_status
== FILE_SKIP
)
1615 if (temp_status
== FILE_SKIPALL
)
1616 ctx
->skip_all
= TRUE
;
1617 if (temp_status
!= FILE_RETRY
)
1619 return_status
= temp_status
;
1624 while (ctx
->preserve
&& mc_chmod (dst_vpath
, src_stat
.st_mode
& ctx
->umask_kill
) != 0
1627 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1628 if (temp_status
== FILE_SKIP
)
1630 if (temp_status
== FILE_SKIPALL
)
1631 ctx
->skip_all
= TRUE
;
1632 if (temp_status
!= FILE_RETRY
)
1634 return_status
= temp_status
;
1639 return_status
= FILE_CONT
;
1644 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1646 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1648 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1649 if (return_status
== FILE_RETRY
)
1651 if (return_status
== FILE_SKIPALL
)
1652 ctx
->skip_all
= TRUE
;
1653 if (return_status
== FILE_SKIP
)
1655 ctx
->do_append
= FALSE
;
1659 if (ctx
->do_reget
!= 0)
1661 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1663 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1665 ctx
->do_append
= FALSE
;
1669 while (mc_fstat (src_desc
, &src_stat
) != 0)
1672 return_status
= FILE_SKIPALL
;
1675 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1676 if (return_status
== FILE_RETRY
)
1678 if (return_status
== FILE_SKIPALL
)
1679 ctx
->skip_all
= TRUE
;
1680 ctx
->do_append
= FALSE
;
1685 src_mode
= src_stat
.st_mode
;
1686 src_uid
= src_stat
.st_uid
;
1687 src_gid
= src_stat
.st_gid
;
1688 utb
.actime
= src_stat
.st_atime
;
1689 utb
.modtime
= src_stat
.st_mtime
;
1690 file_size
= src_stat
.st_size
;
1692 open_flags
= O_WRONLY
;
1696 open_flags
|= O_APPEND
;
1698 open_flags
|= O_CREAT
| O_TRUNC
;
1702 open_flags
|= O_CREAT
| O_EXCL
;
1705 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1707 if (errno
!= EEXIST
)
1710 return_status
= FILE_SKIPALL
;
1713 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1714 if (return_status
== FILE_RETRY
)
1716 if (return_status
== FILE_SKIPALL
)
1717 ctx
->skip_all
= TRUE
;
1718 ctx
->do_append
= FALSE
;
1723 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1725 appending
= ctx
->do_append
;
1726 ctx
->do_append
= FALSE
;
1728 /* Find out the optimal buffer size. */
1729 while (mc_fstat (dest_desc
, &dst_stat
) != 0)
1732 return_status
= FILE_SKIPALL
;
1735 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1736 if (return_status
== FILE_RETRY
)
1738 if (return_status
== FILE_SKIPALL
)
1739 ctx
->skip_all
= TRUE
;
1744 /* try preallocate space; if fail, try copy anyway */
1745 while (vfs_preallocate (dest_desc
, file_size
, appending
? dst_stat
.st_size
: 0) != 0)
1749 /* cannot allocate, start the file copying anyway */
1750 return_status
= FILE_CONT
;
1755 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1757 if (return_status
== FILE_SKIPALL
)
1758 ctx
->skip_all
= TRUE
;
1760 if (ctx
->skip_all
|| return_status
== FILE_SKIP
)
1762 /* skip the space allocation error, start file copying */
1763 return_status
= FILE_CONT
;
1767 if (return_status
== FILE_ABORT
)
1769 mc_close (dest_desc
);
1771 mc_unlink (dst_vpath
);
1772 dst_status
= DEST_NONE
;
1776 /* return_status == FILE_RETRY -- try allocate space again */
1779 ctx
->eta_secs
= 0.0;
1782 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1783 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1785 file_progress_show (ctx
, 1, 1, "", TRUE
);
1786 return_status
= check_progress_buttons (ctx
);
1789 if (return_status
== FILE_CONT
)
1792 off_t n_read_total
= 0;
1793 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1794 int secs
, update_secs
;
1795 const char *stalled_msg
= "";
1796 gboolean is_first_time
= TRUE
;
1798 tv_last_update
= tv_transfer_start
;
1800 bufsize
= io_blksize (dst_stat
);
1801 buf
= g_malloc (bufsize
);
1805 ssize_t n_read
= -1, n_written
;
1808 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0) == 0)
1809 while ((n_read
= mc_read (src_desc
, buf
, bufsize
)) < 0 && !ctx
->skip_all
)
1811 return_status
= file_error (_("Cannot read source file \"%s\"\n%s"), src_path
);
1812 if (return_status
== FILE_RETRY
)
1814 if (return_status
== FILE_SKIPALL
)
1815 ctx
->skip_all
= TRUE
;
1822 gettimeofday (&tv_current
, NULL
);
1828 n_read_total
+= n_read
;
1830 /* Windows NT ftp servers report that files have no
1831 * permissions: -------, so if we happen to have actually
1832 * read something, we should fix the permissions.
1834 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1835 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1836 gettimeofday (&tv_last_input
, NULL
);
1839 while ((n_written
= mc_write (dest_desc
, t
, (size_t) n_read
)) < n_read
)
1841 gboolean write_errno_nospace
;
1845 n_read
-= n_written
;
1850 write_errno_nospace
= (n_written
< 0 && errno
== ENOSPC
);
1853 return_status
= FILE_SKIPALL
;
1856 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1858 if (return_status
== FILE_SKIP
)
1860 if (write_errno_nospace
)
1864 if (return_status
== FILE_SKIPALL
)
1866 ctx
->skip_all
= TRUE
;
1867 if (write_errno_nospace
)
1870 if (return_status
!= FILE_RETRY
)
1875 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1877 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1878 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1880 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1882 copy_file_file_display_progress (tctx
, ctx
,
1884 tv_transfer_start
, file_size
, n_read_total
);
1885 tv_last_update
= tv_current
;
1887 is_first_time
= FALSE
;
1889 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1891 stalled_msg
= _("(stalled)");
1895 gboolean force_update
;
1898 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1900 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1902 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1903 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1906 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1911 return_status
= check_progress_buttons (ctx
);
1913 if (return_status
!= FILE_CONT
)
1920 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1926 rotate_dash (FALSE
);
1927 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1929 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1930 if (temp_status
== FILE_RETRY
)
1932 if (temp_status
== FILE_ABORT
)
1933 return_status
= temp_status
;
1934 if (temp_status
== FILE_SKIPALL
)
1935 ctx
->skip_all
= TRUE
;
1939 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1941 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1942 if (temp_status
== FILE_RETRY
)
1944 if (temp_status
== FILE_SKIPALL
)
1945 ctx
->skip_all
= TRUE
;
1946 return_status
= temp_status
;
1950 if (dst_status
== DEST_SHORT
)
1952 /* Query to remove short file */
1953 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"),
1954 D_ERROR
, 2, _("&Delete"), _("&Keep")) == 0)
1955 mc_unlink (dst_vpath
);
1957 else if (dst_status
== DEST_FULL
)
1959 /* Copy has succeeded */
1960 if (!appending
&& ctx
->preserve_uidgid
)
1962 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1964 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1965 if (temp_status
== FILE_RETRY
)
1967 if (temp_status
== FILE_SKIPALL
)
1969 ctx
->skip_all
= TRUE
;
1970 return_status
= FILE_CONT
;
1972 if (temp_status
== FILE_SKIP
)
1973 return_status
= FILE_CONT
;
1982 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1984 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1985 if (temp_status
== FILE_RETRY
)
1987 if (temp_status
== FILE_SKIPALL
)
1989 ctx
->skip_all
= TRUE
;
1990 return_status
= FILE_CONT
;
1992 if (temp_status
== FILE_SKIP
)
1993 return_status
= FILE_CONT
;
1997 else if (!dst_exists
)
1999 src_mode
= umask (-1);
2001 src_mode
= 0100666 & ~src_mode
;
2002 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
2004 mc_utime (dst_vpath
, &utb
);
2008 if (return_status
== FILE_CONT
)
2009 return_status
= progress_update_one (tctx
, ctx
, file_size
);
2012 vfs_path_free (src_vpath
);
2013 vfs_path_free (dst_vpath
);
2014 return return_status
;
2017 /* --------------------------------------------------------------------------------------------- */
2019 * I think these copy_*_* functions should have a return type.
2020 * anyway, this function *must* have two directories as arguments.
2022 /* FIXME: This function needs to check the return values of the
2026 copy_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
,
2027 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, GSList
* parent_dirs
)
2029 struct dirent
*next
;
2030 struct stat buf
, cbuf
;
2032 FileProgressStatus return_status
= FILE_CONT
;
2034 vfs_path_t
*src_vpath
, *dst_vpath
;
2035 gboolean do_mkdir
= TRUE
;
2037 src_vpath
= vfs_path_from_str (s
);
2038 dst_vpath
= vfs_path_from_str (d
);
2040 /* First get the mode of the source dir */
2043 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
2046 return_status
= FILE_SKIPALL
;
2049 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
2050 if (return_status
== FILE_RETRY
)
2051 goto retry_src_stat
;
2052 if (return_status
== FILE_SKIPALL
)
2053 ctx
->skip_all
= TRUE
;
2058 if (is_in_linklist (dest_dirs
, src_vpath
, &cbuf
))
2060 /* Don't copy a directory we created before (we don't want to copy
2061 infinitely if a directory is copied into itself) */
2062 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2063 return_status
= FILE_CONT
;
2067 /* Hmm, hardlink to directory??? - Norbert */
2068 /* FIXME: In this step we should do something
2069 in case the destination already exist */
2070 /* Check the hardlinks */
2071 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &cbuf
))
2073 /* We have made a hardlink - no more processing is necessary */
2077 if (!S_ISDIR (cbuf
.st_mode
))
2080 return_status
= FILE_SKIPALL
;
2083 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
2084 if (return_status
== FILE_RETRY
)
2085 goto retry_src_stat
;
2086 if (return_status
== FILE_SKIPALL
)
2087 ctx
->skip_all
= TRUE
;
2092 if (is_in_linklist (parent_dirs
, src_vpath
, &cbuf
))
2094 /* we found a cyclic symbolic link */
2095 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
2096 return_status
= FILE_SKIP
;
2100 lp
= g_new0 (struct link
, 1);
2101 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
2102 lp
->ino
= cbuf
.st_ino
;
2103 lp
->dev
= cbuf
.st_dev
;
2104 parent_dirs
= g_slist_prepend (parent_dirs
, lp
);
2107 /* Now, check if the dest dir exists, if not, create it. */
2108 if (mc_stat (dst_vpath
, &buf
) != 0)
2110 /* Here the dir doesn't exist : make it ! */
2111 if (move_over
&& mc_rename (src_vpath
, dst_vpath
) == 0)
2113 return_status
= FILE_CONT
;
2120 * If the destination directory exists, we want to copy the whole
2121 * directory, but we only want this to happen once.
2123 * Escape sequences added to the * to compiler warnings.
2124 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2125 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2127 if (!S_ISDIR (buf
.st_mode
))
2130 return_status
= FILE_SKIPALL
;
2133 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2134 if (return_status
== FILE_SKIPALL
)
2135 ctx
->skip_all
= TRUE
;
2136 if (return_status
== FILE_RETRY
)
2137 goto retry_dst_stat
;
2141 /* Dive into subdir if exists */
2142 if (toplevel
&& ctx
->dive_into_subdirs
)
2147 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), (char *) NULL
);
2148 vfs_path_free (tmp
);
2155 d
= vfs_path_as_str (dst_vpath
);
2159 while (my_mkdir (dst_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2162 return_status
= FILE_SKIPALL
;
2165 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), d
);
2166 if (return_status
== FILE_SKIPALL
)
2167 ctx
->skip_all
= TRUE
;
2169 if (return_status
!= FILE_RETRY
)
2173 lp
= g_new0 (struct link
, 1);
2174 mc_stat (dst_vpath
, &buf
);
2175 lp
->vfs
= vfs_path_get_by_index (dst_vpath
, -1)->class;
2176 lp
->ino
= buf
.st_ino
;
2177 lp
->dev
= buf
.st_dev
;
2178 dest_dirs
= g_slist_prepend (dest_dirs
, lp
);
2181 if (ctx
->preserve_uidgid
)
2183 while (mc_chown (dst_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2186 return_status
= FILE_SKIPALL
;
2189 return_status
= file_error (_("Cannot chown target directory \"%s\"\n%s"), d
);
2190 if (return_status
== FILE_SKIPALL
)
2191 ctx
->skip_all
= TRUE
;
2193 if (return_status
!= FILE_RETRY
)
2198 /* open the source dir for reading */
2199 reading
= mc_opendir (src_vpath
);
2200 if (reading
== NULL
)
2203 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2206 vfs_path_t
*tmp_vpath
;
2209 * Now, we don't want '.' and '..' to be created / copied at any time
2211 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
2214 /* get the filename and add it to the src directory */
2215 path
= mc_build_filename (s
, next
->d_name
, (char *) NULL
);
2216 tmp_vpath
= vfs_path_from_str (path
);
2218 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2219 if (S_ISDIR (buf
.st_mode
))
2223 mdpath
= mc_build_filename (d
, next
->d_name
, (char *) NULL
);
2225 * From here, we just intend to recursively copy subdirs, not
2226 * the double functionality of copying different when the target
2227 * dir already exists. So, we give the recursive call the flag 0
2228 * meaning no toplevel.
2231 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2238 dest_file
= mc_build_filename (d
, x_basename (path
), (char *) NULL
);
2239 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2245 if (do_delete
&& return_status
== FILE_CONT
)
2247 if (ctx
->erase_at_end
)
2249 lp
= g_new0 (struct link
, 1);
2250 lp
->src_vpath
= tmp_vpath
;
2251 lp
->st_mode
= buf
.st_mode
;
2252 erase_list
= g_slist_append (erase_list
, lp
);
2255 else if (S_ISDIR (buf
.st_mode
))
2256 return_status
= erase_dir_iff_empty (ctx
, tmp_vpath
, tctx
->progress_count
);
2258 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
2260 vfs_path_free (tmp_vpath
);
2262 mc_closedir (reading
);
2268 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2269 utb
.actime
= cbuf
.st_atime
;
2270 utb
.modtime
= cbuf
.st_mtime
;
2271 mc_utime (dst_vpath
, &utb
);
2275 cbuf
.st_mode
= umask (-1);
2276 umask (cbuf
.st_mode
);
2277 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2278 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2282 free_link (parent_dirs
->data
);
2283 g_slist_free_1 (parent_dirs
);
2285 vfs_path_free (src_vpath
);
2286 vfs_path_free (dst_vpath
);
2287 return return_status
;
2292 /* --------------------------------------------------------------------------------------------- */
2293 /* {{{ Move routines */
2296 move_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
)
2298 struct stat sbuf
, dbuf
;
2299 FileProgressStatus return_status
= FILE_CONT
;
2300 gboolean move_over
= FALSE
;
2302 vfs_path_t
*src_vpath
, *dst_vpath
;
2304 src_vpath
= vfs_path_from_str (s
);
2305 dst_vpath
= vfs_path_from_str (d
);
2307 file_progress_show_source (ctx
, src_vpath
);
2308 file_progress_show_target (ctx
, dst_vpath
);
2310 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2312 return_status
= FILE_ABORT
;
2318 mc_stat (src_vpath
, &sbuf
);
2320 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2321 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2323 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2328 ; /* destination doesn't exist */
2329 else if (!ctx
->dive_into_subdirs
)
2336 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), (char *) NULL
);
2337 vfs_path_free (tmp
);
2340 d
= vfs_path_as_str (dst_vpath
);
2342 /* Check if the user inputted an existing dir */
2344 if (mc_stat (dst_vpath
, &dbuf
) == 0)
2348 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, TRUE
, TRUE
, NULL
);
2350 if (return_status
!= FILE_CONT
)
2354 else if (ctx
->skip_all
)
2355 return_status
= FILE_SKIPALL
;
2358 if (S_ISDIR (dbuf
.st_mode
))
2359 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), d
);
2361 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), d
);
2362 if (return_status
== FILE_SKIPALL
)
2363 ctx
->skip_all
= TRUE
;
2364 if (return_status
== FILE_RETRY
)
2365 goto retry_dst_stat
;
2372 if (mc_rename (src_vpath
, dst_vpath
) == 0)
2374 return_status
= FILE_CONT
;
2382 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2383 if (return_status
== FILE_SKIPALL
)
2384 ctx
->skip_all
= TRUE
;
2385 if (return_status
== FILE_RETRY
)
2390 /* Failed because of filesystem boundary -> copy dir instead */
2391 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, FALSE
, TRUE
, NULL
);
2393 if (return_status
!= FILE_CONT
)
2396 file_progress_show_source (ctx
, NULL
);
2397 file_progress_show_target (ctx
, NULL
);
2398 file_progress_show (ctx
, 0, 0, "", FALSE
);
2400 return_status
= check_progress_buttons (ctx
);
2401 if (return_status
!= FILE_CONT
)
2405 if (ctx
->erase_at_end
)
2407 /* Reset progress count before delete to avoid counting files twice */
2408 tctx
->progress_count
= tctx
->prev_progress_count
;
2410 while (erase_list
!= NULL
&& return_status
!= FILE_ABORT
)
2412 struct link
*lp
= (struct link
*) erase_list
->data
;
2414 if (S_ISDIR (lp
->st_mode
))
2415 return_status
= erase_dir_iff_empty (ctx
, lp
->src_vpath
, tctx
->progress_count
);
2417 return_status
= erase_file (tctx
, ctx
, lp
->src_vpath
);
2419 erase_list
= g_slist_remove (erase_list
, lp
);
2423 /* Save progress counter before move next directory */
2424 tctx
->prev_progress_count
= tctx
->progress_count
;
2426 erase_dir_iff_empty (ctx
, src_vpath
, tctx
->progress_count
);
2429 erase_list
= free_linklist (erase_list
);
2431 vfs_path_free (src_vpath
);
2432 vfs_path_free (dst_vpath
);
2433 return return_status
;
2438 /* --------------------------------------------------------------------------------------------- */
2439 /* {{{ Erase routines */
2442 erase_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* s_vpath
)
2444 FileProgressStatus error
;
2446 file_progress_show_deleting (ctx
, vfs_path_as_str (s_vpath
), NULL
);
2447 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2448 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2453 /* The old way to detect a non empty directory was:
2454 error = my_rmdir (s);
2455 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2456 For the linux user space nfs server (nfs-server-2.2beta29-2)
2457 we would have to check also for EIO. I hope the new way is
2458 fool proof. (Norbert)
2460 error
= check_dir_is_empty (s_vpath
);
2463 error
= query_recursive (ctx
, vfs_path_as_str (s_vpath
));
2464 if (error
== FILE_CONT
)
2465 error
= recursive_erase (tctx
, ctx
, s_vpath
);
2469 while (my_rmdir (vfs_path_as_str (s_vpath
)) == -1 && !ctx
->skip_all
)
2471 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), vfs_path_as_str (s_vpath
));
2472 if (error
!= FILE_RETRY
)
2481 /* --------------------------------------------------------------------------------------------- */
2482 /* {{{ Panel operate routines */
2485 dirsize_status_init_cb (status_msg_t
* sm
)
2487 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
2488 Widget
*wd
= WIDGET (sm
->dlg
);
2490 const char *b1_name
= N_("&Abort");
2491 const char *b2_name
= N_("&Skip");
2492 int b_width
, ui_width
;
2495 b1_name
= _(b1_name
);
2496 b2_name
= _(b2_name
);
2499 b_width
= str_term_width1 (b1_name
) + 4;
2500 if (dsm
->allow_skip
)
2501 b_width
+= str_term_width1 (b2_name
) + 4 + 1;
2503 ui_width
= MAX (COLS
/ 2, b_width
+ 6);
2504 dsm
->dirname
= label_new (2, 3, "");
2505 add_widget (sm
->dlg
, dsm
->dirname
);
2506 dsm
->count_size
= label_new (3, 3, "");
2507 add_widget (sm
->dlg
, dsm
->count_size
);
2508 add_widget (sm
->dlg
, hline_new (4, -1, -1));
2510 dsm
->abort_button
= WIDGET (button_new (5, 3, FILE_ABORT
, NORMAL_BUTTON
, b1_name
, NULL
));
2511 add_widget (sm
->dlg
, dsm
->abort_button
);
2512 if (dsm
->allow_skip
)
2514 dsm
->skip_button
= WIDGET (button_new (5, 3, FILE_SKIP
, NORMAL_BUTTON
, b2_name
, NULL
));
2515 add_widget (sm
->dlg
, dsm
->skip_button
);
2516 dlg_select_widget (dsm
->skip_button
);
2519 widget_set_size (wd
, wd
->y
, wd
->x
, 8, ui_width
);
2520 dirsize_status_locate_buttons (dsm
);
2523 /* --------------------------------------------------------------------------------------------- */
2526 dirsize_status_update_cb (status_msg_t
* sm
)
2528 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
2529 Widget
*wd
= WIDGET (sm
->dlg
);
2531 /* update second (longer label) */
2532 label_set_textv (dsm
->count_size
, _("Directories: %zd, total size: %s"),
2533 dsm
->dir_count
, size_trunc_sep (dsm
->total_size
, panels_options
.kilobyte_si
));
2535 /* enlarge dialog if required */
2536 if (WIDGET (dsm
->count_size
)->cols
+ 6 > wd
->cols
)
2538 dlg_set_size (sm
->dlg
, wd
->lines
, WIDGET (dsm
->count_size
)->cols
+ 6);
2539 dirsize_status_locate_buttons (dsm
);
2540 dlg_redraw (sm
->dlg
);
2543 /* adjust first label */
2544 label_set_text (dsm
->dirname
, str_trunc (vfs_path_as_str (dsm
->dirname_vpath
), wd
->cols
- 6));
2546 switch (status_msg_common_update (sm
))
2558 /* --------------------------------------------------------------------------------------------- */
2561 dirsize_status_deinit_cb (status_msg_t
* sm
)
2565 /* schedule to update passive panel */
2566 if (get_other_type () == view_listing
)
2567 other_panel
->dirty
= 1;
2570 /* --------------------------------------------------------------------------------------------- */
2574 * Computes the number of bytes used by the files in a directory
2578 compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* sm
,
2579 size_t * ret_dir_count
, size_t * ret_marked_count
, uintmax_t * ret_total
,
2580 gboolean compute_symlinks
)
2582 return do_compute_dir_size (dirname_vpath
, sm
, ret_dir_count
, ret_marked_count
, ret_total
,
2586 /* --------------------------------------------------------------------------------------------- */
2590 * Performs one of the operations on the selection on the source_panel
2591 * (copy, delete, move).
2593 * Returns TRUE if did change the directory
2594 * structure, Returns FALSE if user aborted
2596 * force_single forces operation on the current entry and affects
2597 * default destination. Current filename is used as default.
2601 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2603 WPanel
*panel
= PANEL (source_panel
);
2604 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2605 || (get_current_type () == view_tree
);
2607 const char *source
= NULL
;
2608 #ifdef WITH_FULL_PATHS
2609 vfs_path_t
*source_with_vpath
= NULL
;
2610 #endif /* WITH_FULL_PATHS */
2612 vfs_path_t
*dest_vpath
= NULL
;
2614 char *save_cwd
= NULL
, *save_dest
= NULL
;
2615 struct stat src_stat
;
2616 gboolean ret_val
= TRUE
;
2618 FileProgressStatus value
;
2619 file_op_context_t
*ctx
;
2620 file_op_total_context_t
*tctx
;
2621 vfs_path_t
*tmp_vpath
;
2622 filegui_dialog_type_t dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2624 gboolean do_bg
= FALSE
; /* do background operation? */
2626 static gboolean i18n_flag
= FALSE
;
2629 for (i
= G_N_ELEMENTS (op_names
); i
-- != 0;)
2630 op_names
[i
] = Q_ (op_names
[i
]);
2634 linklist
= free_linklist (linklist
);
2635 dest_dirs
= free_linklist (dest_dirs
);
2642 source
= selection (panel
)->fname
;
2644 source
= panel_get_file (panel
);
2646 ok
= !DIR_IS_DOTDOT (source
);
2649 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2652 vfs_path_t
*source_vpath
;
2654 source_vpath
= vfs_path_from_str (source
);
2656 /* Update stat to get actual info */
2657 ok
= mc_lstat (source_vpath
, &src_stat
) == 0;
2660 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2661 path_trunc (source
, 30), unix_error_string (errno
));
2663 /* Directory was changed outside MC. Reload it forced */
2664 if (!panel
->is_panelized
)
2666 panel_update_flags_t flags
= UP_RELOAD
;
2668 /* don't update panelized panel */
2669 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2670 flags
|= UP_ONLY_CURRENT
;
2672 update_panels (flags
, UP_KEEPSEL
);
2676 vfs_path_free (source_vpath
);
2683 ctx
= file_op_context_new (operation
);
2685 /* Show confirmation dialog */
2686 if (operation
!= OP_DELETE
)
2688 const char *tmp_dest_dir
;
2692 /* Forced single operations default to the original name */
2694 tmp_dest_dir
= source
;
2695 else if (get_other_type () == view_listing
)
2696 tmp_dest_dir
= vfs_path_as_str (other_panel
->cwd_vpath
);
2698 tmp_dest_dir
= vfs_path_as_str (panel
->cwd_vpath
);
2700 * Add trailing backslash only when do non-local ops.
2701 * It saves user from occasional file renames (when destination
2704 if (!force_single
&& tmp_dest_dir
!= NULL
&& tmp_dest_dir
[0] != '\0'
2705 && !IS_PATH_SEP (tmp_dest_dir
[strlen (tmp_dest_dir
) - 1]))
2707 /* add trailing separator */
2708 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2713 dest_dir
= g_strdup (tmp_dest_dir
);
2715 if (dest_dir
== NULL
)
2721 /* Generate confirmation prompt */
2723 panel_operate_generate_prompt (panel
, operation
, source
!= NULL
? &src_stat
: NULL
);
2726 file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2727 source
!= NULL
? source
: (const void *) &panel
->marked
, dest_dir
,
2733 if (dest
== NULL
|| dest
[0] == '\0')
2739 dest_vpath
= vfs_path_from_str (dest
);
2741 else if (confirm_delete
)
2744 char fmd_buf
[BUF_MEDIUM
];
2746 /* Generate confirmation prompt */
2748 panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
? &src_stat
: NULL
);
2751 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2754 const int fmd_xlen
= 64;
2755 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2756 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2764 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2773 tctx
= file_op_total_context_new ();
2774 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2776 #ifdef ENABLE_BACKGROUND
2777 /* Did the user select to do a background operation? */
2782 v
= do_background (ctx
,
2783 g_strconcat (op_names
[operation
], ": ",
2784 vfs_path_as_str (panel
->cwd_vpath
), (char *) NULL
));
2786 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2788 /* If we are the parent */
2791 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2793 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2794 vfs_path_free (dest_vpath
);
2796 /* file_op_context_destroy (ctx); */
2801 #endif /* ENABLE_BACKGROUND */
2803 if (operation
== OP_DELETE
)
2804 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2805 else if (single_entry
&& S_ISDIR (selection (panel
)->st
.st_mode
))
2806 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2807 else if (single_entry
|| force_single
)
2808 dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2810 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2813 /* Initialize things */
2814 /* We do not want to trash cache every time file is
2815 created/touched. However, this will make our cache contain
2818 && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2819 save_dest
= g_strdup (dest
);
2821 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2822 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2823 save_cwd
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
2825 /* Now, let's do the job */
2827 /* This code is only called by the tree and panel code */
2830 /* We now have ETA in all cases */
2832 /* One file: FIXME mc_chdir will take user out of any vfs */
2833 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2838 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2839 chdir_retcode
= mc_chdir (vpath
);
2840 vfs_path_free (vpath
);
2841 if (chdir_retcode
< 0)
2848 /* The source and src_stat variables have been initialized before */
2849 #ifdef WITH_FULL_PATHS
2850 if (g_path_is_absolute (source
))
2851 source_with_vpath
= vfs_path_from_str (source
);
2853 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2854 #endif /* WITH_FULL_PATHS */
2855 if (panel_operate_init_totals (panel
, vfs_path_as_str (source_with_vpath
), ctx
, dialog_type
)
2858 if (operation
== OP_DELETE
)
2860 if (S_ISDIR (src_stat
.st_mode
))
2861 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2863 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2867 temp
= transform_source (ctx
, source_with_vpath
);
2869 value
= transform_error
;
2872 char *repl_dest
, *temp2
;
2874 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2875 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2877 if (ctx
->search_handle
->error_str
!= NULL
)
2878 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2884 temp2
= mc_build_filename (repl_dest
, temp
, (char *) NULL
);
2888 vfs_path_free (dest_vpath
);
2890 dest_vpath
= vfs_path_from_str (dest
);
2895 /* we use file_mask_op_follow_links only with OP_COPY */
2896 ctx
->stat_func (source_with_vpath
, &src_stat
);
2898 if (S_ISDIR (src_stat
.st_mode
))
2900 copy_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2901 dest
, TRUE
, FALSE
, FALSE
, NULL
);
2904 copy_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2909 if (S_ISDIR (src_stat
.st_mode
))
2911 move_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
), dest
);
2914 move_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2919 /* Unknown file operation */
2923 } /* Copy or move operation */
2925 if ((value
== FILE_CONT
) && !force_single
)
2926 unmark_files (panel
);
2933 /* Check destination for copy or move operation */
2934 while (operation
!= OP_DELETE
)
2937 struct stat dst_stat
;
2939 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2941 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2945 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2949 if (panel_operate_init_totals (panel
, NULL
, ctx
, dialog_type
) == FILE_CONT
)
2951 /* Loop for every file, perform the actual copy operation */
2952 for (i
= 0; i
< panel
->dir
.len
; i
++)
2954 const char *source2
;
2956 if (!panel
->dir
.list
[i
].f
.marked
)
2957 continue; /* Skip the unmarked ones */
2959 source2
= panel
->dir
.list
[i
].fname
;
2960 src_stat
= panel
->dir
.list
[i
].st
;
2962 #ifdef WITH_FULL_PATHS
2963 vfs_path_free (source_with_vpath
);
2964 if (g_path_is_absolute (source2
))
2965 source_with_vpath
= vfs_path_from_str (source2
);
2968 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2969 #endif /* WITH_FULL_PATHS */
2971 if (operation
== OP_DELETE
)
2973 if (S_ISDIR (src_stat
.st_mode
))
2974 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2976 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2980 temp
= transform_source (ctx
, source_with_vpath
);
2982 value
= transform_error
;
2985 char *temp2
, *repl_dest
, *source_with_path_str
;
2987 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2988 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2990 if (ctx
->search_handle
->error_str
!= NULL
)
2991 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2997 temp2
= mc_build_filename (repl_dest
, temp
, (char *) NULL
);
3000 source_with_path_str
=
3001 strutils_shell_unescape (vfs_path_as_str (source_with_vpath
));
3002 temp
= strutils_shell_unescape (temp2
);
3008 /* we use file_mask_op_follow_links only with OP_COPY */
3012 vpath
= vfs_path_from_str (source_with_path_str
);
3013 ctx
->stat_func (vpath
, &src_stat
);
3014 vfs_path_free (vpath
);
3016 if (S_ISDIR (src_stat
.st_mode
))
3017 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp
,
3018 TRUE
, FALSE
, FALSE
, NULL
);
3020 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp
);
3021 dest_dirs
= free_linklist (dest_dirs
);
3025 if (S_ISDIR (src_stat
.st_mode
))
3026 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp
);
3028 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp
);
3032 /* Unknown file operation */
3036 g_free (source_with_path_str
);
3039 } /* Copy or move operation */
3041 if (value
== FILE_ABORT
)
3044 if (value
== FILE_CONT
)
3045 do_file_mark (panel
, i
, 0);
3047 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3049 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3050 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3053 if (operation
!= OP_DELETE
)
3054 file_progress_show (ctx
, 0, 0, "", FALSE
);
3056 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3060 } /* Loop for every file */
3062 } /* Many entries */
3066 if (save_cwd
!= NULL
)
3068 tmp_vpath
= vfs_path_from_str (save_cwd
);
3069 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3070 vfs_path_free (tmp_vpath
);
3074 if (save_dest
!= NULL
)
3076 tmp_vpath
= vfs_path_from_str (save_dest
);
3077 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3078 vfs_path_free (tmp_vpath
);
3082 linklist
= free_linklist (linklist
);
3083 dest_dirs
= free_linklist (dest_dirs
);
3084 #ifdef WITH_FULL_PATHS
3085 vfs_path_free (source_with_vpath
);
3086 #endif /* WITH_FULL_PATHS */
3088 vfs_path_free (dest_vpath
);
3089 MC_PTR_FREE (ctx
->dest_mask
);
3091 #ifdef ENABLE_BACKGROUND
3092 /* Let our parent know we are saying bye bye */
3093 if (mc_global
.we_are_background
)
3095 int cur_pid
= getpid ();
3096 /* Send pid to parent with child context, it is fork and
3097 don't modify real parent ctx */
3099 parent_call ((void *) end_bg_process
, ctx
, 0);
3102 my_exit (EXIT_SUCCESS
);
3104 #endif /* ENABLE_BACKGROUND */
3106 file_op_total_context_destroy (tctx
);
3108 file_op_context_destroy (ctx
);
3115 /* --------------------------------------------------------------------------------------------- */
3116 /* {{{ Query/status report routines */
3117 /** Report error with one file */
3119 file_error (const char *format
, const char *file
)
3121 char buf
[BUF_MEDIUM
];
3123 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3125 return do_file_error (buf
);
3128 /* --------------------------------------------------------------------------------------------- */
3131 Cause emacs to enter folding mode for this file: