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 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
251 transform_error
= FILE_ABORT
;
257 transform_error
= FILE_SKIP
;
264 /* --------------------------------------------------------------------------------------------- */
267 free_link (void *data
)
269 struct link
*lp
= (struct link
*) data
;
271 vfs_path_free (lp
->src_vpath
);
272 vfs_path_free (lp
->dst_vpath
);
276 /* --------------------------------------------------------------------------------------------- */
279 free_linklist (GSList
* lp
)
281 g_slist_free_full (lp
, free_link
);
286 /* --------------------------------------------------------------------------------------------- */
289 is_in_linklist (const GSList
* lp
, const vfs_path_t
* vpath
, const struct stat
*sb
)
291 const struct vfs_class
*class;
292 ino_t ino
= sb
->st_ino
;
293 dev_t dev
= sb
->st_dev
;
295 class = vfs_path_get_last_path_vfs (vpath
);
297 for (; lp
!= NULL
; lp
= (const GSList
*) g_slist_next (lp
))
299 const struct link
*lnk
= (const struct link
*) lp
->data
;
301 if (lnk
->vfs
== class && lnk
->ino
== ino
&& lnk
->dev
== dev
)
307 /* --------------------------------------------------------------------------------------------- */
309 * Check and made hardlink
311 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
312 * and a hardlink was successfully made
316 check_hardlinks (const vfs_path_t
* src_vpath
, const vfs_path_t
* dst_vpath
, struct stat
*pstat
)
321 const struct vfs_class
*my_vfs
;
322 ino_t ino
= pstat
->st_ino
;
323 dev_t dev
= pstat
->st_dev
;
324 struct stat link_stat
;
326 if ((vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
329 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
331 for (lp
= linklist
; lp
!= NULL
; lp
= g_slist_next (lp
))
333 lnk
= (struct link
*) lp
->data
;
335 if (lnk
->vfs
== my_vfs
&& lnk
->ino
== ino
&& lnk
->dev
== dev
)
337 const struct vfs_class
*lp_name_class
;
340 lp_name_class
= vfs_path_get_last_path_vfs (lnk
->src_vpath
);
341 stat_result
= mc_stat (lnk
->src_vpath
, &link_stat
);
343 if (stat_result
== 0 && link_stat
.st_ino
== ino
344 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
346 const struct vfs_class
*p_class
, *dst_name_class
;
348 dst_name_class
= vfs_path_get_last_path_vfs (dst_vpath
);
349 p_class
= vfs_path_get_last_path_vfs (lnk
->dst_vpath
);
351 if (dst_name_class
== p_class
&&
352 mc_stat (lnk
->dst_vpath
, &link_stat
) == 0 &&
353 mc_link (lnk
->dst_vpath
, dst_vpath
) == 0)
357 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
362 lnk
= g_new0 (struct link
, 1);
368 lnk
->src_vpath
= vfs_path_clone (src_vpath
);
369 lnk
->dst_vpath
= vfs_path_clone (dst_vpath
);
370 linklist
= g_slist_prepend (linklist
, lnk
);
376 /* --------------------------------------------------------------------------------------------- */
378 * Duplicate the contents of the symbolic link src_path in dst_path.
379 * Try to make a stable symlink if the option "stable symlink" was
380 * set in the file mask dialog.
381 * If dst_path is an existing symlink it will be deleted silently
382 * (upper levels take already care of existing files at dst_path).
385 static FileProgressStatus
386 make_symlink (file_op_context_t
* ctx
, const char *src_path
, const char *dst_path
)
388 char link_target
[MC_MAXPATHLEN
];
390 FileProgressStatus return_status
;
392 vfs_path_t
*src_vpath
;
393 vfs_path_t
*dst_vpath
;
394 gboolean dst_is_symlink
;
395 vfs_path_t
*link_target_vpath
= NULL
;
397 src_vpath
= vfs_path_from_str (src_path
);
398 dst_vpath
= vfs_path_from_str (dst_path
);
399 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
402 len
= mc_readlink (src_vpath
, link_target
, MC_MAXPATHLEN
- 1);
406 return_status
= FILE_SKIPALL
;
409 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
410 if (return_status
== FILE_SKIPALL
)
411 ctx
->skip_all
= TRUE
;
412 if (return_status
== FILE_RETRY
)
413 goto retry_src_readlink
;
417 link_target
[len
] = 0;
419 if (ctx
->stable_symlinks
)
422 if (!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
;
431 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
433 const char *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
);
454 g_strlcpy (link_target
, s
, sizeof (link_target
));
456 tmp_vpath2
= vfs_path_from_str (link_target
);
457 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
458 vfs_path_free (tmp_vpath1
);
459 vfs_path_free (tmp_vpath2
);
462 g_strlcpy (link_target
, s
, sizeof (link_target
));
471 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
474 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
477 return_status
= FILE_CONT
;
481 * if dst_exists, it is obvious that this had failed.
482 * We can delete the old symlink and try again...
486 if (mc_unlink (dst_vpath
) == 0)
487 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
490 return_status
= FILE_CONT
;
495 return_status
= FILE_SKIPALL
;
498 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
499 if (return_status
== FILE_SKIPALL
)
500 ctx
->skip_all
= TRUE
;
501 if (return_status
== FILE_RETRY
)
502 goto retry_dst_symlink
;
506 vfs_path_free (src_vpath
);
507 vfs_path_free (dst_vpath
);
508 vfs_path_free (link_target_vpath
);
509 return return_status
;
512 /* --------------------------------------------------------------------------------------------- */
514 * do_compute_dir_size:
516 * Computes the number of bytes used by the files in a directory
519 static FileProgressStatus
520 do_compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* dsm
,
521 size_t * dir_count
, size_t * ret_marked
, uintmax_t * ret_total
,
522 gboolean compute_symlinks
)
524 static guint64 timestamp
= 0;
525 /* update with 25 FPS rate */
526 static const guint64 delay
= G_USEC_PER_SEC
/ 25;
528 status_msg_t
*sm
= STATUS_MSG (dsm
);
532 struct dirent
*dirent
;
533 FileProgressStatus ret
= FILE_CONT
;
535 if (!compute_symlinks
)
537 res
= mc_lstat (dirname_vpath
, &s
);
541 /* don't scan symlink to directory */
542 if (S_ISLNK (s
.st_mode
))
545 *ret_total
+= (uintmax_t) s
.st_size
;
552 dir
= mc_opendir (dirname_vpath
);
556 while (ret
== FILE_CONT
&& (dirent
= mc_readdir (dir
)) != NULL
)
558 vfs_path_t
*tmp_vpath
;
560 if (DIR_IS_DOT (dirent
->d_name
) || DIR_IS_DOTDOT (dirent
->d_name
))
563 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, (char *) NULL
);
565 res
= mc_lstat (tmp_vpath
, &s
);
568 if (S_ISDIR (s
.st_mode
))
570 do_compute_dir_size (tmp_vpath
, dsm
, dir_count
, ret_marked
, ret_total
,
577 *ret_total
+= (uintmax_t) s
.st_size
;
580 if (ret
== FILE_CONT
&& sm
->update
!= NULL
&& mc_time_elapsed (×tamp
, delay
))
582 dsm
->dirname_vpath
= tmp_vpath
;
583 dsm
->dir_count
= *dir_count
;
584 dsm
->total_size
= *ret_total
;
585 ret
= sm
->update (sm
);
589 vfs_path_free (tmp_vpath
);
596 /* --------------------------------------------------------------------------------------------- */
598 static FileProgressStatus
599 progress_update_one (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, off_t add
)
601 struct timeval tv_current
;
602 static struct timeval tv_start
= { 0, 0 };
604 tctx
->progress_count
++;
605 tctx
->progress_bytes
+= (uintmax_t) add
;
607 if (tv_start
.tv_sec
== 0)
609 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
611 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
612 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
614 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
616 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
617 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
619 tv_start
.tv_sec
= tv_current
.tv_sec
;
622 return check_progress_buttons (ctx
);
625 /* --------------------------------------------------------------------------------------------- */
627 static FileProgressStatus
628 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
632 const char *head_msg
;
634 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
636 msg
= g_strdup_printf (fmt
, a
, b
);
637 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
641 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
644 /* --------------------------------------------------------------------------------------------- */
646 static FileProgressStatus
647 warn_same_file (const char *fmt
, const char *a
, const char *b
)
649 #ifdef ENABLE_BACKGROUND
654 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
, const char *a
, const char *b
);
658 pntr
.f
= real_warn_same_file
;
660 if (mc_global
.we_are_background
)
661 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
663 return real_warn_same_file (Foreground
, fmt
, a
, b
);
666 /* --------------------------------------------------------------------------------------------- */
667 /* {{{ Query/status report routines */
669 static FileProgressStatus
670 real_do_file_error (enum OperationMode mode
, const char *error
)
675 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
677 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
699 /* --------------------------------------------------------------------------------------------- */
701 static FileProgressStatus
702 real_query_recursive (file_op_context_t
* ctx
, enum OperationMode mode
, const char *s
)
704 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
709 msg
= mode
== Foreground
710 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
711 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
712 text
= g_strdup_printf (msg
, path_trunc (s
, 30));
717 ctx
->recursive_result
=
718 query_dialog (op_names
[OP_DELETE
], text
, D_ERROR
, 5, _("&Yes"), _("&No"), _("A&ll"),
719 _("Non&e"), _("&Abort"));
722 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
726 switch (ctx
->recursive_result
)
729 case RECURSIVE_ALWAYS
:
733 case RECURSIVE_NEVER
:
736 case RECURSIVE_ABORT
:
742 /* --------------------------------------------------------------------------------------------- */
744 #ifdef ENABLE_BACKGROUND
745 static FileProgressStatus
746 do_file_error (const char *str
)
752 FileProgressStatus (*f
) (enum OperationMode
, const char *);
756 pntr
.f
= real_do_file_error
;
758 if (mc_global
.we_are_background
)
759 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
761 return real_do_file_error (Foreground
, str
);
764 /* --------------------------------------------------------------------------------------------- */
766 static FileProgressStatus
767 query_recursive (file_op_context_t
* ctx
, const char *s
)
773 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *);
777 pntr
.f
= real_query_recursive
;
779 if (mc_global
.we_are_background
)
780 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
782 return real_query_recursive (ctx
, Foreground
, s
);
785 /* --------------------------------------------------------------------------------------------- */
787 static FileProgressStatus
788 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
789 struct stat
*_d_stat
)
795 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *,
796 struct stat
*, struct stat
*);
800 pntr
.f
= file_progress_real_query_replace
;
802 if (mc_global
.we_are_background
)
803 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
804 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
806 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
810 /* --------------------------------------------------------------------------------------------- */
812 static FileProgressStatus
813 do_file_error (const char *str
)
815 return real_do_file_error (Foreground
, str
);
818 /* --------------------------------------------------------------------------------------------- */
820 static FileProgressStatus
821 query_recursive (file_op_context_t
* ctx
, const char *s
)
823 return real_query_recursive (ctx
, Foreground
, s
);
826 /* --------------------------------------------------------------------------------------------- */
828 static FileProgressStatus
829 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
830 struct stat
*_d_stat
)
832 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
835 #endif /* !ENABLE_BACKGROUND */
837 /* --------------------------------------------------------------------------------------------- */
838 /** Report error with two files */
840 static FileProgressStatus
841 files_error (const char *format
, const char *file1
, const char *file2
)
843 char buf
[BUF_MEDIUM
];
844 char *nfile1
= g_strdup (path_trunc (file1
, 15));
845 char *nfile2
= g_strdup (path_trunc (file2
, 15));
847 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
852 return do_file_error (buf
);
857 /* --------------------------------------------------------------------------------------------- */
860 copy_file_file_display_progress (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
861 struct timeval tv_current
, struct timeval tv_transfer_start
,
862 off_t file_size
, off_t n_read_total
)
866 /* 1. Update rotating dash after some time */
870 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
872 if (n_read_total
== 0)
876 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
877 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
880 /* 4. Compute BPS rate */
881 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
882 if (ctx
->bps_time
< 1)
884 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
886 /* 5. Compute total ETA and BPS */
887 if (ctx
->progress_bytes
!= 0)
889 uintmax_t remain_bytes
;
891 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
894 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
899 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
900 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
903 /* broken on lot of little files */
905 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
906 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
911 /* --------------------------------------------------------------------------------------------- */
913 /* {{{ Move routines */
914 static FileProgressStatus
915 move_file_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
,
918 struct stat src_stats
, dst_stats
;
919 FileProgressStatus return_status
= FILE_CONT
;
920 gboolean copy_done
= FALSE
;
921 gboolean old_ask_overwrite
;
922 vfs_path_t
*src_vpath
, *dst_vpath
;
924 src_vpath
= vfs_path_from_str (s
);
925 dst_vpath
= vfs_path_from_str (d
);
927 file_progress_show_source (ctx
, src_vpath
);
928 file_progress_show_target (ctx
, dst_vpath
);
930 if (check_progress_buttons (ctx
) == FILE_ABORT
)
932 return_status
= FILE_ABORT
;
938 while (mc_lstat (src_vpath
, &src_stats
) != 0)
940 /* Source doesn't exist */
942 return_status
= FILE_SKIPALL
;
945 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
946 if (return_status
== FILE_SKIPALL
)
947 ctx
->skip_all
= TRUE
;
950 if (return_status
!= FILE_RETRY
)
954 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
956 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
958 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
962 if (S_ISDIR (dst_stats
.st_mode
))
964 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
966 return_status
= FILE_SKIP
;
970 if (confirm_overwrite
)
972 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
973 if (return_status
!= FILE_CONT
)
976 /* Ok to overwrite */
981 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
983 return_status
= make_symlink (ctx
, s
, d
);
984 if (return_status
== FILE_CONT
)
985 goto retry_src_remove
;
989 if (mc_rename (src_vpath
, dst_vpath
) == 0)
991 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
996 /* Comparison to EXDEV seems not to work in nfs if you're moving from
997 one nfs to the same, but on the server it is on two different
998 filesystems. Then nfs returns EIO instead of EXDEV.
999 Hope it will not hurt if we always in case of error try to copy/delete. */
1001 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1006 return_status
= FILE_SKIPALL
;
1009 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
1010 if (return_status
== FILE_SKIPALL
)
1011 ctx
->skip_all
= TRUE
;
1012 if (return_status
== FILE_RETRY
)
1020 /* Failed because filesystem boundary -> copy the file instead */
1021 old_ask_overwrite
= tctx
->ask_overwrite
;
1022 tctx
->ask_overwrite
= FALSE
;
1023 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
1024 tctx
->ask_overwrite
= old_ask_overwrite
;
1025 if (return_status
!= FILE_CONT
)
1030 file_progress_show_source (ctx
, NULL
);
1031 file_progress_show (ctx
, 0, 0, "", FALSE
);
1033 return_status
= check_progress_buttons (ctx
);
1034 if (return_status
!= FILE_CONT
)
1039 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
1041 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
1042 if (return_status
== FILE_RETRY
)
1043 goto retry_src_remove
;
1044 if (return_status
== FILE_SKIPALL
)
1045 ctx
->skip_all
= TRUE
;
1050 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
1053 vfs_path_free (src_vpath
);
1054 vfs_path_free (dst_vpath
);
1056 return return_status
;
1061 /* --------------------------------------------------------------------------------------------- */
1062 /* {{{ Erase routines */
1063 /** Don't update progress status if progress_count==NULL */
1065 static FileProgressStatus
1066 erase_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1070 file_progress_show_deleting (ctx
, vfs_path_as_str (vpath
), &tctx
->progress_count
);
1071 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1072 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1077 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
1079 /* ignore, most likely the mc_unlink fails, too */
1083 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
1087 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), vfs_path_as_str (vpath
));
1088 if (return_status
== FILE_ABORT
)
1089 return return_status
;
1090 if (return_status
== FILE_RETRY
)
1092 if (return_status
== FILE_SKIPALL
)
1093 ctx
->skip_all
= TRUE
;
1097 if (tctx
->progress_count
== 0)
1100 return check_progress_buttons (ctx
);
1103 /* --------------------------------------------------------------------------------------------- */
1106 Recursive remove of files
1108 skip ->warn every level, gets default
1109 skipall->remove as much as possible
1111 static FileProgressStatus
1112 recursive_erase (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1114 struct dirent
*next
;
1117 FileProgressStatus return_status
= FILE_CONT
;
1119 reading
= mc_opendir (vpath
);
1120 if (reading
== NULL
)
1123 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1125 vfs_path_t
*tmp_vpath
;
1128 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
1131 tmp_vpath
= vfs_path_append_new (vpath
, next
->d_name
, (char *) NULL
);
1132 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1134 mc_closedir (reading
);
1135 vfs_path_free (tmp_vpath
);
1138 if (S_ISDIR (buf
.st_mode
))
1139 return_status
= recursive_erase (tctx
, ctx
, tmp_vpath
);
1141 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
1142 vfs_path_free (tmp_vpath
);
1144 mc_closedir (reading
);
1146 if (return_status
== FILE_ABORT
)
1149 s
= vfs_path_as_str (vpath
);
1151 file_progress_show_deleting (ctx
, s
, NULL
);
1152 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1153 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1158 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1160 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1161 if (return_status
== FILE_RETRY
)
1163 if (return_status
== FILE_ABORT
)
1165 if (return_status
== FILE_SKIPALL
)
1166 ctx
->skip_all
= TRUE
;
1170 return return_status
;
1173 /* --------------------------------------------------------------------------------------------- */
1174 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1175 in the directory path points to, 0 else. */
1178 check_dir_is_empty (const vfs_path_t
* vpath
)
1184 dir
= mc_opendir (vpath
);
1188 for (d
= mc_readdir (dir
); d
!= NULL
; d
= mc_readdir (dir
))
1189 if (!DIR_IS_DOT (d
->d_name
) && !DIR_IS_DOTDOT (d
->d_name
))
1199 /* --------------------------------------------------------------------------------------------- */
1201 static FileProgressStatus
1202 erase_dir_iff_empty (file_op_context_t
* ctx
, const vfs_path_t
* vpath
, size_t count
)
1204 FileProgressStatus error
= FILE_CONT
;
1207 s
= vfs_path_as_str (vpath
);
1209 file_progress_show_deleting (ctx
, s
, NULL
);
1210 file_progress_show_count (ctx
, count
, ctx
->progress_count
);
1211 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1216 if (check_dir_is_empty (vpath
) == 1) /* not empty or error */
1218 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1220 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1221 if (error
== FILE_SKIPALL
)
1222 ctx
->skip_all
= TRUE
;
1223 if (error
!= FILE_RETRY
)
1233 /* --------------------------------------------------------------------------------------------- */
1234 /* {{{ Panel operate routines */
1237 * Return currently selected entry name or the name of the first marked
1238 * entry if there is one.
1242 panel_get_file (WPanel
* panel
)
1244 if (get_current_type () == view_tree
)
1247 const vfs_path_t
*selected_name
;
1249 tree
= (WTree
*) get_panel_widget (get_current_index ());
1250 selected_name
= tree_selected_name (tree
);
1251 return vfs_path_as_str (selected_name
);
1254 if (panel
->marked
!= 0)
1258 for (i
= 0; i
< panel
->dir
.len
; i
++)
1259 if (panel
->dir
.list
[i
].f
.marked
)
1260 return panel
->dir
.list
[i
].fname
;
1263 return panel
->dir
.list
[panel
->selected
].fname
;
1266 /* --------------------------------------------------------------------------------------------- */
1268 * panel_compute_totals:
1270 * compute the number of files and the number of bytes
1271 * used up by the whole selection, recursing directories
1272 * as required. In addition, it checks to see if it will
1273 * overwrite any files by doing the copy.
1276 static FileProgressStatus
1277 panel_compute_totals (const WPanel
* panel
, dirsize_status_msg_t
* sm
, size_t * ret_count
,
1278 uintmax_t * ret_total
, gboolean compute_symlinks
)
1281 size_t dir_count
= 0;
1283 for (i
= 0; i
< panel
->dir
.len
; i
++)
1287 if (!panel
->dir
.list
[i
].f
.marked
)
1290 s
= &panel
->dir
.list
[i
].st
;
1292 if (S_ISDIR (s
->st_mode
))
1295 FileProgressStatus status
;
1297 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, (char *) NULL
);
1298 status
= compute_dir_size (p
, sm
, &dir_count
, ret_count
, ret_total
, compute_symlinks
);
1301 if (status
!= FILE_CONT
)
1307 *ret_total
+= (uintmax_t) s
->st_size
;
1314 /* --------------------------------------------------------------------------------------------- */
1316 /** Initialize variables for progress bars */
1317 static FileProgressStatus
1318 panel_operate_init_totals (const WPanel
* panel
, const char *source
, file_op_context_t
* ctx
,
1319 filegui_dialog_type_t dialog_type
)
1321 FileProgressStatus status
;
1323 #ifdef ENABLE_BACKGROUND
1324 if (mc_global
.we_are_background
)
1328 if (verbose
&& file_op_compute_totals
)
1330 dirsize_status_msg_t dsm
;
1332 memset (&dsm
, 0, sizeof (dsm
));
1333 dsm
.allow_skip
= TRUE
;
1334 status_msg_init (STATUS_MSG (&dsm
), _("Directory scanning"), 0, dirsize_status_init_cb
,
1335 dirsize_status_update_cb
, dirsize_status_deinit_cb
);
1337 ctx
->progress_count
= 0;
1338 ctx
->progress_bytes
= 0;
1341 status
= panel_compute_totals (panel
, &dsm
, &ctx
->progress_count
, &ctx
->progress_bytes
,
1346 size_t dir_count
= 0;
1348 p
= vfs_path_from_str (source
);
1349 status
= compute_dir_size (p
, &dsm
, &dir_count
, &ctx
->progress_count
,
1350 &ctx
->progress_bytes
, ctx
->follow_links
);
1354 status_msg_deinit (STATUS_MSG (&dsm
));
1356 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1358 if (status
== FILE_SKIP
)
1364 ctx
->progress_count
= panel
->marked
;
1365 ctx
->progress_bytes
= panel
->total
;
1366 ctx
->progress_totals_computed
= FALSE
;
1369 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
1374 /* --------------------------------------------------------------------------------------------- */
1376 * Generate user prompt for panel operation.
1377 * src_stat must be not NULL for single source, and NULL for multiple sources
1381 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1382 const struct stat
*src_stat
)
1385 char *format_string
;
1388 static gboolean i18n_flag
= FALSE
;
1393 for (i
= G_N_ELEMENTS (op_names1
); i
-- != 0;)
1394 op_names1
[i
] = Q_ (op_names1
[i
]);
1397 for (i
= G_N_ELEMENTS (prompt_parts
); i
-- != 0;)
1398 prompt_parts
[i
] = _(prompt_parts
[i
]);
1400 one_format
= _(one_format
);
1401 many_format
= _(many_format
);
1402 #endif /* ENABLE_NLS */
1406 /* Possible prompts:
1408 * "Copy file \"%s\" with source mask:"
1409 * "Copy %d files with source mask:"
1410 * "Copy directory \"%s\" with source mask:"
1411 * "Copy %d directories with source mask:"
1412 * "Copy %d files/directories with source mask:"
1414 * "Move file \"%s\" with source mask:"
1415 * "Move %d files with source mask:"
1416 * "Move directory \"%s\" with source mask:"
1417 * "Move %d directories with source mask:"
1418 * "Move %d files/directories with source mask:"
1420 * "Delete file \"%s\"?"
1421 * "Delete %d files?"
1422 * "Delete directory \"%s\"?"
1423 * "Delete %d directories?"
1424 * "Delete %d files/directories?"
1427 cp
= (src_stat
!= NULL
? one_format
: many_format
);
1429 /* 1. Substitute %o */
1430 format_string
= str_replace_all (cp
, "%o", op_names1
[(int) operation
]);
1432 /* 2. Substitute %n */
1433 cp
= operation
== OP_DELETE
? "\n" : " ";
1435 format_string
= str_replace_all (sp
, "%n", cp
);
1438 /* 3. Substitute %f */
1439 if (src_stat
!= NULL
)
1440 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1441 else if (panel
->marked
== panel
->dirs_marked
)
1442 cp
= prompt_parts
[3];
1444 cp
= panel
->dirs_marked
!= 0 ? prompt_parts
[4] : prompt_parts
[1];
1447 format_string
= str_replace_all (sp
, "%f", cp
);
1450 /* 4. Substitute %m */
1451 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1453 format_string
= str_replace_all (sp
, "%m", cp
);
1456 return format_string
;
1459 /* --------------------------------------------------------------------------------------------- */
1461 #ifdef ENABLE_BACKGROUND
1463 end_bg_process (file_op_context_t
* ctx
, enum OperationMode mode
)
1470 unregister_task_with_pid (pid
);
1471 /* file_op_context_destroy(ctx); */
1477 /* --------------------------------------------------------------------------------------------- */
1478 /*** public functions ****************************************************************************/
1479 /* --------------------------------------------------------------------------------------------- */
1482 copy_file_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
1483 const char *src_path
, const char *dst_path
)
1485 uid_t src_uid
= (uid_t
) (-1);
1486 gid_t src_gid
= (gid_t
) (-1);
1488 int src_desc
, dest_desc
= -1;
1489 mode_t src_mode
= 0; /* The mode of the source file */
1490 struct stat src_stat
, dst_stat
;
1492 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1493 off_t file_size
= -1;
1494 FileProgressStatus return_status
, temp_status
;
1495 struct timeval tv_transfer_start
;
1496 dest_status_t dst_status
= DEST_NONE
;
1498 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1501 /* FIXME: We should not be using global variables! */
1503 return_status
= FILE_RETRY
;
1505 dst_vpath
= vfs_path_from_str (dst_path
);
1506 src_vpath
= vfs_path_from_str (src_path
);
1508 file_progress_show_source (ctx
, src_vpath
);
1509 file_progress_show_target (ctx
, dst_vpath
);
1511 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1513 return_status
= FILE_ABORT
;
1519 while (mc_stat (dst_vpath
, &dst_stat
) == 0)
1521 if (S_ISDIR (dst_stat
.st_mode
))
1524 return_status
= FILE_SKIPALL
;
1527 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1528 if (return_status
== FILE_SKIPALL
)
1529 ctx
->skip_all
= TRUE
;
1530 if (return_status
== FILE_RETRY
)
1540 while ((*ctx
->stat_func
) (src_vpath
, &src_stat
) != 0)
1543 return_status
= FILE_SKIPALL
;
1546 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1547 if (return_status
== FILE_SKIPALL
)
1548 ctx
->skip_all
= TRUE
;
1551 if (return_status
!= FILE_RETRY
)
1557 /* Destination already exists */
1558 if (src_stat
.st_dev
== dst_stat
.st_dev
&& src_stat
.st_ino
== dst_stat
.st_ino
)
1560 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1561 src_path
, dst_path
);
1565 /* Should we replace destination? */
1566 if (tctx
->ask_overwrite
)
1569 return_status
= query_replace (ctx
, dst_path
, &src_stat
, &dst_stat
);
1570 if (return_status
!= FILE_CONT
)
1575 if (!ctx
->do_append
)
1577 /* Check the hardlinks */
1578 if (!ctx
->follow_links
&& src_stat
.st_nlink
> 1
1579 && check_hardlinks (src_vpath
, dst_vpath
, &src_stat
))
1581 /* We have made a hardlink - no more processing is necessary */
1582 return_status
= FILE_CONT
;
1586 if (S_ISLNK (src_stat
.st_mode
))
1588 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1592 if (S_ISCHR (src_stat
.st_mode
) || S_ISBLK (src_stat
.st_mode
) || S_ISFIFO (src_stat
.st_mode
)
1593 || S_ISNAM (src_stat
.st_mode
) || S_ISSOCK (src_stat
.st_mode
))
1595 while (mc_mknod (dst_vpath
, src_stat
.st_mode
& ctx
->umask_kill
, src_stat
.st_rdev
) < 0
1598 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1599 if (return_status
== FILE_RETRY
)
1601 if (return_status
== FILE_SKIPALL
)
1602 ctx
->skip_all
= TRUE
;
1607 while (ctx
->preserve_uidgid
1608 && mc_chown (dst_vpath
, src_stat
.st_uid
, src_stat
.st_gid
) != 0 && !ctx
->skip_all
)
1610 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1611 if (temp_status
== FILE_SKIP
)
1613 if (temp_status
== FILE_SKIPALL
)
1614 ctx
->skip_all
= TRUE
;
1615 if (temp_status
!= FILE_RETRY
)
1617 return_status
= temp_status
;
1622 while (ctx
->preserve
&& mc_chmod (dst_vpath
, src_stat
.st_mode
& ctx
->umask_kill
) != 0
1625 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1626 if (temp_status
== FILE_SKIP
)
1628 if (temp_status
== FILE_SKIPALL
)
1629 ctx
->skip_all
= TRUE
;
1630 if (temp_status
!= FILE_RETRY
)
1632 return_status
= temp_status
;
1637 return_status
= FILE_CONT
;
1642 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1644 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1646 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1647 if (return_status
== FILE_RETRY
)
1649 if (return_status
== FILE_SKIPALL
)
1650 ctx
->skip_all
= TRUE
;
1651 if (return_status
== FILE_SKIP
)
1653 ctx
->do_append
= FALSE
;
1657 if (ctx
->do_reget
!= 0)
1659 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1661 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1663 ctx
->do_append
= FALSE
;
1667 while (mc_fstat (src_desc
, &src_stat
) != 0)
1670 return_status
= FILE_SKIPALL
;
1673 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1674 if (return_status
== FILE_RETRY
)
1676 if (return_status
== FILE_SKIPALL
)
1677 ctx
->skip_all
= TRUE
;
1678 ctx
->do_append
= FALSE
;
1683 src_mode
= src_stat
.st_mode
;
1684 src_uid
= src_stat
.st_uid
;
1685 src_gid
= src_stat
.st_gid
;
1686 utb
.actime
= src_stat
.st_atime
;
1687 utb
.modtime
= src_stat
.st_mtime
;
1688 file_size
= src_stat
.st_size
;
1690 open_flags
= O_WRONLY
;
1694 open_flags
|= O_APPEND
;
1696 open_flags
|= O_CREAT
| O_TRUNC
;
1700 open_flags
|= O_CREAT
| O_EXCL
;
1703 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1705 if (errno
!= EEXIST
)
1708 return_status
= FILE_SKIPALL
;
1711 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1712 if (return_status
== FILE_RETRY
)
1714 if (return_status
== FILE_SKIPALL
)
1715 ctx
->skip_all
= TRUE
;
1716 ctx
->do_append
= FALSE
;
1721 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1723 appending
= ctx
->do_append
;
1724 ctx
->do_append
= FALSE
;
1726 /* Find out the optimal buffer size. */
1727 while (mc_fstat (dest_desc
, &dst_stat
) != 0)
1730 return_status
= FILE_SKIPALL
;
1733 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1734 if (return_status
== FILE_RETRY
)
1736 if (return_status
== FILE_SKIPALL
)
1737 ctx
->skip_all
= TRUE
;
1742 /* try preallocate space; if fail, try copy anyway */
1743 while (vfs_preallocate (dest_desc
, file_size
, appending
? dst_stat
.st_size
: 0) != 0)
1747 /* cannot allocate, start the file copying anyway */
1748 return_status
= FILE_CONT
;
1753 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1755 if (return_status
== FILE_SKIPALL
)
1756 ctx
->skip_all
= TRUE
;
1758 if (ctx
->skip_all
|| return_status
== FILE_SKIP
)
1760 /* skip the space allocation error, start file copying */
1761 return_status
= FILE_CONT
;
1765 if (return_status
== FILE_ABORT
)
1767 mc_close (dest_desc
);
1769 mc_unlink (dst_vpath
);
1770 dst_status
= DEST_NONE
;
1774 /* return_status == FILE_RETRY -- try allocate space again */
1777 ctx
->eta_secs
= 0.0;
1780 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1781 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1783 file_progress_show (ctx
, 1, 1, "", TRUE
);
1784 return_status
= check_progress_buttons (ctx
);
1787 if (return_status
== FILE_CONT
)
1790 off_t n_read_total
= 0;
1791 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1792 int secs
, update_secs
;
1793 const char *stalled_msg
= "";
1794 gboolean is_first_time
= TRUE
;
1796 tv_last_update
= tv_transfer_start
;
1798 bufsize
= io_blksize (dst_stat
);
1799 buf
= g_malloc (bufsize
);
1803 ssize_t n_read
= -1, n_written
;
1806 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0) == 0)
1807 while ((n_read
= mc_read (src_desc
, buf
, bufsize
)) < 0 && !ctx
->skip_all
)
1809 return_status
= file_error (_("Cannot read source file \"%s\"\n%s"), src_path
);
1810 if (return_status
== FILE_RETRY
)
1812 if (return_status
== FILE_SKIPALL
)
1813 ctx
->skip_all
= TRUE
;
1820 gettimeofday (&tv_current
, NULL
);
1826 n_read_total
+= n_read
;
1828 /* Windows NT ftp servers report that files have no
1829 * permissions: -------, so if we happen to have actually
1830 * read something, we should fix the permissions.
1832 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1833 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1834 gettimeofday (&tv_last_input
, NULL
);
1837 while ((n_written
= mc_write (dest_desc
, t
, (size_t) n_read
)) < n_read
)
1839 gboolean write_errno_nospace
;
1843 n_read
-= n_written
;
1848 write_errno_nospace
= (n_written
< 0 && errno
== ENOSPC
);
1851 return_status
= FILE_SKIPALL
;
1854 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1856 if (return_status
== FILE_SKIP
)
1858 if (write_errno_nospace
)
1862 if (return_status
== FILE_SKIPALL
)
1864 ctx
->skip_all
= TRUE
;
1865 if (write_errno_nospace
)
1868 if (return_status
!= FILE_RETRY
)
1873 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1875 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1876 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1878 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1880 copy_file_file_display_progress (tctx
, ctx
,
1882 tv_transfer_start
, file_size
, n_read_total
);
1883 tv_last_update
= tv_current
;
1885 is_first_time
= FALSE
;
1887 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1889 stalled_msg
= _("(stalled)");
1893 gboolean force_update
;
1896 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1898 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1900 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1901 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1904 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1909 return_status
= check_progress_buttons (ctx
);
1911 if (return_status
!= FILE_CONT
)
1918 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1924 rotate_dash (FALSE
);
1925 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1927 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1928 if (temp_status
== FILE_RETRY
)
1930 if (temp_status
== FILE_ABORT
)
1931 return_status
= temp_status
;
1932 if (temp_status
== FILE_SKIPALL
)
1933 ctx
->skip_all
= TRUE
;
1937 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1939 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1940 if (temp_status
== FILE_RETRY
)
1942 if (temp_status
== FILE_SKIPALL
)
1943 ctx
->skip_all
= TRUE
;
1944 return_status
= temp_status
;
1948 if (dst_status
== DEST_SHORT
)
1950 /* Query to remove short file */
1951 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"),
1952 D_ERROR
, 2, _("&Delete"), _("&Keep")) == 0)
1953 mc_unlink (dst_vpath
);
1955 else if (dst_status
== DEST_FULL
)
1957 /* Copy has succeeded */
1958 if (!appending
&& ctx
->preserve_uidgid
)
1960 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1962 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1963 if (temp_status
== FILE_RETRY
)
1965 if (temp_status
== FILE_SKIPALL
)
1967 ctx
->skip_all
= TRUE
;
1968 return_status
= FILE_CONT
;
1970 if (temp_status
== FILE_SKIP
)
1971 return_status
= FILE_CONT
;
1980 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1982 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1983 if (temp_status
== FILE_RETRY
)
1985 if (temp_status
== FILE_SKIPALL
)
1987 ctx
->skip_all
= TRUE
;
1988 return_status
= FILE_CONT
;
1990 if (temp_status
== FILE_SKIP
)
1991 return_status
= FILE_CONT
;
1995 else if (!dst_exists
)
1997 src_mode
= umask (-1);
1999 src_mode
= 0100666 & ~src_mode
;
2000 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
2002 mc_utime (dst_vpath
, &utb
);
2006 if (return_status
== FILE_CONT
)
2007 return_status
= progress_update_one (tctx
, ctx
, file_size
);
2010 vfs_path_free (src_vpath
);
2011 vfs_path_free (dst_vpath
);
2012 return return_status
;
2015 /* --------------------------------------------------------------------------------------------- */
2017 * I think these copy_*_* functions should have a return type.
2018 * anyway, this function *must* have two directories as arguments.
2020 /* FIXME: This function needs to check the return values of the
2024 copy_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
,
2025 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, GSList
* parent_dirs
)
2027 struct dirent
*next
;
2028 struct stat buf
, cbuf
;
2030 FileProgressStatus return_status
= FILE_CONT
;
2032 vfs_path_t
*src_vpath
, *dst_vpath
;
2033 gboolean do_mkdir
= TRUE
;
2035 src_vpath
= vfs_path_from_str (s
);
2036 dst_vpath
= vfs_path_from_str (d
);
2038 /* First get the mode of the source dir */
2041 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
2044 return_status
= FILE_SKIPALL
;
2047 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
2048 if (return_status
== FILE_RETRY
)
2049 goto retry_src_stat
;
2050 if (return_status
== FILE_SKIPALL
)
2051 ctx
->skip_all
= TRUE
;
2056 if (is_in_linklist (dest_dirs
, src_vpath
, &cbuf
))
2058 /* Don't copy a directory we created before (we don't want to copy
2059 infinitely if a directory is copied into itself) */
2060 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2061 return_status
= FILE_CONT
;
2065 /* Hmm, hardlink to directory??? - Norbert */
2066 /* FIXME: In this step we should do something
2067 in case the destination already exist */
2068 /* Check the hardlinks */
2069 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &cbuf
))
2071 /* We have made a hardlink - no more processing is necessary */
2075 if (!S_ISDIR (cbuf
.st_mode
))
2078 return_status
= FILE_SKIPALL
;
2081 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
2082 if (return_status
== FILE_RETRY
)
2083 goto retry_src_stat
;
2084 if (return_status
== FILE_SKIPALL
)
2085 ctx
->skip_all
= TRUE
;
2090 if (is_in_linklist (parent_dirs
, src_vpath
, &cbuf
))
2092 /* we found a cyclic symbolic link */
2093 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
2094 return_status
= FILE_SKIP
;
2098 lp
= g_new0 (struct link
, 1);
2099 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
2100 lp
->ino
= cbuf
.st_ino
;
2101 lp
->dev
= cbuf
.st_dev
;
2102 parent_dirs
= g_slist_prepend (parent_dirs
, lp
);
2105 /* Now, check if the dest dir exists, if not, create it. */
2106 if (mc_stat (dst_vpath
, &buf
) != 0)
2108 /* Here the dir doesn't exist : make it ! */
2109 if (move_over
&& mc_rename (src_vpath
, dst_vpath
) == 0)
2111 return_status
= FILE_CONT
;
2118 * If the destination directory exists, we want to copy the whole
2119 * directory, but we only want this to happen once.
2121 * Escape sequences added to the * to compiler warnings.
2122 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2123 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2125 if (!S_ISDIR (buf
.st_mode
))
2128 return_status
= FILE_SKIPALL
;
2131 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2132 if (return_status
== FILE_SKIPALL
)
2133 ctx
->skip_all
= TRUE
;
2134 if (return_status
== FILE_RETRY
)
2135 goto retry_dst_stat
;
2139 /* Dive into subdir if exists */
2140 if (toplevel
&& ctx
->dive_into_subdirs
)
2145 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), (char *) NULL
);
2146 vfs_path_free (tmp
);
2153 d
= vfs_path_as_str (dst_vpath
);
2157 while (my_mkdir (dst_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2160 return_status
= FILE_SKIPALL
;
2163 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), d
);
2164 if (return_status
== FILE_SKIPALL
)
2165 ctx
->skip_all
= TRUE
;
2167 if (return_status
!= FILE_RETRY
)
2171 lp
= g_new0 (struct link
, 1);
2172 mc_stat (dst_vpath
, &buf
);
2173 lp
->vfs
= vfs_path_get_by_index (dst_vpath
, -1)->class;
2174 lp
->ino
= buf
.st_ino
;
2175 lp
->dev
= buf
.st_dev
;
2176 dest_dirs
= g_slist_prepend (dest_dirs
, lp
);
2179 if (ctx
->preserve_uidgid
)
2181 while (mc_chown (dst_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2184 return_status
= FILE_SKIPALL
;
2187 return_status
= file_error (_("Cannot chown target directory \"%s\"\n%s"), d
);
2188 if (return_status
== FILE_SKIPALL
)
2189 ctx
->skip_all
= TRUE
;
2191 if (return_status
!= FILE_RETRY
)
2196 /* open the source dir for reading */
2197 reading
= mc_opendir (src_vpath
);
2198 if (reading
== NULL
)
2201 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2204 vfs_path_t
*tmp_vpath
;
2207 * Now, we don't want '.' and '..' to be created / copied at any time
2209 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
2212 /* get the filename and add it to the src directory */
2213 path
= mc_build_filename (s
, next
->d_name
, (char *) NULL
);
2214 tmp_vpath
= vfs_path_from_str (path
);
2216 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2217 if (S_ISDIR (buf
.st_mode
))
2221 mdpath
= mc_build_filename (d
, next
->d_name
, (char *) NULL
);
2223 * From here, we just intend to recursively copy subdirs, not
2224 * the double functionality of copying different when the target
2225 * dir already exists. So, we give the recursive call the flag 0
2226 * meaning no toplevel.
2229 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2236 dest_file
= mc_build_filename (d
, x_basename (path
), (char *) NULL
);
2237 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2243 if (do_delete
&& return_status
== FILE_CONT
)
2245 if (ctx
->erase_at_end
)
2247 lp
= g_new0 (struct link
, 1);
2248 lp
->src_vpath
= tmp_vpath
;
2249 lp
->st_mode
= buf
.st_mode
;
2250 erase_list
= g_slist_append (erase_list
, lp
);
2253 else if (S_ISDIR (buf
.st_mode
))
2254 return_status
= erase_dir_iff_empty (ctx
, tmp_vpath
, tctx
->progress_count
);
2256 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
2258 vfs_path_free (tmp_vpath
);
2260 mc_closedir (reading
);
2266 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2267 utb
.actime
= cbuf
.st_atime
;
2268 utb
.modtime
= cbuf
.st_mtime
;
2269 mc_utime (dst_vpath
, &utb
);
2273 cbuf
.st_mode
= umask (-1);
2274 umask (cbuf
.st_mode
);
2275 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2276 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2280 free_link (parent_dirs
->data
);
2281 g_slist_free_1 (parent_dirs
);
2283 vfs_path_free (src_vpath
);
2284 vfs_path_free (dst_vpath
);
2285 return return_status
;
2290 /* --------------------------------------------------------------------------------------------- */
2291 /* {{{ Move routines */
2294 move_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
)
2296 struct stat sbuf
, dbuf
;
2297 FileProgressStatus return_status
= FILE_CONT
;
2298 gboolean move_over
= FALSE
;
2300 vfs_path_t
*src_vpath
, *dst_vpath
;
2302 src_vpath
= vfs_path_from_str (s
);
2303 dst_vpath
= vfs_path_from_str (d
);
2305 file_progress_show_source (ctx
, src_vpath
);
2306 file_progress_show_target (ctx
, dst_vpath
);
2308 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2310 return_status
= FILE_ABORT
;
2316 mc_stat (src_vpath
, &sbuf
);
2318 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2319 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2321 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2326 ; /* destination doesn't exist */
2327 else if (!ctx
->dive_into_subdirs
)
2334 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), (char *) NULL
);
2335 vfs_path_free (tmp
);
2338 d
= vfs_path_as_str (dst_vpath
);
2340 /* Check if the user inputted an existing dir */
2342 if (mc_stat (dst_vpath
, &dbuf
) == 0)
2346 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, TRUE
, TRUE
, NULL
);
2348 if (return_status
!= FILE_CONT
)
2352 else if (ctx
->skip_all
)
2353 return_status
= FILE_SKIPALL
;
2356 if (S_ISDIR (dbuf
.st_mode
))
2357 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), d
);
2359 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), d
);
2360 if (return_status
== FILE_SKIPALL
)
2361 ctx
->skip_all
= TRUE
;
2362 if (return_status
== FILE_RETRY
)
2363 goto retry_dst_stat
;
2370 if (mc_rename (src_vpath
, dst_vpath
) == 0)
2372 return_status
= FILE_CONT
;
2380 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2381 if (return_status
== FILE_SKIPALL
)
2382 ctx
->skip_all
= TRUE
;
2383 if (return_status
== FILE_RETRY
)
2388 /* Failed because of filesystem boundary -> copy dir instead */
2389 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, FALSE
, TRUE
, NULL
);
2391 if (return_status
!= FILE_CONT
)
2394 file_progress_show_source (ctx
, NULL
);
2395 file_progress_show_target (ctx
, NULL
);
2396 file_progress_show (ctx
, 0, 0, "", FALSE
);
2398 return_status
= check_progress_buttons (ctx
);
2399 if (return_status
!= FILE_CONT
)
2403 if (ctx
->erase_at_end
)
2405 /* Reset progress count before delete to avoid counting files twice */
2406 tctx
->progress_count
= tctx
->prev_progress_count
;
2408 while (erase_list
!= NULL
&& return_status
!= FILE_ABORT
)
2410 struct link
*lp
= (struct link
*) erase_list
->data
;
2412 if (S_ISDIR (lp
->st_mode
))
2413 return_status
= erase_dir_iff_empty (ctx
, lp
->src_vpath
, tctx
->progress_count
);
2415 return_status
= erase_file (tctx
, ctx
, lp
->src_vpath
);
2417 erase_list
= g_slist_remove (erase_list
, lp
);
2421 /* Save progress counter before move next directory */
2422 tctx
->prev_progress_count
= tctx
->progress_count
;
2424 erase_dir_iff_empty (ctx
, src_vpath
, tctx
->progress_count
);
2427 erase_list
= free_linklist (erase_list
);
2429 vfs_path_free (src_vpath
);
2430 vfs_path_free (dst_vpath
);
2431 return return_status
;
2436 /* --------------------------------------------------------------------------------------------- */
2437 /* {{{ Erase routines */
2440 erase_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* s_vpath
)
2442 FileProgressStatus error
;
2444 file_progress_show_deleting (ctx
, vfs_path_as_str (s_vpath
), NULL
);
2445 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2446 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2451 /* The old way to detect a non empty directory was:
2452 error = my_rmdir (s);
2453 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2454 For the linux user space nfs server (nfs-server-2.2beta29-2)
2455 we would have to check also for EIO. I hope the new way is
2456 fool proof. (Norbert)
2458 error
= check_dir_is_empty (s_vpath
);
2461 error
= query_recursive (ctx
, vfs_path_as_str (s_vpath
));
2462 if (error
== FILE_CONT
)
2463 error
= recursive_erase (tctx
, ctx
, s_vpath
);
2467 while (my_rmdir (vfs_path_as_str (s_vpath
)) == -1 && !ctx
->skip_all
)
2469 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), vfs_path_as_str (s_vpath
));
2470 if (error
!= FILE_RETRY
)
2479 /* --------------------------------------------------------------------------------------------- */
2480 /* {{{ Panel operate routines */
2483 dirsize_status_init_cb (status_msg_t
* sm
)
2485 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
2486 Widget
*wd
= WIDGET (sm
->dlg
);
2488 const char *b1_name
= N_("&Abort");
2489 const char *b2_name
= N_("&Skip");
2490 int b_width
, ui_width
;
2493 b1_name
= _(b1_name
);
2494 b2_name
= _(b2_name
);
2497 b_width
= str_term_width1 (b1_name
) + 4;
2498 if (dsm
->allow_skip
)
2499 b_width
+= str_term_width1 (b2_name
) + 4 + 1;
2501 ui_width
= max (COLS
/ 2, b_width
+ 6);
2502 dsm
->dirname
= label_new (2, 3, "");
2503 add_widget (sm
->dlg
, dsm
->dirname
);
2504 dsm
->count_size
= label_new (3, 3, "");
2505 add_widget (sm
->dlg
, dsm
->count_size
);
2506 add_widget (sm
->dlg
, hline_new (4, -1, -1));
2508 dsm
->abort_button
= WIDGET (button_new (5, 3, FILE_ABORT
, NORMAL_BUTTON
, b1_name
, NULL
));
2509 add_widget (sm
->dlg
, dsm
->abort_button
);
2510 if (dsm
->allow_skip
)
2512 dsm
->skip_button
= WIDGET (button_new (5, 3, FILE_SKIP
, NORMAL_BUTTON
, b2_name
, NULL
));
2513 add_widget (sm
->dlg
, dsm
->skip_button
);
2514 dlg_select_widget (dsm
->skip_button
);
2517 widget_set_size (wd
, wd
->y
, wd
->x
, 8, ui_width
);
2518 dirsize_status_locate_buttons (dsm
);
2521 /* --------------------------------------------------------------------------------------------- */
2524 dirsize_status_update_cb (status_msg_t
* sm
)
2526 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
2527 Widget
*wd
= WIDGET (sm
->dlg
);
2529 /* update second (longer label) */
2530 label_set_textv (dsm
->count_size
, _("Directories: %zd, total size: %s"),
2531 dsm
->dir_count
, size_trunc_sep (dsm
->total_size
, panels_options
.kilobyte_si
));
2533 /* enlarge dialog if required */
2534 if (WIDGET (dsm
->count_size
)->cols
+ 6 > wd
->cols
)
2536 dlg_set_size (sm
->dlg
, wd
->lines
, WIDGET (dsm
->count_size
)->cols
+ 6);
2537 dirsize_status_locate_buttons (dsm
);
2538 dlg_redraw (sm
->dlg
);
2541 /* adjust first label */
2542 label_set_text (dsm
->dirname
, str_trunc (vfs_path_as_str (dsm
->dirname_vpath
), wd
->cols
- 6));
2544 switch (status_msg_common_update (sm
))
2556 /* --------------------------------------------------------------------------------------------- */
2559 dirsize_status_deinit_cb (status_msg_t
* sm
)
2563 /* schedule to update passive panel */
2564 if (get_other_type () == view_listing
)
2565 other_panel
->dirty
= 1;
2568 /* --------------------------------------------------------------------------------------------- */
2572 * Computes the number of bytes used by the files in a directory
2576 compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* sm
,
2577 size_t * ret_dir_count
, size_t * ret_marked_count
, uintmax_t * ret_total
,
2578 gboolean compute_symlinks
)
2580 return do_compute_dir_size (dirname_vpath
, sm
, ret_dir_count
, ret_marked_count
, ret_total
,
2584 /* --------------------------------------------------------------------------------------------- */
2588 * Performs one of the operations on the selection on the source_panel
2589 * (copy, delete, move).
2591 * Returns TRUE if did change the directory
2592 * structure, Returns FALSE if user aborted
2594 * force_single forces operation on the current entry and affects
2595 * default destination. Current filename is used as default.
2599 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2601 WPanel
*panel
= PANEL (source_panel
);
2602 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2603 || (get_current_type () == view_tree
);
2605 const char *source
= NULL
;
2606 #ifdef WITH_FULL_PATHS
2607 vfs_path_t
*source_with_vpath
= NULL
;
2608 #endif /* WITH_FULL_PATHS */
2610 vfs_path_t
*dest_vpath
= NULL
;
2612 char *save_cwd
= NULL
, *save_dest
= NULL
;
2613 struct stat src_stat
;
2614 gboolean ret_val
= TRUE
;
2616 FileProgressStatus value
;
2617 file_op_context_t
*ctx
;
2618 file_op_total_context_t
*tctx
;
2619 vfs_path_t
*tmp_vpath
;
2620 filegui_dialog_type_t dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2622 gboolean do_bg
= FALSE
; /* do background operation? */
2624 static gboolean i18n_flag
= FALSE
;
2627 for (i
= G_N_ELEMENTS (op_names
); i
-- != 0;)
2628 op_names
[i
] = Q_ (op_names
[i
]);
2632 linklist
= free_linklist (linklist
);
2633 dest_dirs
= free_linklist (dest_dirs
);
2640 source
= selection (panel
)->fname
;
2642 source
= panel_get_file (panel
);
2644 ok
= !DIR_IS_DOTDOT (source
);
2647 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2650 vfs_path_t
*source_vpath
;
2652 source_vpath
= vfs_path_from_str (source
);
2654 /* Update stat to get actual info */
2655 ok
= mc_lstat (source_vpath
, &src_stat
) == 0;
2658 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2659 path_trunc (source
, 30), unix_error_string (errno
));
2661 /* Directory was changed outside MC. Reload it forced */
2662 if (!panel
->is_panelized
)
2664 panel_update_flags_t flags
= UP_RELOAD
;
2666 /* don't update panelized panel */
2667 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2668 flags
|= UP_ONLY_CURRENT
;
2670 update_panels (flags
, UP_KEEPSEL
);
2674 vfs_path_free (source_vpath
);
2681 ctx
= file_op_context_new (operation
);
2683 /* Show confirmation dialog */
2684 if (operation
!= OP_DELETE
)
2686 const char *tmp_dest_dir
;
2690 /* Forced single operations default to the original name */
2692 tmp_dest_dir
= source
;
2693 else if (get_other_type () == view_listing
)
2694 tmp_dest_dir
= vfs_path_as_str (other_panel
->cwd_vpath
);
2696 tmp_dest_dir
= vfs_path_as_str (panel
->cwd_vpath
);
2698 * Add trailing backslash only when do non-local ops.
2699 * It saves user from occasional file renames (when destination
2702 if (!force_single
&& tmp_dest_dir
!= NULL
&& tmp_dest_dir
[0] != '\0'
2703 && !IS_PATH_SEP (tmp_dest_dir
[strlen (tmp_dest_dir
) - 1]))
2705 /* add trailing separator */
2706 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2711 dest_dir
= g_strdup (tmp_dest_dir
);
2713 if (dest_dir
== NULL
)
2719 /* Generate confirmation prompt */
2721 panel_operate_generate_prompt (panel
, operation
, source
!= NULL
? &src_stat
: NULL
);
2724 file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2725 source
!= NULL
? source
: (const void *) &panel
->marked
, dest_dir
,
2731 if (dest
== NULL
|| dest
[0] == '\0')
2737 dest_vpath
= vfs_path_from_str (dest
);
2739 else if (confirm_delete
)
2742 char fmd_buf
[BUF_MEDIUM
];
2744 /* Generate confirmation prompt */
2746 panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
? &src_stat
: NULL
);
2749 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2752 const int fmd_xlen
= 64;
2753 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2754 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2762 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2771 tctx
= file_op_total_context_new ();
2772 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2774 #ifdef ENABLE_BACKGROUND
2775 /* Did the user select to do a background operation? */
2780 v
= do_background (ctx
,
2781 g_strconcat (op_names
[operation
], ": ",
2782 vfs_path_as_str (panel
->cwd_vpath
), (char *) NULL
));
2784 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2786 /* If we are the parent */
2789 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2791 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2792 vfs_path_free (dest_vpath
);
2794 /* file_op_context_destroy (ctx); */
2799 #endif /* ENABLE_BACKGROUND */
2801 if (operation
== OP_DELETE
)
2802 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2803 else if (single_entry
&& S_ISDIR (selection (panel
)->st
.st_mode
))
2804 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2805 else if (single_entry
|| force_single
)
2806 dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2808 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2811 /* Initialize things */
2812 /* We do not want to trash cache every time file is
2813 created/touched. However, this will make our cache contain
2816 && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2817 save_dest
= g_strdup (dest
);
2819 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2820 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2821 save_cwd
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
2823 /* Now, let's do the job */
2825 /* This code is only called by the tree and panel code */
2828 /* We now have ETA in all cases */
2830 /* One file: FIXME mc_chdir will take user out of any vfs */
2831 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2836 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2837 chdir_retcode
= mc_chdir (vpath
);
2838 vfs_path_free (vpath
);
2839 if (chdir_retcode
< 0)
2846 /* The source and src_stat variables have been initialized before */
2847 #ifdef WITH_FULL_PATHS
2848 if (g_path_is_absolute (source
))
2849 source_with_vpath
= vfs_path_from_str (source
);
2851 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2852 #endif /* WITH_FULL_PATHS */
2853 if (panel_operate_init_totals (panel
, vfs_path_as_str (source_with_vpath
), ctx
, dialog_type
)
2856 if (operation
== OP_DELETE
)
2858 if (S_ISDIR (src_stat
.st_mode
))
2859 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2861 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2865 temp
= transform_source (ctx
, source_with_vpath
);
2867 value
= transform_error
;
2870 char *repl_dest
, *temp2
;
2872 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2873 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2875 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2880 temp2
= mc_build_filename (repl_dest
, temp
, (char *) NULL
);
2884 vfs_path_free (dest_vpath
);
2886 dest_vpath
= vfs_path_from_str (dest
);
2891 /* we use file_mask_op_follow_links only with OP_COPY */
2892 ctx
->stat_func (source_with_vpath
, &src_stat
);
2894 if (S_ISDIR (src_stat
.st_mode
))
2896 copy_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2897 dest
, TRUE
, FALSE
, FALSE
, NULL
);
2900 copy_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2905 if (S_ISDIR (src_stat
.st_mode
))
2907 move_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
), dest
);
2910 move_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2915 /* Unknown file operation */
2919 } /* Copy or move operation */
2921 if ((value
== FILE_CONT
) && !force_single
)
2922 unmark_files (panel
);
2929 /* Check destination for copy or move operation */
2930 while (operation
!= OP_DELETE
)
2933 struct stat dst_stat
;
2935 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2937 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2941 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2945 if (panel_operate_init_totals (panel
, NULL
, ctx
, dialog_type
) == FILE_CONT
)
2947 /* Loop for every file, perform the actual copy operation */
2948 for (i
= 0; i
< panel
->dir
.len
; i
++)
2950 const char *source2
;
2952 if (!panel
->dir
.list
[i
].f
.marked
)
2953 continue; /* Skip the unmarked ones */
2955 source2
= panel
->dir
.list
[i
].fname
;
2956 src_stat
= panel
->dir
.list
[i
].st
;
2958 #ifdef WITH_FULL_PATHS
2959 vfs_path_free (source_with_vpath
);
2960 if (g_path_is_absolute (source2
))
2961 source_with_vpath
= vfs_path_from_str (source2
);
2964 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2965 #endif /* WITH_FULL_PATHS */
2967 if (operation
== OP_DELETE
)
2969 if (S_ISDIR (src_stat
.st_mode
))
2970 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2972 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2976 temp
= transform_source (ctx
, source_with_vpath
);
2978 value
= transform_error
;
2981 char *temp2
, *repl_dest
, *source_with_path_str
;
2983 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2984 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2986 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2991 temp2
= mc_build_filename (repl_dest
, temp
, (char *) NULL
);
2994 source_with_path_str
=
2995 strutils_shell_unescape (vfs_path_as_str (source_with_vpath
));
2996 temp
= strutils_shell_unescape (temp2
);
3002 /* we use file_mask_op_follow_links only with OP_COPY */
3006 vpath
= vfs_path_from_str (source_with_path_str
);
3007 ctx
->stat_func (vpath
, &src_stat
);
3008 vfs_path_free (vpath
);
3010 if (S_ISDIR (src_stat
.st_mode
))
3011 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp
,
3012 TRUE
, FALSE
, FALSE
, NULL
);
3014 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp
);
3015 dest_dirs
= free_linklist (dest_dirs
);
3019 if (S_ISDIR (src_stat
.st_mode
))
3020 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp
);
3022 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp
);
3026 /* Unknown file operation */
3030 g_free (source_with_path_str
);
3033 } /* Copy or move operation */
3035 if (value
== FILE_ABORT
)
3038 if (value
== FILE_CONT
)
3039 do_file_mark (panel
, i
, 0);
3041 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3043 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3044 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3047 if (operation
!= OP_DELETE
)
3048 file_progress_show (ctx
, 0, 0, "", FALSE
);
3050 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3054 } /* Loop for every file */
3056 } /* Many entries */
3060 if (save_cwd
!= NULL
)
3062 tmp_vpath
= vfs_path_from_str (save_cwd
);
3063 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3064 vfs_path_free (tmp_vpath
);
3068 if (save_dest
!= NULL
)
3070 tmp_vpath
= vfs_path_from_str (save_dest
);
3071 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3072 vfs_path_free (tmp_vpath
);
3076 linklist
= free_linklist (linklist
);
3077 dest_dirs
= free_linklist (dest_dirs
);
3078 #ifdef WITH_FULL_PATHS
3079 vfs_path_free (source_with_vpath
);
3080 #endif /* WITH_FULL_PATHS */
3082 vfs_path_free (dest_vpath
);
3083 MC_PTR_FREE (ctx
->dest_mask
);
3085 #ifdef ENABLE_BACKGROUND
3086 /* Let our parent know we are saying bye bye */
3087 if (mc_global
.we_are_background
)
3089 int cur_pid
= getpid ();
3090 /* Send pid to parent with child context, it is fork and
3091 don't modify real parent ctx */
3093 parent_call ((void *) end_bg_process
, ctx
, 0);
3096 my_exit (EXIT_SUCCESS
);
3098 #endif /* ENABLE_BACKGROUND */
3100 file_op_total_context_destroy (tctx
);
3102 file_op_context_destroy (ctx
);
3109 /* --------------------------------------------------------------------------------------------- */
3110 /* {{{ Query/status report routines */
3111 /** Report error with one file */
3113 file_error (const char *format
, const char *file
)
3115 char buf
[BUF_MEDIUM
];
3117 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3119 return do_file_error (buf
);
3122 /* --------------------------------------------------------------------------------------------- */
3125 Cause emacs to enter folding mode for this file: