4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011, 2012, 2013
6 The Free Software Foundation, Inc.
9 Janne Kukonlehto, 1994, 1995
10 Fred Leeflang, 1994, 1995
11 Miguel de Icaza, 1994, 1995, 1996
12 Jakub Jelinek, 1995, 1996
15 Andrew Borodin <aborodin@vmail.ru>, 2011, 2012, 2013
17 The copy code was based in GNU's cp, and was written by:
18 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
20 The move code was based in GNU's mv, and was written by:
21 Mike Parker and David MacKenzie.
23 Janne Kukonlehto added much error recovery to them for being used
24 in an interactive program.
26 This file is part of the Midnight Commander.
28 The Midnight Commander is free software: you can redistribute it
29 and/or modify it under the terms of the GNU General Public License as
30 published by the Free Software Foundation, either version 3 of the License,
31 or (at your option) any later version.
33 The Midnight Commander is distributed in the hope that it will be useful,
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 GNU General Public License for more details.
38 You should have received a copy of the GNU General Public License
39 along with this program. If not, see <http://www.gnu.org/licenses/>.
43 * Please note that all dialogs used here must be safe for background
47 /** \file src/filemanager/file.c
48 * \brief Source: file management
51 /* {{{ Include files */
60 #include <sys/types.h>
65 #include "lib/global.h"
66 #include "lib/tty/tty.h"
67 #include "lib/tty/key.h"
68 #include "lib/search.h"
69 #include "lib/strescape.h"
70 #include "lib/strutil.h"
72 #include "lib/vfs/vfs.h"
73 #include "lib/widget.h"
75 #include "src/setup.h"
76 #ifdef ENABLE_BACKGROUND
77 #include "src/background.h" /* do_background() */
80 /* Needed for current_panel, other_panel and WTree */
85 #include "midnight.h" /* current_panel */
86 #include "layout.h" /* rotate_dash() */
92 /*** global variables ****************************************************************************/
94 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
95 const char *op_names
[3] = {
96 N_("DialogTitle|Copy"),
97 N_("DialogTitle|Move"),
98 N_("DialogTitle|Delete")
101 /*** file scope macro definitions ****************************************************************/
103 /* Hack: the vfs code should not rely on this */
104 #define WITH_FULL_PATHS 1
106 #define FILEOP_UPDATE_INTERVAL 2
107 #define FILEOP_STALLING_INTERVAL 4
109 /*** file scope type declarations ****************************************************************/
111 /* This is a hard link cache */
114 const struct vfs_class
*vfs
;
119 vfs_path_t
*src_vpath
;
120 vfs_path_t
*dst_vpath
;
123 /* Status of the destination file */
126 DEST_NONE
= 0, /* Not created */
127 DEST_SHORT
= 1, /* Created, not fully copied */
128 DEST_FULL
= 2 /* Created, fully copied */
132 * This array introduced to avoid translation problems. The former (op_names)
133 * is assumed to be nouns, suitable in dialog box titles; this one should
134 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
135 * (I don't use spaces around the words, because someday they could be
136 * dropped, when widgets get smarter)
139 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
140 static const char *op_names1
[] = {
141 N_("FileOperation|Copy"),
142 N_("FileOperation|Move"),
143 N_("FileOperation|Delete")
147 * These are formats for building a prompt. Parts encoded as follows:
148 * %o - operation from op_names1
149 * %f - file/files or files/directories, as appropriate
150 * %m - "with source mask" or question mark for delete
151 * %s - source name (truncated)
152 * %d - number of marked files
153 * %e - "to:" or question mark for delete
155 * xgettext:no-c-format */
156 static const char *one_format
= N_("%o %f \"%s\"%m");
157 /* xgettext:no-c-format */
158 static const char *many_format
= N_("%o %d %f%m");
160 static const char *prompt_parts
[] = {
165 N_("files/directories"),
166 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
167 N_(" with source mask:"),
171 static const char *question_format
= N_("%s?");
173 /*** file scope variables ************************************************************************/
175 /* the hard link cache */
176 static GSList
*linklist
= NULL
;
178 /* the files-to-be-erased list */
179 static GSList
*erase_list
= NULL
;
182 * In copy_dir_dir we use two additional single linked lists: The first -
183 * variable name 'parent_dirs' - holds information about already copied
184 * directories and is used to detect cyclic symbolic links.
185 * The second ('dest_dirs' below) holds information about just created
186 * target directories and is used to detect when an directory is copied
187 * into itself (we don't want to copy infinitly).
188 * Both lists don't use the linkcount and name structure members of struct
191 static GSList
*dest_dirs
= NULL
;
193 static FileProgressStatus transform_error
= FILE_CONT
;
195 /*** file scope functions ************************************************************************/
196 /* --------------------------------------------------------------------------------------------- */
199 transform_source (file_op_context_t
* ctx
, const vfs_path_t
* source_vpath
)
204 s
= g_strdup (vfs_path_as_str (source_vpath
));
206 /* We remove \n from the filename since regex routines would use \n as an anchor */
207 /* this is just to be allowed to maniupulate file names with \n on it */
208 for (q
= s
; *q
!= '\0'; q
++)
212 fnsource
= (char *) x_basename (s
);
214 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
216 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
217 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
219 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
221 transform_error
= FILE_ABORT
;
227 transform_error
= FILE_SKIP
;
234 /* --------------------------------------------------------------------------------------------- */
237 free_link (void *data
)
239 struct link
*lp
= (struct link
*) data
;
241 vfs_path_free (lp
->src_vpath
);
242 vfs_path_free (lp
->dst_vpath
);
246 /* --------------------------------------------------------------------------------------------- */
249 free_linklist (GSList
* lp
)
251 g_slist_foreach (lp
, (GFunc
) free_link
, NULL
);
257 /* --------------------------------------------------------------------------------------------- */
260 is_in_linklist (const GSList
* lp
, const vfs_path_t
* vpath
, const struct stat
*sb
)
262 const struct vfs_class
*class;
263 ino_t ino
= sb
->st_ino
;
264 dev_t dev
= sb
->st_dev
;
266 class = vfs_path_get_last_path_vfs (vpath
);
268 for (; lp
!= NULL
; lp
= g_slist_next (lp
))
270 const struct link
*lnk
= (const struct link
*) lp
->data
;
272 if (lnk
->vfs
== class && lnk
->ino
== ino
&& lnk
->dev
== dev
)
278 /* --------------------------------------------------------------------------------------------- */
280 * Check and made hardlink
282 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
283 * and a hardlink was successfully made
287 check_hardlinks (const vfs_path_t
* src_vpath
, const vfs_path_t
* dst_vpath
, struct stat
*pstat
)
292 const struct vfs_class
*my_vfs
;
293 ino_t ino
= pstat
->st_ino
;
294 dev_t dev
= pstat
->st_dev
;
295 struct stat link_stat
;
297 if ((vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
300 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
302 for (lp
= linklist
; lp
!= NULL
; lp
= g_slist_next (lp
))
304 lnk
= (struct link
*) lp
->data
;
306 if (lnk
->vfs
== my_vfs
&& lnk
->ino
== ino
&& lnk
->dev
== dev
)
308 const struct vfs_class
*lp_name_class
;
311 lp_name_class
= vfs_path_get_last_path_vfs (lnk
->src_vpath
);
312 stat_result
= mc_stat (lnk
->src_vpath
, &link_stat
);
314 if (stat_result
== 0 && link_stat
.st_ino
== ino
315 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
317 const struct vfs_class
*p_class
, *dst_name_class
;
319 dst_name_class
= vfs_path_get_last_path_vfs (dst_vpath
);
320 p_class
= vfs_path_get_last_path_vfs (lnk
->dst_vpath
);
322 if (dst_name_class
== p_class
&&
323 mc_stat (lnk
->dst_vpath
, &link_stat
) == 0 &&
324 mc_link (lnk
->dst_vpath
, dst_vpath
) == 0)
328 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
333 lnk
= g_new0 (struct link
, 1);
339 lnk
->src_vpath
= vfs_path_clone (src_vpath
);
340 lnk
->dst_vpath
= vfs_path_clone (dst_vpath
);
341 linklist
= g_slist_prepend (linklist
, lnk
);
347 /* --------------------------------------------------------------------------------------------- */
349 * Duplicate the contents of the symbolic link src_path in dst_path.
350 * Try to make a stable symlink if the option "stable symlink" was
351 * set in the file mask dialog.
352 * If dst_path is an existing symlink it will be deleted silently
353 * (upper levels take already care of existing files at dst_path).
356 static FileProgressStatus
357 make_symlink (file_op_context_t
* ctx
, const char *src_path
, const char *dst_path
)
359 char link_target
[MC_MAXPATHLEN
];
361 FileProgressStatus return_status
;
363 vfs_path_t
*src_vpath
;
364 vfs_path_t
*dst_vpath
;
365 gboolean dst_is_symlink
;
366 vfs_path_t
*link_target_vpath
= NULL
;
368 src_vpath
= vfs_path_from_str (src_path
);
369 dst_vpath
= vfs_path_from_str (dst_path
);
370 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
373 len
= mc_readlink (src_vpath
, link_target
, MC_MAXPATHLEN
- 1);
377 return_status
= FILE_SKIPALL
;
380 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
381 if (return_status
== FILE_SKIPALL
)
382 ctx
->skip_all
= TRUE
;
383 if (return_status
== FILE_RETRY
)
384 goto retry_src_readlink
;
388 link_target
[len
] = 0;
390 if (ctx
->stable_symlinks
)
393 if (!vfs_file_is_local (src_vpath
) || !vfs_file_is_local (dst_vpath
))
395 message (D_ERROR
, MSG_ERROR
,
396 _("Cannot make stable symlinks across"
397 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
398 ctx
->stable_symlinks
= FALSE
;
402 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
404 const char *r
= strrchr (src_path
, PATH_SEP
);
411 p
= g_strndup (src_path
, r
- src_path
+ 1);
412 if (g_path_is_absolute (dst_path
))
413 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
415 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
417 if (vfs_path_tokens_count (q
) > 1)
420 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
422 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
423 s
= g_strconcat (p
, link_target
, (char *) NULL
);
425 g_strlcpy (link_target
, s
, sizeof (link_target
));
427 tmp_vpath2
= vfs_path_from_str (link_target
);
428 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
429 vfs_path_free (tmp_vpath1
);
430 vfs_path_free (tmp_vpath2
);
433 g_strlcpy (link_target
, s
, sizeof (link_target
));
442 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
445 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
448 return_status
= FILE_CONT
;
452 * if dst_exists, it is obvious that this had failed.
453 * We can delete the old symlink and try again...
457 if (mc_unlink (dst_vpath
) == 0)
458 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
461 return_status
= FILE_CONT
;
466 return_status
= FILE_SKIPALL
;
469 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
470 if (return_status
== FILE_SKIPALL
)
471 ctx
->skip_all
= TRUE
;
472 if (return_status
== FILE_RETRY
)
473 goto retry_dst_symlink
;
477 vfs_path_free (src_vpath
);
478 vfs_path_free (dst_vpath
);
479 vfs_path_free (link_target_vpath
);
480 return return_status
;
483 /* --------------------------------------------------------------------------------------------- */
485 * do_compute_dir_size:
487 * Computes the number of bytes used by the files in a directory
490 static FileProgressStatus
491 do_compute_dir_size (const vfs_path_t
* dirname_vpath
, void *ui
,
492 compute_dir_size_callback cback
, size_t * dir_count
, size_t * ret_marked
,
493 uintmax_t * ret_total
, gboolean compute_symlinks
)
495 static unsigned short int update_ui_count
= 0;
500 struct dirent
*dirent
;
501 FileProgressStatus ret
= FILE_CONT
;
503 if (!compute_symlinks
)
505 res
= mc_lstat (dirname_vpath
, &s
);
509 /* don't scan symlink to directory */
510 if (S_ISLNK (s
.st_mode
))
513 *ret_total
+= (uintmax_t) s
.st_size
;
520 dir
= mc_opendir (dirname_vpath
);
524 while (ret
== FILE_CONT
&& (dirent
= mc_readdir (dir
)) != NULL
)
526 vfs_path_t
*tmp_vpath
;
528 if (DIR_IS_DOT (dirent
->d_name
) || DIR_IS_DOTDOT (dirent
->d_name
))
531 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, NULL
);
533 res
= mc_lstat (tmp_vpath
, &s
);
536 if (S_ISDIR (s
.st_mode
))
539 do_compute_dir_size (tmp_vpath
, ui
, cback
, dir_count
, ret_marked
, ret_total
,
541 if (ret
== FILE_CONT
)
543 (cback
== NULL
) ? FILE_CONT
: cback (ui
, tmp_vpath
, *dir_count
, *ret_total
);
548 *ret_total
+= (uintmax_t) s
.st_size
;
551 if ((update_ui_count
& 31) == 0)
553 (cback
== NULL
) ? FILE_CONT
: cback (ui
, dirname_vpath
, *dir_count
,
558 vfs_path_free (tmp_vpath
);
565 /* --------------------------------------------------------------------------------------------- */
567 static FileProgressStatus
568 progress_update_one (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, off_t add
)
570 struct timeval tv_current
;
571 static struct timeval tv_start
= { };
573 tctx
->progress_count
++;
574 tctx
->progress_bytes
+= (uintmax_t) add
;
576 if (tv_start
.tv_sec
== 0)
578 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
580 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
581 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
583 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
585 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
586 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
588 tv_start
.tv_sec
= tv_current
.tv_sec
;
591 return check_progress_buttons (ctx
);
594 /* --------------------------------------------------------------------------------------------- */
596 static FileProgressStatus
597 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
601 const char *head_msg
;
603 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
605 msg
= g_strdup_printf (fmt
, a
, b
);
606 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
610 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
613 /* --------------------------------------------------------------------------------------------- */
615 static FileProgressStatus
616 warn_same_file (const char *fmt
, const char *a
, const char *b
)
618 #ifdef ENABLE_BACKGROUND
623 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
624 const char *a
, const char *b
);
628 pntr
.f
= real_warn_same_file
;
630 if (mc_global
.we_are_background
)
631 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
633 return real_warn_same_file (Foreground
, fmt
, a
, b
);
636 /* --------------------------------------------------------------------------------------------- */
637 /* {{{ Query/status report routines */
639 static FileProgressStatus
640 real_do_file_error (enum OperationMode mode
, const char *error
)
645 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
647 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
669 /* --------------------------------------------------------------------------------------------- */
671 static FileProgressStatus
672 real_query_recursive (file_op_context_t
* ctx
, enum OperationMode mode
, const char *s
)
675 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
681 msg
= mode
== Foreground
682 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
683 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
684 text
= g_strdup_printf (msg
, path_trunc (s
, 30));
689 ctx
->recursive_result
=
690 (FileCopyMode
) query_dialog (op_names
[OP_DELETE
], text
, D_ERROR
, 5,
691 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
694 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
698 switch (ctx
->recursive_result
)
701 case RECURSIVE_ALWAYS
:
705 case RECURSIVE_NEVER
:
708 case RECURSIVE_ABORT
:
714 /* --------------------------------------------------------------------------------------------- */
716 #ifdef ENABLE_BACKGROUND
717 static FileProgressStatus
718 do_file_error (const char *str
)
723 FileProgressStatus (*f
) (enum OperationMode
, const char *);
725 pntr
.f
= real_do_file_error
;
727 if (mc_global
.we_are_background
)
728 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
730 return real_do_file_error (Foreground
, str
);
733 /* --------------------------------------------------------------------------------------------- */
735 static FileProgressStatus
736 query_recursive (file_op_context_t
* ctx
, const char *s
)
741 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *);
743 pntr
.f
= real_query_recursive
;
745 if (mc_global
.we_are_background
)
746 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
748 return real_query_recursive (ctx
, Foreground
, s
);
751 /* --------------------------------------------------------------------------------------------- */
753 static FileProgressStatus
754 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
755 struct stat
*_d_stat
)
760 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *,
761 struct stat
*, struct stat
*);
763 pntr
.f
= file_progress_real_query_replace
;
765 if (mc_global
.we_are_background
)
766 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
767 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
769 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
773 /* --------------------------------------------------------------------------------------------- */
775 static FileProgressStatus
776 do_file_error (const char *str
)
778 return real_do_file_error (Foreground
, str
);
781 /* --------------------------------------------------------------------------------------------- */
783 static FileProgressStatus
784 query_recursive (file_op_context_t
* ctx
, const char *s
)
786 return real_query_recursive (ctx
, Foreground
, s
);
789 /* --------------------------------------------------------------------------------------------- */
791 static FileProgressStatus
792 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
793 struct stat
*_d_stat
)
795 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
798 #endif /* !ENABLE_BACKGROUND */
800 /* --------------------------------------------------------------------------------------------- */
801 /** Report error with two files */
803 static FileProgressStatus
804 files_error (const char *format
, const char *file1
, const char *file2
)
806 char buf
[BUF_MEDIUM
];
807 char *nfile1
= g_strdup (path_trunc (file1
, 15));
808 char *nfile2
= g_strdup (path_trunc (file2
, 15));
810 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
815 return do_file_error (buf
);
820 /* --------------------------------------------------------------------------------------------- */
823 copy_file_file_display_progress (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
,
824 struct timeval tv_current
, struct timeval tv_transfer_start
,
825 off_t file_size
, off_t n_read_total
)
829 /* 1. Update rotating dash after some time */
833 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
835 if (n_read_total
== 0)
839 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
840 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
843 /* 4. Compute BPS rate */
844 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
845 if (ctx
->bps_time
< 1)
847 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
849 /* 5. Compute total ETA and BPS */
850 if (ctx
->progress_bytes
!= 0)
852 uintmax_t remain_bytes
;
854 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
857 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
862 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
863 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
866 /* broken on lot of little files */
868 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
869 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
874 /* --------------------------------------------------------------------------------------------- */
876 /* {{{ Move routines */
877 static FileProgressStatus
878 move_file_file (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
)
880 struct stat src_stats
, dst_stats
;
881 FileProgressStatus return_status
= FILE_CONT
;
882 gboolean copy_done
= FALSE
;
883 gboolean old_ask_overwrite
;
884 vfs_path_t
*src_vpath
, *dst_vpath
;
886 src_vpath
= vfs_path_from_str (s
);
887 dst_vpath
= vfs_path_from_str (d
);
889 file_progress_show_source (ctx
, src_vpath
);
890 file_progress_show_target (ctx
, dst_vpath
);
892 if (check_progress_buttons (ctx
) == FILE_ABORT
)
894 return_status
= FILE_ABORT
;
900 while (mc_lstat (src_vpath
, &src_stats
) != 0)
902 /* Source doesn't exist */
904 return_status
= FILE_SKIPALL
;
907 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
908 if (return_status
== FILE_SKIPALL
)
909 ctx
->skip_all
= TRUE
;
912 if (return_status
!= FILE_RETRY
)
916 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
918 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
920 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
924 if (S_ISDIR (dst_stats
.st_mode
))
926 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
928 return_status
= FILE_SKIP
;
932 if (confirm_overwrite
)
934 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
935 if (return_status
!= FILE_CONT
)
938 /* Ok to overwrite */
943 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
945 return_status
= make_symlink (ctx
, s
, d
);
946 if (return_status
== FILE_CONT
)
947 goto retry_src_remove
;
951 if (mc_rename (src_vpath
, dst_vpath
) == 0)
953 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
958 /* Comparison to EXDEV seems not to work in nfs if you're moving from
959 one nfs to the same, but on the server it is on two different
960 filesystems. Then nfs returns EIO instead of EXDEV.
961 Hope it will not hurt if we always in case of error try to copy/delete. */
963 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
968 return_status
= FILE_SKIPALL
;
971 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
972 if (return_status
== FILE_SKIPALL
)
973 ctx
->skip_all
= TRUE
;
974 if (return_status
== FILE_RETRY
)
982 /* Failed because filesystem boundary -> copy the file instead */
983 old_ask_overwrite
= tctx
->ask_overwrite
;
984 tctx
->ask_overwrite
= FALSE
;
985 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
986 tctx
->ask_overwrite
= old_ask_overwrite
;
987 if (return_status
!= FILE_CONT
)
992 file_progress_show_source (ctx
, NULL
);
993 file_progress_show (ctx
, 0, 0, "", FALSE
);
995 return_status
= check_progress_buttons (ctx
);
996 if (return_status
!= FILE_CONT
)
1001 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
1003 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
1004 if (return_status
== FILE_RETRY
)
1005 goto retry_src_remove
;
1006 if (return_status
== FILE_SKIPALL
)
1007 ctx
->skip_all
= TRUE
;
1012 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
1015 vfs_path_free (src_vpath
);
1016 vfs_path_free (dst_vpath
);
1018 return return_status
;
1023 /* --------------------------------------------------------------------------------------------- */
1024 /* {{{ Erase routines */
1025 /** Don't update progress status if progress_count==NULL */
1027 static FileProgressStatus
1028 erase_file (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1032 file_progress_show_deleting (ctx
, vfs_path_as_str (vpath
), &tctx
->progress_count
);
1033 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1034 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1039 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
1041 /* ignore, most likely the mc_unlink fails, too */
1045 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
1049 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), vfs_path_as_str (vpath
));
1050 if (return_status
== FILE_ABORT
)
1051 return return_status
;
1052 if (return_status
== FILE_RETRY
)
1054 if (return_status
== FILE_SKIPALL
)
1055 ctx
->skip_all
= TRUE
;
1059 if (tctx
->progress_count
== 0)
1062 return check_progress_buttons (ctx
);
1065 /* --------------------------------------------------------------------------------------------- */
1068 Recursive remove of files
1070 skip ->warn every level, gets default
1071 skipall->remove as much as possible
1073 static FileProgressStatus
1074 recursive_erase (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1076 struct dirent
*next
;
1079 FileProgressStatus return_status
= FILE_CONT
;
1081 reading
= mc_opendir (vpath
);
1082 if (reading
== NULL
)
1085 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1087 vfs_path_t
*tmp_vpath
;
1090 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
1093 tmp_vpath
= vfs_path_append_new (vpath
, next
->d_name
, NULL
);
1094 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1096 mc_closedir (reading
);
1097 vfs_path_free (tmp_vpath
);
1100 if (S_ISDIR (buf
.st_mode
))
1101 return_status
= recursive_erase (tctx
, ctx
, tmp_vpath
);
1103 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
1104 vfs_path_free (tmp_vpath
);
1106 mc_closedir (reading
);
1108 if (return_status
== FILE_ABORT
)
1111 s
= vfs_path_as_str (vpath
);
1113 file_progress_show_deleting (ctx
, s
, NULL
);
1114 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1115 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1120 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1122 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1123 if (return_status
== FILE_RETRY
)
1125 if (return_status
== FILE_ABORT
)
1127 if (return_status
== FILE_SKIPALL
)
1128 ctx
->skip_all
= TRUE
;
1132 return return_status
;
1135 /* --------------------------------------------------------------------------------------------- */
1136 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1137 in the directory path points to, 0 else. */
1140 check_dir_is_empty (const vfs_path_t
* vpath
)
1146 dir
= mc_opendir (vpath
);
1150 for (d
= mc_readdir (dir
); d
!= NULL
; d
= mc_readdir (dir
))
1151 if (!DIR_IS_DOT (d
->d_name
) && !DIR_IS_DOTDOT (d
->d_name
))
1161 /* --------------------------------------------------------------------------------------------- */
1163 static FileProgressStatus
1164 erase_dir_iff_empty (file_op_context_t
* ctx
, const vfs_path_t
* vpath
, size_t count
)
1166 FileProgressStatus error
= FILE_CONT
;
1169 s
= vfs_path_as_str (vpath
);
1171 file_progress_show_deleting (ctx
, s
, NULL
);
1172 file_progress_show_count (ctx
, count
, ctx
->progress_count
);
1173 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1178 if (check_dir_is_empty (vpath
) == 1) /* not empty or error */
1180 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1182 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1183 if (error
== FILE_SKIPALL
)
1184 ctx
->skip_all
= TRUE
;
1185 if (error
!= FILE_RETRY
)
1195 /* --------------------------------------------------------------------------------------------- */
1196 /* {{{ Panel operate routines */
1199 * Return currently selected entry name or the name of the first marked
1200 * entry if there is one.
1204 panel_get_file (WPanel
* panel
)
1206 if (get_current_type () == view_tree
)
1209 vfs_path_t
*selected_name
;
1211 tree
= (WTree
*) get_panel_widget (get_current_index ());
1212 selected_name
= tree_selected_name (tree
);
1213 return g_strdup (vfs_path_as_str (selected_name
));
1216 if (panel
->marked
!= 0)
1220 for (i
= 0; i
< panel
->dir
.len
; i
++)
1221 if (panel
->dir
.list
[i
].f
.marked
)
1222 return g_strdup (panel
->dir
.list
[i
].fname
);
1224 return g_strdup (panel
->dir
.list
[panel
->selected
].fname
);
1227 /* --------------------------------------------------------------------------------------------- */
1229 * panel_compute_totals:
1231 * compute the number of files and the number of bytes
1232 * used up by the whole selection, recursing directories
1233 * as required. In addition, it checks to see if it will
1234 * overwrite any files by doing the copy.
1237 static FileProgressStatus
1238 panel_compute_totals (const WPanel
* panel
, void *ui
, compute_dir_size_callback cback
,
1239 size_t * ret_count
, uintmax_t * ret_total
, gboolean compute_symlinks
)
1243 for (i
= 0; i
< panel
->dir
.len
; i
++)
1247 if (!panel
->dir
.list
[i
].f
.marked
)
1250 s
= &panel
->dir
.list
[i
].st
;
1252 if (S_ISDIR (s
->st_mode
))
1255 FileProgressStatus status
;
1257 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, NULL
);
1258 status
= compute_dir_size (p
, ui
, cback
, ret_count
, ret_total
, compute_symlinks
);
1261 if (status
!= FILE_CONT
)
1267 *ret_total
+= (uintmax_t) s
->st_size
;
1274 /* --------------------------------------------------------------------------------------------- */
1276 /** Initialize variables for progress bars */
1277 static FileProgressStatus
1278 panel_operate_init_totals (const WPanel
* panel
, const char *source
, file_op_context_t
* ctx
,
1279 filegui_dialog_type_t dialog_type
)
1281 FileProgressStatus status
;
1283 #ifdef ENABLE_BACKGROUND
1284 if (mc_global
.we_are_background
)
1288 if (verbose
&& file_op_compute_totals
)
1290 ComputeDirSizeUI
*ui
;
1292 ui
= compute_dir_size_create_ui (TRUE
);
1294 ctx
->progress_count
= 0;
1295 ctx
->progress_bytes
= 0;
1298 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1299 &ctx
->progress_count
, &ctx
->progress_bytes
,
1305 p
= vfs_path_from_str (source
);
1306 status
= compute_dir_size (p
, ui
, compute_dir_size_update_ui
,
1307 &ctx
->progress_count
, &ctx
->progress_bytes
,
1312 compute_dir_size_destroy_ui (ui
);
1314 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1316 if (status
== FILE_SKIP
)
1322 ctx
->progress_count
= panel
->marked
;
1323 ctx
->progress_bytes
= panel
->total
;
1324 ctx
->progress_totals_computed
= FALSE
;
1327 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
1332 /* --------------------------------------------------------------------------------------------- */
1334 * Generate user prompt for panel operation.
1335 * single_source is the name if the source entry or NULL for multiple
1337 * src_stat is only used when single_source is not NULL.
1341 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1342 gboolean single_source
, const struct stat
*src_stat
)
1344 const char *sp
, *cp
;
1345 char format_string
[BUF_MEDIUM
];
1346 char *dp
= format_string
;
1347 gboolean build_question
= FALSE
;
1349 static gboolean i18n_flag
= FALSE
;
1354 for (i
= G_N_ELEMENTS (op_names1
); i
-- != 0;)
1355 op_names1
[i
] = Q_ (op_names1
[i
]);
1358 for (i
= G_N_ELEMENTS (prompt_parts
); i
-- != 0;)
1359 prompt_parts
[i
] = _(prompt_parts
[i
]);
1361 one_format
= _(one_format
);
1362 many_format
= _(many_format
);
1363 question_format
= _(question_format
);
1364 #endif /* ENABLE_NLS */
1368 sp
= single_source
? one_format
: many_format
;
1379 cp
= op_names1
[operation
];
1382 if (operation
== OP_DELETE
)
1385 build_question
= TRUE
;
1388 cp
= prompt_parts
[5];
1391 if (operation
== OP_DELETE
)
1394 build_question
= TRUE
;
1397 cp
= prompt_parts
[6];
1401 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1403 cp
= (panel
->marked
== panel
->dirs_marked
)
1405 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1418 /* form two-lines query prompt for file deletion */
1419 if (operation
== OP_DELETE
&& sp
[-1] == 'f')
1423 while (isblank (*sp
) != 0)
1436 char tmp
[BUF_MEDIUM
];
1438 memmove (tmp
, format_string
, sizeof (tmp
));
1439 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1442 return g_strdup (format_string
);
1445 /* --------------------------------------------------------------------------------------------- */
1447 #ifdef ENABLE_BACKGROUND
1449 end_bg_process (file_op_context_t
* ctx
, enum OperationMode mode
)
1456 unregister_task_with_pid (pid
);
1457 /* file_op_context_destroy(ctx); */
1463 /* --------------------------------------------------------------------------------------------- */
1464 /*** public functions ****************************************************************************/
1465 /* --------------------------------------------------------------------------------------------- */
1468 copy_file_file (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
,
1469 const char *src_path
, const char *dst_path
)
1471 uid_t src_uid
= (uid_t
) (-1);
1472 gid_t src_gid
= (gid_t
) (-1);
1474 int src_desc
, dest_desc
= -1;
1475 int n_read
, n_written
;
1476 mode_t src_mode
= 0; /* The mode of the source file */
1477 struct stat sb
, sb2
;
1479 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1480 off_t file_size
= -1;
1481 FileProgressStatus return_status
, temp_status
;
1482 struct timeval tv_transfer_start
;
1483 dest_status_t dst_status
= DEST_NONE
;
1485 gboolean is_first_time
= TRUE
;
1486 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1487 gboolean write_errno_nospace
= FALSE
;
1489 /* FIXME: We should not be using global variables! */
1491 return_status
= FILE_RETRY
;
1493 dst_vpath
= vfs_path_from_str (dst_path
);
1494 src_vpath
= vfs_path_from_str (src_path
);
1496 file_progress_show_source (ctx
, src_vpath
);
1497 file_progress_show_target (ctx
, dst_vpath
);
1499 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1501 return_status
= FILE_ABORT
;
1507 while (mc_stat (dst_vpath
, &sb2
) == 0)
1509 if (S_ISDIR (sb2
.st_mode
))
1512 return_status
= FILE_SKIPALL
;
1515 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1516 if (return_status
== FILE_SKIPALL
)
1517 ctx
->skip_all
= TRUE
;
1518 if (return_status
== FILE_RETRY
)
1528 while ((*ctx
->stat_func
) (src_vpath
, &sb
) != 0)
1531 return_status
= FILE_SKIPALL
;
1534 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1535 if (return_status
== FILE_SKIPALL
)
1536 ctx
->skip_all
= TRUE
;
1539 if (return_status
!= FILE_RETRY
)
1545 /* Destination already exists */
1546 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1548 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1549 src_path
, dst_path
);
1553 /* Should we replace destination? */
1554 if (tctx
->ask_overwrite
)
1557 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1558 if (return_status
!= FILE_CONT
)
1563 if (!ctx
->do_append
)
1565 /* Check the hardlinks */
1566 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &sb
))
1568 /* We have made a hardlink - no more processing is necessary */
1569 return_status
= FILE_CONT
;
1573 if (S_ISLNK (sb
.st_mode
))
1575 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1579 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1580 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1582 while (mc_mknod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0
1585 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1586 if (return_status
== FILE_RETRY
)
1588 if (return_status
== FILE_SKIPALL
)
1589 ctx
->skip_all
= TRUE
;
1594 while (ctx
->preserve_uidgid
&& mc_chown (dst_vpath
, sb
.st_uid
, sb
.st_gid
) != 0
1597 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1598 if (temp_status
== FILE_SKIP
)
1600 if (temp_status
== FILE_SKIPALL
)
1601 ctx
->skip_all
= TRUE
;
1602 if (temp_status
!= FILE_RETRY
)
1604 return_status
= temp_status
;
1609 while (ctx
->preserve
&& mc_chmod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
) != 0
1612 temp_status
= file_error (_("Cannot chmod 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 return_status
= FILE_CONT
;
1629 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1631 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1633 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1634 if (return_status
== FILE_RETRY
)
1636 if (return_status
== FILE_SKIPALL
)
1637 ctx
->skip_all
= TRUE
;
1638 if (return_status
== FILE_SKIP
)
1644 if (ctx
->do_reget
!= 0)
1646 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1648 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1650 ctx
->do_append
= FALSE
;
1654 while (mc_fstat (src_desc
, &sb
) != 0)
1657 return_status
= FILE_SKIPALL
;
1660 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1661 if (return_status
== FILE_RETRY
)
1663 if (return_status
== FILE_SKIPALL
)
1664 ctx
->skip_all
= TRUE
;
1665 ctx
->do_append
= FALSE
;
1670 src_mode
= sb
.st_mode
;
1671 src_uid
= sb
.st_uid
;
1672 src_gid
= sb
.st_gid
;
1673 utb
.actime
= sb
.st_atime
;
1674 utb
.modtime
= sb
.st_mtime
;
1675 file_size
= sb
.st_size
;
1677 open_flags
= O_WRONLY
;
1680 if (ctx
->do_append
!= 0)
1681 open_flags
|= O_APPEND
;
1683 open_flags
|= O_CREAT
| O_TRUNC
;
1687 open_flags
|= O_CREAT
| O_EXCL
;
1690 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1692 if (errno
!= EEXIST
)
1695 return_status
= FILE_SKIPALL
;
1698 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1699 if (return_status
== FILE_RETRY
)
1701 if (return_status
== FILE_SKIPALL
)
1702 ctx
->skip_all
= TRUE
;
1703 ctx
->do_append
= FALSE
;
1708 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1710 appending
= ctx
->do_append
;
1711 ctx
->do_append
= FALSE
;
1713 /* Find out the optimal buffer size. */
1714 while (mc_fstat (dest_desc
, &sb
) != 0)
1717 return_status
= FILE_SKIPALL
;
1720 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1721 if (return_status
== FILE_RETRY
)
1723 if (return_status
== FILE_SKIPALL
)
1724 ctx
->skip_all
= TRUE
;
1731 errno
= vfs_preallocate (dest_desc
, file_size
, (ctx
->do_append
!= 0) ? sb
.st_size
: 0);
1736 return_status
= FILE_SKIPALL
;
1740 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1741 if (return_status
== FILE_RETRY
)
1743 if (return_status
== FILE_SKIPALL
)
1744 ctx
->skip_all
= TRUE
;
1746 mc_close (dest_desc
);
1748 mc_unlink (dst_vpath
);
1749 dst_status
= DEST_NONE
;
1753 ctx
->eta_secs
= 0.0;
1756 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1757 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1759 file_progress_show (ctx
, 1, 1, "", TRUE
);
1760 return_status
= check_progress_buttons (ctx
);
1763 if (return_status
!= FILE_CONT
)
1767 off_t n_read_total
= 0;
1768 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1769 int secs
, update_secs
;
1770 const char *stalled_msg
= "";
1772 tv_last_update
= tv_transfer_start
;
1779 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1782 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0 && !ctx
->skip_all
)
1784 return_status
= file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
1785 if (return_status
== FILE_RETRY
)
1787 if (return_status
== FILE_SKIPALL
)
1788 ctx
->skip_all
= TRUE
;
1794 gettimeofday (&tv_current
, NULL
);
1799 n_read_total
+= n_read
;
1801 /* Windows NT ftp servers report that files have no
1802 * permissions: -------, so if we happen to have actually
1803 * read something, we should fix the permissions.
1805 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1806 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1807 gettimeofday (&tv_last_input
, NULL
);
1810 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
)
1814 n_read
-= n_written
;
1819 write_errno_nospace
= (n_written
< 0 && errno
== ENOSPC
);
1822 return_status
= FILE_SKIPALL
;
1825 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1827 if (return_status
== FILE_SKIP
)
1829 if (write_errno_nospace
)
1833 if (return_status
== FILE_SKIPALL
)
1835 ctx
->skip_all
= TRUE
;
1836 if (write_errno_nospace
)
1839 if (return_status
!= FILE_RETRY
)
1842 /* User pressed "Retry". Will the next mc_write() call be successful?
1843 * Reset error flag to be ready for that. */
1844 write_errno_nospace
= FALSE
;
1848 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1850 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1851 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1853 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1855 copy_file_file_display_progress (tctx
, ctx
,
1857 tv_transfer_start
, file_size
, n_read_total
);
1858 tv_last_update
= tv_current
;
1860 is_first_time
= FALSE
;
1862 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1864 stalled_msg
= _("(stalled)");
1868 gboolean force_update
;
1871 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1873 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1875 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1876 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1879 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1884 return_status
= check_progress_buttons (ctx
);
1886 if (return_status
!= FILE_CONT
)
1894 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1897 rotate_dash (FALSE
);
1898 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1900 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1901 if (temp_status
== FILE_RETRY
)
1903 if (temp_status
== FILE_ABORT
)
1904 return_status
= temp_status
;
1905 if (temp_status
== FILE_SKIPALL
)
1906 ctx
->skip_all
= TRUE
;
1910 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1912 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1913 if (temp_status
== FILE_RETRY
)
1915 if (temp_status
== FILE_SKIPALL
)
1916 ctx
->skip_all
= TRUE
;
1917 return_status
= temp_status
;
1921 if (dst_status
== DEST_SHORT
)
1923 /* Remove short file */
1926 /* In case of copy/move to full partition, keep source file
1927 * and remove incomplete destination one */
1928 if (!write_errno_nospace
)
1929 result
= query_dialog (Q_ ("DialogTitle|Copy"),
1930 _("Incomplete file was retrieved. Keep it?"),
1931 D_ERROR
, 2, _("&Delete"), _("&Keep"));
1933 mc_unlink (dst_vpath
);
1935 else if (dst_status
== DEST_FULL
)
1937 /* Copy has succeeded */
1938 if (!appending
&& ctx
->preserve_uidgid
)
1940 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1942 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1943 if (temp_status
== FILE_RETRY
)
1945 if (temp_status
== FILE_SKIPALL
)
1947 ctx
->skip_all
= TRUE
;
1948 return_status
= FILE_CONT
;
1950 if (temp_status
== FILE_SKIP
)
1951 return_status
= FILE_CONT
;
1960 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1962 temp_status
= file_error (_("Cannot chmod 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
;
1975 else if (!dst_exists
)
1977 src_mode
= umask (-1);
1979 src_mode
= 0100666 & ~src_mode
;
1980 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
1982 mc_utime (dst_vpath
, &utb
);
1986 if (return_status
== FILE_CONT
)
1987 return_status
= progress_update_one (tctx
, ctx
, file_size
);
1990 vfs_path_free (src_vpath
);
1991 vfs_path_free (dst_vpath
);
1992 return return_status
;
1995 /* --------------------------------------------------------------------------------------------- */
1997 * I think these copy_*_* functions should have a return type.
1998 * anyway, this function *must* have two directories as arguments.
2000 /* FIXME: This function needs to check the return values of the
2004 copy_dir_dir (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
,
2005 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, GSList
* parent_dirs
)
2007 struct dirent
*next
;
2008 struct stat buf
, cbuf
;
2010 FileProgressStatus return_status
= FILE_CONT
;
2012 vfs_path_t
*src_vpath
, *dst_vpath
;
2013 gboolean do_mkdir
= TRUE
;
2015 src_vpath
= vfs_path_from_str (s
);
2016 dst_vpath
= vfs_path_from_str (d
);
2018 /* First get the mode of the source dir */
2021 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
2024 return_status
= FILE_SKIPALL
;
2027 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
2028 if (return_status
== FILE_RETRY
)
2029 goto retry_src_stat
;
2030 if (return_status
== FILE_SKIPALL
)
2031 ctx
->skip_all
= TRUE
;
2036 if (is_in_linklist (dest_dirs
, src_vpath
, &cbuf
))
2038 /* Don't copy a directory we created before (we don't want to copy
2039 infinitely if a directory is copied into itself) */
2040 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2041 return_status
= FILE_CONT
;
2045 /* Hmm, hardlink to directory??? - Norbert */
2046 /* FIXME: In this step we should do something
2047 in case the destination already exist */
2048 /* Check the hardlinks */
2049 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &cbuf
))
2051 /* We have made a hardlink - no more processing is necessary */
2055 if (!S_ISDIR (cbuf
.st_mode
))
2058 return_status
= FILE_SKIPALL
;
2061 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
2062 if (return_status
== FILE_RETRY
)
2063 goto retry_src_stat
;
2064 if (return_status
== FILE_SKIPALL
)
2065 ctx
->skip_all
= TRUE
;
2070 if (is_in_linklist (parent_dirs
, src_vpath
, &cbuf
))
2072 /* we found a cyclic symbolic link */
2073 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
2074 return_status
= FILE_SKIP
;
2078 lp
= g_new0 (struct link
, 1);
2079 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
2080 lp
->ino
= cbuf
.st_ino
;
2081 lp
->dev
= cbuf
.st_dev
;
2082 parent_dirs
= g_slist_prepend (parent_dirs
, lp
);
2085 /* Now, check if the dest dir exists, if not, create it. */
2086 if (mc_stat (dst_vpath
, &buf
) != 0)
2088 /* Here the dir doesn't exist : make it ! */
2089 if (move_over
&& mc_rename (src_vpath
, dst_vpath
) == 0)
2091 return_status
= FILE_CONT
;
2098 * If the destination directory exists, we want to copy the whole
2099 * directory, but we only want this to happen once.
2101 * Escape sequences added to the * to compiler warnings.
2102 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2103 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2105 if (!S_ISDIR (buf
.st_mode
))
2108 return_status
= FILE_SKIPALL
;
2111 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2112 if (return_status
== FILE_SKIPALL
)
2113 ctx
->skip_all
= TRUE
;
2114 if (return_status
== FILE_RETRY
)
2115 goto retry_dst_stat
;
2119 /* Dive into subdir if exists */
2120 if (toplevel
&& ctx
->dive_into_subdirs
)
2125 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), NULL
);
2126 vfs_path_free (tmp
);
2133 d
= vfs_path_as_str (dst_vpath
);
2137 while (my_mkdir (dst_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2140 return_status
= FILE_SKIPALL
;
2143 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), d
);
2144 if (return_status
== FILE_SKIPALL
)
2145 ctx
->skip_all
= TRUE
;
2147 if (return_status
!= FILE_RETRY
)
2151 lp
= g_new0 (struct link
, 1);
2152 mc_stat (dst_vpath
, &buf
);
2153 lp
->vfs
= vfs_path_get_by_index (dst_vpath
, -1)->class;
2154 lp
->ino
= buf
.st_ino
;
2155 lp
->dev
= buf
.st_dev
;
2156 dest_dirs
= g_slist_prepend (dest_dirs
, lp
);
2159 if (ctx
->preserve_uidgid
)
2161 while (mc_chown (dst_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2164 return_status
= FILE_SKIPALL
;
2167 return_status
= file_error (_("Cannot chown target directory \"%s\"\n%s"), d
);
2168 if (return_status
== FILE_SKIPALL
)
2169 ctx
->skip_all
= TRUE
;
2171 if (return_status
!= FILE_RETRY
)
2176 /* open the source dir for reading */
2177 reading
= mc_opendir (src_vpath
);
2178 if (reading
== NULL
)
2181 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2184 vfs_path_t
*tmp_vpath
;
2187 * Now, we don't want '.' and '..' to be created / copied at any time
2189 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
2192 /* get the filename and add it to the src directory */
2193 path
= mc_build_filename (s
, next
->d_name
, NULL
);
2194 tmp_vpath
= vfs_path_from_str (path
);
2196 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2197 if (S_ISDIR (buf
.st_mode
))
2201 mdpath
= mc_build_filename (d
, next
->d_name
, NULL
);
2203 * From here, we just intend to recursively copy subdirs, not
2204 * the double functionality of copying different when the target
2205 * dir already exists. So, we give the recursive call the flag 0
2206 * meaning no toplevel.
2209 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2216 dest_file
= mc_build_filename (d
, x_basename (path
), NULL
);
2217 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2223 if (do_delete
&& return_status
== FILE_CONT
)
2225 if (ctx
->erase_at_end
)
2227 lp
= g_new0 (struct link
, 1);
2228 lp
->src_vpath
= tmp_vpath
;
2229 lp
->st_mode
= buf
.st_mode
;
2230 erase_list
= g_slist_append (erase_list
, lp
);
2233 else if (S_ISDIR (buf
.st_mode
))
2234 return_status
= erase_dir_iff_empty (ctx
, tmp_vpath
, tctx
->progress_count
);
2236 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
2238 vfs_path_free (tmp_vpath
);
2240 mc_closedir (reading
);
2246 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2247 utb
.actime
= cbuf
.st_atime
;
2248 utb
.modtime
= cbuf
.st_mtime
;
2249 mc_utime (dst_vpath
, &utb
);
2253 cbuf
.st_mode
= umask (-1);
2254 umask (cbuf
.st_mode
);
2255 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2256 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2260 free_link (parent_dirs
->data
);
2261 g_slist_free_1 (parent_dirs
);
2263 vfs_path_free (src_vpath
);
2264 vfs_path_free (dst_vpath
);
2265 return return_status
;
2270 /* --------------------------------------------------------------------------------------------- */
2271 /* {{{ Move routines */
2274 move_dir_dir (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
)
2276 struct stat sbuf
, dbuf
, destbuf
;
2277 FileProgressStatus return_status
;
2278 gboolean move_over
= FALSE
;
2280 vfs_path_t
*src_vpath
, *dst_vpath
;
2282 src_vpath
= vfs_path_from_str (s
);
2283 dst_vpath
= vfs_path_from_str (d
);
2285 file_progress_show_source (ctx
, src_vpath
);
2286 file_progress_show_target (ctx
, dst_vpath
);
2288 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2290 return_status
= FILE_ABORT
;
2296 mc_stat (src_vpath
, &sbuf
);
2298 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2299 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2301 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2306 ; /* destination doesn't exist */
2307 else if (!ctx
->dive_into_subdirs
)
2314 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), NULL
);
2315 vfs_path_free (tmp
);
2318 d
= vfs_path_as_str (dst_vpath
);
2320 /* Check if the user inputted an existing dir */
2322 if (mc_stat (dst_vpath
, &destbuf
) == 0)
2326 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, TRUE
, TRUE
, NULL
);
2328 if (return_status
!= FILE_CONT
)
2332 else if (ctx
->skip_all
)
2333 return_status
= FILE_SKIPALL
;
2336 if (S_ISDIR (destbuf
.st_mode
))
2337 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), d
);
2339 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), d
);
2340 if (return_status
== FILE_SKIPALL
)
2341 ctx
->skip_all
= TRUE
;
2342 if (return_status
== FILE_RETRY
)
2343 goto retry_dst_stat
;
2350 if (mc_rename (src_vpath
, dst_vpath
) == 0)
2352 return_status
= FILE_CONT
;
2360 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2361 if (return_status
== FILE_SKIPALL
)
2362 ctx
->skip_all
= TRUE
;
2363 if (return_status
== FILE_RETRY
)
2368 /* Failed because of filesystem boundary -> copy dir instead */
2369 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, FALSE
, TRUE
, NULL
);
2371 if (return_status
!= FILE_CONT
)
2374 file_progress_show_source (ctx
, NULL
);
2375 file_progress_show (ctx
, 0, 0, "", FALSE
);
2377 return_status
= check_progress_buttons (ctx
);
2378 if (return_status
!= FILE_CONT
)
2382 if (ctx
->erase_at_end
)
2384 while (erase_list
!= NULL
&& return_status
!= FILE_ABORT
)
2386 struct link
*lp
= (struct link
*) erase_list
->data
;
2388 if (S_ISDIR (lp
->st_mode
))
2389 return_status
= erase_dir_iff_empty (ctx
, lp
->src_vpath
, tctx
->progress_count
);
2391 return_status
= erase_file (tctx
, ctx
, lp
->src_vpath
);
2393 erase_list
= g_slist_remove (erase_list
, lp
);
2397 erase_dir_iff_empty (ctx
, src_vpath
, tctx
->progress_count
);
2400 erase_list
= free_linklist (erase_list
);
2402 vfs_path_free (src_vpath
);
2403 vfs_path_free (dst_vpath
);
2404 return return_status
;
2409 /* --------------------------------------------------------------------------------------------- */
2410 /* {{{ Erase routines */
2413 erase_dir (FileOpTotalContext
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* s_vpath
)
2415 FileProgressStatus error
;
2417 file_progress_show_deleting (ctx
, vfs_path_as_str (s_vpath
), NULL
);
2418 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2419 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2424 /* The old way to detect a non empty directory was:
2425 error = my_rmdir (s);
2426 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2427 For the linux user space nfs server (nfs-server-2.2beta29-2)
2428 we would have to check also for EIO. I hope the new way is
2429 fool proof. (Norbert)
2431 error
= check_dir_is_empty (s_vpath
);
2434 error
= query_recursive (ctx
, vfs_path_as_str (s_vpath
));
2435 if (error
== FILE_CONT
)
2436 error
= recursive_erase (tctx
, ctx
, s_vpath
);
2440 while (my_rmdir (vfs_path_as_str (s_vpath
)) == -1 && !ctx
->skip_all
)
2442 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), vfs_path_as_str (s_vpath
));
2443 if (error
!= FILE_RETRY
)
2452 /* --------------------------------------------------------------------------------------------- */
2453 /* {{{ Panel operate routines */
2456 compute_dir_size_create_ui (gboolean allow_skip
)
2458 ComputeDirSizeUI
*ui
;
2460 const char *b1_name
= N_("&Abort");
2461 const char *b2_name
= N_("&Skip");
2462 int b1_width
, b2_width
= 0, b_width
= 0;
2468 b1_name
= _(b1_name
);
2469 b2_name
= _(b2_name
);
2472 b1_width
= str_term_width1 (b1_name
) + 4;
2474 b2_width
= str_term_width1 (b2_name
) + 4 + 1;
2475 b_width
= b1_width
+ b2_width
;
2477 ui
= g_new (ComputeDirSizeUI
, 1);
2479 ui_width
= max (COLS
/ 2, b_width
+ 6);
2480 ui
->dlg
= dlg_create (TRUE
, 0, 0, 8, ui_width
, dialog_colors
, NULL
, NULL
, NULL
,
2481 _("Directory scanning"), DLG_CENTER
);
2483 ui
->dirname
= label_new (2, 3, "");
2484 add_widget (ui
->dlg
, ui
->dirname
);
2485 add_widget (ui
->dlg
, hline_new (4, -1, -1));
2486 b1_x
= (ui_width
- b_width
) / 2;
2487 b
= WIDGET (button_new (5, b1_x
, FILE_ABORT
, NORMAL_BUTTON
, b1_name
, NULL
));
2488 add_widget (ui
->dlg
, b
);
2491 add_widget (ui
->dlg
,
2492 button_new (5, b1_x
+ 1 + b1_width
, FILE_SKIP
, NORMAL_BUTTON
, b2_name
, NULL
));
2493 dlg_select_widget (b
);
2496 /* We will manage the dialog without any help,
2497 that's why we have to call dlg_init */
2503 /* --------------------------------------------------------------------------------------------- */
2506 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
2510 /* schedule to update passive panel */
2511 other_panel
->dirty
= 1;
2513 /* close and destroy dialog */
2514 dlg_run_done (ui
->dlg
);
2515 dlg_destroy (ui
->dlg
);
2520 /* --------------------------------------------------------------------------------------------- */
2523 compute_dir_size_update_ui (void *ui
, const vfs_path_t
* dirname_vpath
, size_t dir_count
,
2524 uintmax_t total_size
)
2526 ComputeDirSizeUI
*this = (ComputeDirSizeUI
*) ui
;
2529 char buffer
[BUF_1K
];
2534 g_snprintf (buffer
, sizeof (buffer
), _("%s\nDirectories: %zd, total size: %s"),
2535 str_trunc (vfs_path_as_str (dirname_vpath
), WIDGET (this->dlg
)->cols
- 6),
2536 dir_count
, size_trunc_sep (total_size
, panels_options
.kilobyte_si
));
2537 label_set_text (this->dirname
, buffer
);
2539 event
.x
= -1; /* Don't show the GPM cursor */
2540 c
= tty_get_event (&event
, FALSE
, FALSE
);
2544 /* Reinitialize to avoid old values after events other than
2545 selecting a button */
2546 this->dlg
->ret_value
= FILE_CONT
;
2548 dlg_process_event (this->dlg
, c
, &event
);
2550 switch (this->dlg
->ret_value
)
2562 /* --------------------------------------------------------------------------------------------- */
2566 * Computes the number of bytes used by the files in a directory
2570 compute_dir_size (const vfs_path_t
* dirname_vpath
, void *ui
, compute_dir_size_callback cback
,
2571 size_t * ret_count
, uintmax_t * ret_total
, gboolean compute_symlinks
)
2575 return do_compute_dir_size (dirname_vpath
, ui
, cback
, ret_count
, &marked
, ret_total
,
2579 /* --------------------------------------------------------------------------------------------- */
2583 * Performs one of the operations on the selection on the source_panel
2584 * (copy, delete, move).
2586 * Returns TRUE if did change the directory
2587 * structure, Returns FALSE if user aborted
2589 * force_single forces operation on the current entry and affects
2590 * default destination. Current filename is used as default.
2594 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2596 WPanel
*panel
= (WPanel
*) source_panel
;
2597 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2598 || (get_current_type () == view_tree
);
2600 char *source
= NULL
;
2601 #ifdef WITH_FULL_PATHS
2602 vfs_path_t
*source_with_vpath
= NULL
;
2604 #define source_with_path source
2605 #endif /* !WITH_FULL_PATHS */
2607 vfs_path_t
*dest_vpath
= NULL
;
2609 char *save_cwd
= NULL
, *save_dest
= NULL
;
2610 struct stat src_stat
;
2611 gboolean ret_val
= TRUE
;
2613 FileProgressStatus value
;
2614 file_op_context_t
*ctx
;
2615 FileOpTotalContext
*tctx
;
2616 vfs_path_t
*tmp_vpath
;
2617 filegui_dialog_type_t dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2619 gboolean do_bg
= FALSE
; /* do background operation? */
2621 static gboolean i18n_flag
= FALSE
;
2624 for (i
= G_N_ELEMENTS (op_names
); i
-- != 0;)
2625 op_names
[i
] = Q_ (op_names
[i
]);
2629 linklist
= free_linklist (linklist
);
2630 dest_dirs
= free_linklist (dest_dirs
);
2634 vfs_path_t
*source_vpath
;
2637 source
= g_strdup (selection (panel
)->fname
);
2639 source
= panel_get_file (panel
);
2641 if (DIR_IS_DOTDOT (source
))
2644 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2648 source_vpath
= vfs_path_from_str (source
);
2649 /* Update stat to get actual info */
2650 if (mc_lstat (source_vpath
, &src_stat
) != 0)
2652 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2653 path_trunc (source
, 30), unix_error_string (errno
));
2655 /* Directory was changed outside MC. Reload it forced */
2656 if (!panel
->is_panelized
)
2658 panel_update_flags_t flags
= UP_RELOAD
;
2660 /* don't update panelized panel */
2661 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2662 flags
|= UP_ONLY_CURRENT
;
2664 update_panels (flags
, UP_KEEPSEL
);
2666 vfs_path_free (source_vpath
);
2669 vfs_path_free (source_vpath
);
2672 ctx
= file_op_context_new (operation
);
2674 /* Show confirmation dialog */
2675 if (operation
!= OP_DELETE
)
2677 char *tmp_dest_dir
, *dest_dir
;
2680 /* Forced single operations default to the original name */
2682 tmp_dest_dir
= g_strdup (source
);
2683 else if (get_other_type () == view_listing
)
2684 tmp_dest_dir
= g_strdup (vfs_path_as_str (other_panel
->cwd_vpath
));
2686 tmp_dest_dir
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
2688 * Add trailing backslash only when do non-local ops.
2689 * It saves user from occasional file renames (when destination
2692 if (!force_single
&& tmp_dest_dir
[0] != '\0'
2693 && tmp_dest_dir
[strlen (tmp_dest_dir
) - 1] != PATH_SEP
)
2695 /* add trailing separator */
2696 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2697 g_free (tmp_dest_dir
);
2702 dest_dir
= tmp_dest_dir
;
2704 if (dest_dir
== NULL
)
2710 /* Generate confirmation prompt */
2711 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2713 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2714 source
!= NULL
? (void *) source
2715 : (void *) &panel
->marked
, dest_dir
, &do_bg
);
2720 if (dest
== NULL
|| dest
[0] == '\0')
2726 dest_vpath
= vfs_path_from_str (dest
);
2728 else if (confirm_delete
)
2731 char fmd_buf
[BUF_MEDIUM
];
2733 /* Generate confirmation prompt */
2734 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2737 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2740 const int fmd_xlen
= 64;
2741 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2742 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2750 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2759 tctx
= file_op_total_context_new ();
2760 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2762 #ifdef ENABLE_BACKGROUND
2763 /* Did the user select to do a background operation? */
2768 v
= do_background (ctx
,
2769 g_strconcat (op_names
[operation
], ": ",
2770 vfs_path_as_str (panel
->cwd_vpath
), (char *) NULL
));
2772 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2774 /* If we are the parent */
2777 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2779 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2780 vfs_path_free (dest_vpath
);
2782 /* file_op_context_destroy (ctx); */
2787 #endif /* ENABLE_BACKGROUND */
2789 if (operation
== OP_DELETE
)
2790 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2791 else if (single_entry
&& S_ISDIR (selection (panel
)->st
.st_mode
))
2792 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2793 else if (single_entry
|| force_single
)
2794 dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2796 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2799 /* Initialize things */
2800 /* We do not want to trash cache every time file is
2801 created/touched. However, this will make our cache contain
2804 && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2805 save_dest
= g_strdup (dest
);
2807 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2808 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2809 save_cwd
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
2811 /* Now, let's do the job */
2813 /* This code is only called by the tree and panel code */
2816 /* We now have ETA in all cases */
2818 /* One file: FIXME mc_chdir will take user out of any vfs */
2819 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2824 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2825 chdir_retcode
= mc_chdir (vpath
);
2826 vfs_path_free (vpath
);
2827 if (chdir_retcode
< 0)
2834 /* The source and src_stat variables have been initialized before */
2835 #ifdef WITH_FULL_PATHS
2836 if (g_path_is_absolute (source
))
2837 source_with_vpath
= vfs_path_from_str (source
);
2839 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2840 #endif /* WITH_FULL_PATHS */
2841 if (panel_operate_init_totals (panel
, vfs_path_as_str (source_with_vpath
), ctx
, dialog_type
)
2844 if (operation
== OP_DELETE
)
2846 if (S_ISDIR (src_stat
.st_mode
))
2847 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2849 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2853 temp
= transform_source (ctx
, source_with_vpath
);
2855 value
= transform_error
;
2858 char *repl_dest
, *temp2
;
2860 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2861 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2863 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2868 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2872 vfs_path_free (dest_vpath
);
2874 dest_vpath
= vfs_path_from_str (dest
);
2879 /* we use file_mask_op_follow_links only with OP_COPY */
2880 ctx
->stat_func (source_with_vpath
, &src_stat
);
2882 if (S_ISDIR (src_stat
.st_mode
))
2884 copy_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2885 dest
, TRUE
, FALSE
, FALSE
, NULL
);
2888 copy_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2893 if (S_ISDIR (src_stat
.st_mode
))
2895 move_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
), dest
);
2898 move_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2903 /* Unknown file operation */
2907 } /* Copy or move operation */
2909 if ((value
== FILE_CONT
) && !force_single
)
2910 unmark_files (panel
);
2917 /* Check destination for copy or move operation */
2918 while (operation
!= OP_DELETE
)
2921 struct stat dst_stat
;
2923 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2925 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2929 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2933 if (panel_operate_init_totals (panel
, NULL
, ctx
, dialog_type
) == FILE_CONT
)
2935 /* Loop for every file, perform the actual copy operation */
2936 for (i
= 0; i
< panel
->dir
.len
; i
++)
2938 const char *source2
;
2940 if (!panel
->dir
.list
[i
].f
.marked
)
2941 continue; /* Skip the unmarked ones */
2943 source2
= panel
->dir
.list
[i
].fname
;
2944 src_stat
= panel
->dir
.list
[i
].st
;
2946 #ifdef WITH_FULL_PATHS
2947 vfs_path_free (source_with_vpath
);
2948 if (g_path_is_absolute (source2
))
2949 source_with_vpath
= vfs_path_from_str (source2
);
2952 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2953 #endif /* WITH_FULL_PATHS */
2955 if (operation
== OP_DELETE
)
2957 if (S_ISDIR (src_stat
.st_mode
))
2958 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2960 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2964 temp
= transform_source (ctx
, source_with_vpath
);
2966 value
= transform_error
;
2969 char *temp2
, *temp3
, *repl_dest
, *source_with_path_str
;
2971 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2972 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2974 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2979 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2982 source_with_path_str
=
2983 strutils_shell_unescape (vfs_path_as_str (source_with_vpath
));
2985 temp2
= strutils_shell_unescape (temp2
);
2991 /* we use file_mask_op_follow_links only with OP_COPY */
2995 vpath
= vfs_path_from_str (source_with_path_str
);
2996 ctx
->stat_func (vpath
, &src_stat
);
2997 vfs_path_free (vpath
);
2999 if (S_ISDIR (src_stat
.st_mode
))
3000 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
,
3001 TRUE
, FALSE
, FALSE
, NULL
);
3003 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
3004 dest_dirs
= free_linklist (dest_dirs
);
3008 if (S_ISDIR (src_stat
.st_mode
))
3009 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
);
3011 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
3015 /* Unknown file operation */
3021 } /* Copy or move operation */
3023 if (value
== FILE_ABORT
)
3026 if (value
== FILE_CONT
)
3027 do_file_mark (panel
, i
, 0);
3029 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3031 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3032 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3035 if (operation
!= OP_DELETE
)
3036 file_progress_show (ctx
, 0, 0, "", FALSE
);
3038 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3042 } /* Loop for every file */
3044 } /* Many entries */
3048 if (save_cwd
!= NULL
)
3050 tmp_vpath
= vfs_path_from_str (save_cwd
);
3051 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3052 vfs_path_free (tmp_vpath
);
3056 if (save_dest
!= NULL
)
3058 tmp_vpath
= vfs_path_from_str (save_dest
);
3059 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3060 vfs_path_free (tmp_vpath
);
3064 linklist
= free_linklist (linklist
);
3065 dest_dirs
= free_linklist (dest_dirs
);
3066 #ifdef WITH_FULL_PATHS
3067 vfs_path_free (source_with_vpath
);
3068 #endif /* WITH_FULL_PATHS */
3070 vfs_path_free (dest_vpath
);
3071 g_free (ctx
->dest_mask
);
3072 ctx
->dest_mask
= NULL
;
3074 #ifdef ENABLE_BACKGROUND
3075 /* Let our parent know we are saying bye bye */
3076 if (mc_global
.we_are_background
)
3078 int cur_pid
= getpid ();
3079 /* Send pid to parent with child context, it is fork and
3080 don't modify real parent ctx */
3082 parent_call ((void *) end_bg_process
, ctx
, 0);
3085 my_exit (EXIT_SUCCESS
);
3087 #endif /* ENABLE_BACKGROUND */
3089 file_op_total_context_destroy (tctx
);
3091 file_op_context_destroy (ctx
);
3099 /* --------------------------------------------------------------------------------------------- */
3100 /* {{{ Query/status report routines */
3101 /** Report error with one file */
3103 file_error (const char *format
, const char *file
)
3105 char buf
[BUF_MEDIUM
];
3107 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3109 return do_file_error (buf
);
3112 /* --------------------------------------------------------------------------------------------- */
3115 Cause emacs to enter folding mode for this file: