4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011
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
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>
64 #include "lib/global.h"
65 #include "lib/tty/tty.h"
66 #include "lib/tty/key.h"
67 #include "lib/search.h"
68 #include "lib/strescape.h"
69 #include "lib/strutil.h"
71 #include "lib/vfs/vfs.h"
72 #include "lib/widget.h"
74 #include "src/setup.h"
75 #ifdef ENABLE_BACKGROUND
76 #include "src/background.h" /* do_background() */
79 #include "layout.h" /* rotate_dash() */
81 /* Needed for current_panel, other_panel and WTree */
86 #include "midnight.h" /* current_panel */
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 (FileOpContext
* ctx
, const char *source
)
204 s
= g_strdup (source
);
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
))
215 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
219 transform_error
= FILE_SKIP
;
226 /* --------------------------------------------------------------------------------------------- */
229 free_link (void *data
)
231 struct link
*lp
= (struct link
*) data
;
233 vfs_path_free (lp
->src_vpath
);
234 vfs_path_free (lp
->dst_vpath
);
238 /* --------------------------------------------------------------------------------------------- */
241 free_linklist (GSList
* lp
)
243 g_slist_foreach (lp
, (GFunc
) free_link
, NULL
);
249 /* --------------------------------------------------------------------------------------------- */
252 is_in_linklist (const GSList
* lp
, const vfs_path_t
* vpath
, const struct stat
*sb
)
254 const struct vfs_class
*class;
255 ino_t ino
= sb
->st_ino
;
256 dev_t dev
= sb
->st_dev
;
258 class = vfs_path_get_last_path_vfs (vpath
);
260 for (; lp
!= NULL
; lp
= g_slist_next (lp
))
262 const struct link
*lnk
= (const struct link
*) lp
->data
;
264 if (lnk
->vfs
== class && lnk
->ino
== ino
&& lnk
->dev
== dev
)
270 /* --------------------------------------------------------------------------------------------- */
272 * Check and made hardlink
274 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
275 * and a hardlink was succesfully made
279 check_hardlinks (const vfs_path_t
* src_vpath
, const vfs_path_t
* dst_vpath
, struct stat
*pstat
)
284 const struct vfs_class
*my_vfs
;
285 ino_t ino
= pstat
->st_ino
;
286 dev_t dev
= pstat
->st_dev
;
287 struct stat link_stat
;
289 if ((vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
292 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
294 for (lp
= linklist
; lp
!= NULL
; lp
= g_slist_next (lp
))
296 lnk
= (struct link
*) lp
->data
;
298 if (lnk
->vfs
== my_vfs
&& lnk
->ino
== ino
&& lnk
->dev
== dev
)
300 const struct vfs_class
*lp_name_class
;
303 lp_name_class
= vfs_path_get_last_path_vfs (lnk
->src_vpath
);
304 stat_result
= mc_stat (lnk
->src_vpath
, &link_stat
);
306 if (stat_result
== 0 && link_stat
.st_ino
== ino
307 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
309 const struct vfs_class
*p_class
, *dst_name_class
;
311 dst_name_class
= vfs_path_get_last_path_vfs (dst_vpath
);
312 p_class
= vfs_path_get_last_path_vfs (lnk
->dst_vpath
);
314 if (dst_name_class
== p_class
&&
315 mc_stat (lnk
->dst_vpath
, &link_stat
) == 0 &&
316 mc_link (lnk
->dst_vpath
, dst_vpath
) == 0)
320 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
325 lnk
= g_new0 (struct link
, 1);
331 lnk
->src_vpath
= vfs_path_clone (src_vpath
);
332 lnk
->dst_vpath
= vfs_path_clone (dst_vpath
);
333 linklist
= g_slist_prepend (linklist
, lnk
);
339 /* --------------------------------------------------------------------------------------------- */
341 * Duplicate the contents of the symbolic link src_path in dst_path.
342 * Try to make a stable symlink if the option "stable symlink" was
343 * set in the file mask dialog.
344 * If dst_path is an existing symlink it will be deleted silently
345 * (upper levels take already care of existing files at dst_path).
348 static FileProgressStatus
349 make_symlink (FileOpContext
* ctx
, const char *src_path
, const char *dst_path
)
351 char link_target
[MC_MAXPATHLEN
];
353 FileProgressStatus return_status
;
355 vfs_path_t
*src_vpath
;
356 vfs_path_t
*dst_vpath
;
357 gboolean dst_is_symlink
;
358 vfs_path_t
*link_target_vpath
= NULL
;
360 src_vpath
= vfs_path_from_str (src_path
);
361 dst_vpath
= vfs_path_from_str (dst_path
);
362 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
365 len
= mc_readlink (src_vpath
, link_target
, MC_MAXPATHLEN
- 1);
369 return_status
= FILE_SKIPALL
;
372 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
373 if (return_status
== FILE_SKIPALL
)
374 ctx
->skip_all
= TRUE
;
375 if (return_status
== FILE_RETRY
)
376 goto retry_src_readlink
;
380 link_target
[len
] = 0;
382 if (ctx
->stable_symlinks
)
385 if (!vfs_file_is_local (src_vpath
) || !vfs_file_is_local (dst_vpath
))
387 message (D_ERROR
, MSG_ERROR
,
388 _("Cannot make stable symlinks across"
389 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
390 ctx
->stable_symlinks
= FALSE
;
394 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
399 const char *r
= strrchr (src_path
, PATH_SEP
);
403 p
= g_strndup (src_path
, r
- src_path
+ 1);
404 if (g_path_is_absolute (dst_path
))
405 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
407 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
409 if (vfs_path_tokens_count (q
) > 1)
411 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
413 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
414 s
= g_strconcat (p
, link_target
, (char *) NULL
);
416 g_strlcpy (link_target
, s
, sizeof (link_target
));
418 tmp_vpath2
= vfs_path_from_str (link_target
);
419 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
420 vfs_path_free (tmp_vpath1
);
421 vfs_path_free (tmp_vpath2
);
424 g_strlcpy (link_target
, s
, sizeof (link_target
));
433 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
436 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
439 return_status
= FILE_CONT
;
443 * if dst_exists, it is obvious that this had failed.
444 * We can delete the old symlink and try again...
448 if (mc_unlink (dst_vpath
) == 0)
449 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
452 return_status
= FILE_CONT
;
457 return_status
= FILE_SKIPALL
;
460 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
461 if (return_status
== FILE_SKIPALL
)
462 ctx
->skip_all
= TRUE
;
463 if (return_status
== FILE_RETRY
)
464 goto retry_dst_symlink
;
468 vfs_path_free (src_vpath
);
469 vfs_path_free (dst_vpath
);
470 vfs_path_free (link_target_vpath
);
471 return return_status
;
474 /* --------------------------------------------------------------------------------------------- */
476 static FileProgressStatus
477 progress_update_one (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, off_t add
)
479 struct timeval tv_current
;
480 static struct timeval tv_start
= { };
482 tctx
->progress_count
++;
483 tctx
->progress_bytes
+= (uintmax_t) add
;
485 if (tv_start
.tv_sec
== 0)
487 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
489 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
490 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
492 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
494 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
495 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
497 tv_start
.tv_sec
= tv_current
.tv_sec
;
500 return check_progress_buttons (ctx
);
503 /* --------------------------------------------------------------------------------------------- */
505 static FileProgressStatus
506 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
510 const char *head_msg
;
512 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
514 msg
= g_strdup_printf (fmt
, a
, b
);
515 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
519 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
522 /* --------------------------------------------------------------------------------------------- */
524 static FileProgressStatus
525 warn_same_file (const char *fmt
, const char *a
, const char *b
)
527 #ifdef ENABLE_BACKGROUND
532 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
533 const char *a
, const char *b
);
537 pntr
.f
= real_warn_same_file
;
539 if (mc_global
.we_are_background
)
540 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
542 return real_warn_same_file (Foreground
, fmt
, a
, b
);
545 /* --------------------------------------------------------------------------------------------- */
546 /* {{{ Query/status report routines */
548 static FileProgressStatus
549 real_do_file_error (enum OperationMode mode
, const char *error
)
554 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
556 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
578 /* --------------------------------------------------------------------------------------------- */
580 static FileProgressStatus
581 real_query_recursive (FileOpContext
* ctx
, enum OperationMode mode
, const char *s
)
584 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
590 msg
= mode
== Foreground
591 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
592 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
593 text
= g_strdup_printf (msg
, path_trunc (s
, 30));
598 ctx
->recursive_result
=
599 (FileCopyMode
) query_dialog (op_names
[OP_DELETE
], text
, D_ERROR
, 5,
600 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
603 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
607 switch (ctx
->recursive_result
)
610 case RECURSIVE_ALWAYS
:
614 case RECURSIVE_NEVER
:
617 case RECURSIVE_ABORT
:
623 /* --------------------------------------------------------------------------------------------- */
625 #ifdef ENABLE_BACKGROUND
626 static FileProgressStatus
627 do_file_error (const char *str
)
632 FileProgressStatus (*f
) (enum OperationMode
, const char *);
634 pntr
.f
= real_do_file_error
;
636 if (mc_global
.we_are_background
)
637 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
639 return real_do_file_error (Foreground
, str
);
642 /* --------------------------------------------------------------------------------------------- */
644 static FileProgressStatus
645 query_recursive (FileOpContext
* ctx
, const char *s
)
650 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
652 pntr
.f
= real_query_recursive
;
654 if (mc_global
.we_are_background
)
655 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
657 return real_query_recursive (ctx
, Foreground
, s
);
660 /* --------------------------------------------------------------------------------------------- */
662 static FileProgressStatus
663 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
664 struct stat
*_d_stat
)
669 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
670 struct stat
*, struct stat
*);
672 pntr
.f
= file_progress_real_query_replace
;
674 if (mc_global
.we_are_background
)
675 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
676 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
678 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
682 /* --------------------------------------------------------------------------------------------- */
684 static FileProgressStatus
685 do_file_error (const char *str
)
687 return real_do_file_error (Foreground
, str
);
690 /* --------------------------------------------------------------------------------------------- */
692 static FileProgressStatus
693 query_recursive (FileOpContext
* ctx
, const char *s
)
695 return real_query_recursive (ctx
, Foreground
, s
);
698 /* --------------------------------------------------------------------------------------------- */
700 static FileProgressStatus
701 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
702 struct stat
*_d_stat
)
704 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
707 #endif /* !ENABLE_BACKGROUND */
709 /* --------------------------------------------------------------------------------------------- */
710 /** Report error with two files */
712 static FileProgressStatus
713 files_error (const char *format
, const char *file1
, const char *file2
)
715 char buf
[BUF_MEDIUM
];
716 char *nfile1
= g_strdup (path_trunc (file1
, 15));
717 char *nfile2
= g_strdup (path_trunc (file2
, 15));
719 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
724 return do_file_error (buf
);
729 /* --------------------------------------------------------------------------------------------- */
732 copy_file_file_display_progress (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
733 struct timeval tv_current
, struct timeval tv_transfer_start
,
734 off_t file_size
, off_t n_read_total
)
738 /* 1. Update rotating dash after some time */
742 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
744 if (n_read_total
== 0)
748 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
749 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
752 /* 4. Compute BPS rate */
753 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
754 if (ctx
->bps_time
< 1)
756 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
758 /* 5. Compute total ETA and BPS */
759 if (ctx
->progress_bytes
!= 0)
761 uintmax_t remain_bytes
;
763 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
766 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
771 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
772 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
775 /* broken on lot of little files */
777 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
778 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
783 /* --------------------------------------------------------------------------------------------- */
785 /* {{{ Move routines */
786 static FileProgressStatus
787 move_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
789 struct stat src_stats
, dst_stats
;
790 FileProgressStatus return_status
= FILE_CONT
;
791 gboolean copy_done
= FALSE
;
792 gboolean old_ask_overwrite
;
793 vfs_path_t
*src_vpath
, *dst_vpath
;
795 src_vpath
= vfs_path_from_str (s
);
796 dst_vpath
= vfs_path_from_str (d
);
798 file_progress_show_source (ctx
, src_vpath
);
799 file_progress_show_target (ctx
, dst_vpath
);
801 if (check_progress_buttons (ctx
) == FILE_ABORT
)
803 return_status
= FILE_ABORT
;
809 while (mc_lstat (src_vpath
, &src_stats
) != 0)
811 /* Source doesn't exist */
813 return_status
= FILE_SKIPALL
;
816 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
817 if (return_status
== FILE_SKIPALL
)
818 ctx
->skip_all
= TRUE
;
821 if (return_status
!= FILE_RETRY
)
825 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
827 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
829 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
833 if (S_ISDIR (dst_stats
.st_mode
))
835 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
837 return_status
= FILE_SKIP
;
841 if (confirm_overwrite
)
843 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
844 if (return_status
!= FILE_CONT
)
847 /* Ok to overwrite */
852 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
854 return_status
= make_symlink (ctx
, s
, d
);
855 if (return_status
== FILE_CONT
)
856 goto retry_src_remove
;
860 if (mc_rename (src_vpath
, dst_vpath
) == 0)
862 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
867 /* Comparison to EXDEV seems not to work in nfs if you're moving from
868 one nfs to the same, but on the server it is on two different
869 filesystems. Then nfs returns EIO instead of EXDEV.
870 Hope it will not hurt if we always in case of error try to copy/delete. */
872 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
877 return_status
= FILE_SKIPALL
;
880 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
881 if (return_status
== FILE_SKIPALL
)
882 ctx
->skip_all
= TRUE
;
883 if (return_status
== FILE_RETRY
)
891 /* Failed because filesystem boundary -> copy the file instead */
892 old_ask_overwrite
= tctx
->ask_overwrite
;
893 tctx
->ask_overwrite
= FALSE
;
894 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
895 tctx
->ask_overwrite
= old_ask_overwrite
;
896 if (return_status
!= FILE_CONT
)
901 file_progress_show_source (ctx
, NULL
);
902 file_progress_show (ctx
, 0, 0, "", FALSE
);
904 return_status
= check_progress_buttons (ctx
);
905 if (return_status
!= FILE_CONT
)
910 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
912 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
913 if (return_status
== FILE_RETRY
)
914 goto retry_src_remove
;
915 if (return_status
== FILE_SKIPALL
)
916 ctx
->skip_all
= TRUE
;
921 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
924 vfs_path_free (src_vpath
);
925 vfs_path_free (dst_vpath
);
927 return return_status
;
932 /* --------------------------------------------------------------------------------------------- */
933 /* {{{ Erase routines */
934 /** Don't update progress status if progress_count==NULL */
936 static FileProgressStatus
937 erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const vfs_path_t
* vpath
)
943 s
= vfs_path_to_str (vpath
);
944 file_progress_show_deleting (ctx
, s
);
945 if (check_progress_buttons (ctx
) == FILE_ABORT
)
952 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
954 /* ignore, most likely the mc_unlink fails, too */
958 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
960 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), s
);
961 if (return_status
== FILE_ABORT
)
964 return return_status
;
966 if (return_status
== FILE_RETRY
)
968 if (return_status
== FILE_SKIPALL
)
969 ctx
->skip_all
= TRUE
;
973 if (tctx
->progress_count
== 0)
975 return progress_update_one (tctx
, ctx
, buf
.st_size
);
978 /* --------------------------------------------------------------------------------------------- */
981 Recursive remove of files
983 skip ->warn every level, gets default
984 skipall->remove as much as possible
986 static FileProgressStatus
987 recursive_erase (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
993 FileProgressStatus return_status
= FILE_CONT
;
996 if (strcmp (s
, "..") == 0)
999 vpath
= vfs_path_from_str (s
);
1000 reading
= mc_opendir (vpath
);
1002 if (reading
== NULL
)
1004 return_status
= FILE_RETRY
;
1008 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1010 vfs_path_t
*tmp_vpath
;
1012 if (!strcmp (next
->d_name
, "."))
1014 if (!strcmp (next
->d_name
, ".."))
1016 path
= mc_build_filename (s
, next
->d_name
, NULL
);
1017 tmp_vpath
= vfs_path_from_str (path
);
1018 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1021 mc_closedir (reading
);
1022 vfs_path_free (tmp_vpath
);
1023 return_status
= FILE_RETRY
;
1026 if (S_ISDIR (buf
.st_mode
))
1027 return_status
= recursive_erase (tctx
, ctx
, path
);
1029 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
1030 vfs_path_free (tmp_vpath
);
1033 mc_closedir (reading
);
1034 if (return_status
== FILE_ABORT
)
1037 file_progress_show_deleting (ctx
, s
);
1038 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1040 return_status
= FILE_ABORT
;
1045 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1047 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1048 if (return_status
== FILE_RETRY
)
1050 if (return_status
== FILE_ABORT
)
1052 if (return_status
== FILE_SKIPALL
)
1053 ctx
->skip_all
= TRUE
;
1058 vfs_path_free (vpath
);
1059 return return_status
;
1062 /* --------------------------------------------------------------------------------------------- */
1063 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1064 in the directory path points to, 0 else. */
1067 check_dir_is_empty (const vfs_path_t
* vpath
)
1073 dir
= mc_opendir (vpath
);
1077 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
))
1079 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1080 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1081 continue; /* "." or ".." */
1090 /* --------------------------------------------------------------------------------------------- */
1092 static FileProgressStatus
1093 erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
)
1095 FileProgressStatus error
;
1096 vfs_path_t
*s_vpath
;
1098 if (strcmp (s
, "..") == 0)
1101 if (strcmp (s
, ".") == 0)
1104 file_progress_show_deleting (ctx
, s
);
1105 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1110 s_vpath
= vfs_path_from_str (s
);
1112 if (check_dir_is_empty (s_vpath
) == 1) /* not empty or error */
1114 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1116 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1117 if (error
== FILE_SKIPALL
)
1118 ctx
->skip_all
= TRUE
;
1119 if (error
!= FILE_RETRY
)
1121 vfs_path_free (s_vpath
);
1127 vfs_path_free (s_vpath
);
1133 /* --------------------------------------------------------------------------------------------- */
1134 /* {{{ Panel operate routines */
1137 * Return currently selected entry name or the name of the first marked
1138 * entry if there is one.
1142 panel_get_file (WPanel
* panel
)
1144 if (get_current_type () == view_tree
)
1148 tree
= (WTree
*) get_panel_widget (get_current_index ());
1149 return vfs_path_to_str (tree_selected_name (tree
));
1152 if (panel
->marked
!= 0)
1156 for (i
= 0; i
< panel
->count
; i
++)
1157 if (panel
->dir
.list
[i
].f
.marked
)
1158 return g_strdup (panel
->dir
.list
[i
].fname
);
1160 return g_strdup (panel
->dir
.list
[panel
->selected
].fname
);
1163 /* --------------------------------------------------------------------------------------------- */
1165 * panel_compute_totals:
1167 * compute the number of files and the number of bytes
1168 * used up by the whole selection, recursing directories
1169 * as required. In addition, it checks to see if it will
1170 * overwrite any files by doing the copy.
1173 static FileProgressStatus
1174 panel_compute_totals (const WPanel
* panel
, const void *ui
,
1175 compute_dir_size_callback cback
,
1176 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
1183 for (i
= 0; i
< panel
->count
; i
++)
1187 if (!panel
->dir
.list
[i
].f
.marked
)
1190 s
= &panel
->dir
.list
[i
].st
;
1192 if (S_ISDIR (s
->st_mode
))
1195 size_t subdir_count
= 0;
1196 uintmax_t subdir_bytes
= 0;
1197 FileProgressStatus status
;
1199 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, NULL
);
1201 compute_dir_size (p
, ui
, cback
, &subdir_count
, &subdir_bytes
, compute_symlinks
);
1204 if (status
!= FILE_CONT
)
1207 *ret_marked
+= subdir_count
;
1208 *ret_total
+= subdir_bytes
;
1213 *ret_total
+= (uintmax_t) s
->st_size
;
1220 /* --------------------------------------------------------------------------------------------- */
1222 /** Initialize variables for progress bars */
1223 static FileProgressStatus
1224 panel_operate_init_totals (FileOperation operation
,
1225 const WPanel
* panel
, const char *source
, FileOpContext
* ctx
)
1227 FileProgressStatus status
;
1229 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
)
1231 ComputeDirSizeUI
*ui
;
1233 ui
= compute_dir_size_create_ui ();
1236 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1237 &ctx
->progress_count
, &ctx
->progress_bytes
,
1243 p
= vfs_path_from_str (source
);
1244 status
= compute_dir_size (p
, ui
, compute_dir_size_update_ui
,
1245 &ctx
->progress_count
, &ctx
->progress_bytes
,
1250 compute_dir_size_destroy_ui (ui
);
1252 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1257 ctx
->progress_count
= panel
->marked
;
1258 ctx
->progress_bytes
= panel
->total
;
1259 ctx
->progress_totals_computed
= FALSE
;
1265 /* --------------------------------------------------------------------------------------------- */
1267 * Generate user prompt for panel operation.
1268 * single_source is the name if the source entry or NULL for multiple
1270 * src_stat is only used when single_source is not NULL.
1274 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1275 gboolean single_source
, const struct stat
*src_stat
)
1277 const char *sp
, *cp
;
1278 char format_string
[BUF_MEDIUM
];
1279 char *dp
= format_string
;
1280 gboolean build_question
= FALSE
;
1282 static gboolean i18n_flag
= FALSE
;
1287 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1288 op_names1
[i
] = Q_ (op_names1
[i
]);
1291 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1292 prompt_parts
[i
] = _(prompt_parts
[i
]);
1294 one_format
= _(one_format
);
1295 many_format
= _(many_format
);
1296 question_format
= _(question_format
);
1297 #endif /* ENABLE_NLS */
1301 sp
= single_source
? one_format
: many_format
;
1312 cp
= op_names1
[operation
];
1315 if (operation
== OP_DELETE
)
1318 build_question
= TRUE
;
1321 cp
= prompt_parts
[5];
1324 if (operation
== OP_DELETE
)
1327 build_question
= TRUE
;
1330 cp
= prompt_parts
[6];
1334 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1336 cp
= (panel
->marked
== panel
->dirs_marked
)
1338 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1351 /* form two-lines query prompt for file deletion */
1352 if (operation
== OP_DELETE
&& sp
[-1] == 'f')
1356 while (isblank (*sp
) != 0)
1369 char tmp
[BUF_MEDIUM
];
1371 memmove (tmp
, format_string
, sizeof (tmp
));
1372 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1375 return g_strdup (format_string
);
1378 /* --------------------------------------------------------------------------------------------- */
1380 #ifdef ENABLE_BACKGROUND
1382 end_bg_process (FileOpContext
* ctx
, enum OperationMode mode
)
1389 unregister_task_with_pid (pid
);
1390 /* file_op_context_destroy(ctx); */
1396 /* --------------------------------------------------------------------------------------------- */
1397 /*** public functions ****************************************************************************/
1398 /* --------------------------------------------------------------------------------------------- */
1401 copy_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
1402 const char *src_path
, const char *dst_path
)
1404 uid_t src_uid
= (uid_t
) (-1);
1405 gid_t src_gid
= (gid_t
) (-1);
1407 int src_desc
, dest_desc
= -1;
1408 int n_read
, n_written
;
1409 mode_t src_mode
= 0; /* The mode of the source file */
1410 struct stat sb
, sb2
;
1412 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1413 off_t file_size
= -1;
1414 FileProgressStatus return_status
, temp_status
;
1415 struct timeval tv_transfer_start
;
1416 dest_status_t dst_status
= DEST_NONE
;
1418 gboolean is_first_time
= TRUE
;
1419 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1420 gboolean write_errno_nospace
= FALSE
;
1422 /* FIXME: We should not be using global variables! */
1424 return_status
= FILE_RETRY
;
1426 dst_vpath
= vfs_path_from_str (dst_path
);
1427 src_vpath
= vfs_path_from_str (src_path
);
1429 file_progress_show_source (ctx
, src_vpath
);
1430 file_progress_show_target (ctx
, dst_vpath
);
1432 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1434 return_status
= FILE_ABORT
;
1440 while (mc_stat (dst_vpath
, &sb2
) == 0)
1442 if (S_ISDIR (sb2
.st_mode
))
1445 return_status
= FILE_SKIPALL
;
1448 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1449 if (return_status
== FILE_SKIPALL
)
1450 ctx
->skip_all
= TRUE
;
1451 if (return_status
== FILE_RETRY
)
1461 while ((*ctx
->stat_func
) (src_vpath
, &sb
) != 0)
1464 return_status
= FILE_SKIPALL
;
1467 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1468 if (return_status
== FILE_SKIPALL
)
1469 ctx
->skip_all
= TRUE
;
1472 if (return_status
!= FILE_RETRY
)
1478 /* Destination already exists */
1479 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1481 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1482 src_path
, dst_path
);
1486 /* Should we replace destination? */
1487 if (tctx
->ask_overwrite
)
1490 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1491 if (return_status
!= FILE_CONT
)
1496 if (!ctx
->do_append
)
1498 /* Check the hardlinks */
1499 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &sb
))
1501 /* We have made a hardlink - no more processing is necessary */
1502 return_status
= FILE_CONT
;
1506 if (S_ISLNK (sb
.st_mode
))
1508 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1512 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1513 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1515 while (mc_mknod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0
1518 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1519 if (return_status
== FILE_RETRY
)
1521 if (return_status
== FILE_SKIPALL
)
1522 ctx
->skip_all
= TRUE
;
1527 while (ctx
->preserve_uidgid
&& mc_chown (dst_vpath
, sb
.st_uid
, sb
.st_gid
) != 0
1530 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1531 if (temp_status
== FILE_SKIP
)
1533 if (temp_status
== FILE_SKIPALL
)
1534 ctx
->skip_all
= TRUE
;
1535 if (temp_status
!= FILE_RETRY
)
1537 return_status
= temp_status
;
1542 while (ctx
->preserve
&& mc_chmod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
) != 0
1545 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1546 if (temp_status
== FILE_SKIP
)
1548 if (temp_status
== FILE_SKIPALL
)
1549 ctx
->skip_all
= TRUE
;
1550 if (temp_status
!= FILE_RETRY
)
1552 return_status
= temp_status
;
1557 return_status
= FILE_CONT
;
1562 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1564 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1566 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1567 if (return_status
== FILE_RETRY
)
1569 if (return_status
== FILE_SKIPALL
)
1570 ctx
->skip_all
= TRUE
;
1571 if (return_status
== FILE_SKIP
)
1577 if (ctx
->do_reget
!= 0)
1579 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1581 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1583 ctx
->do_append
= FALSE
;
1587 while (mc_fstat (src_desc
, &sb
) != 0)
1590 return_status
= FILE_SKIPALL
;
1593 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1594 if (return_status
== FILE_RETRY
)
1596 if (return_status
== FILE_SKIPALL
)
1597 ctx
->skip_all
= TRUE
;
1598 ctx
->do_append
= FALSE
;
1603 src_mode
= sb
.st_mode
;
1604 src_uid
= sb
.st_uid
;
1605 src_gid
= sb
.st_gid
;
1606 utb
.actime
= sb
.st_atime
;
1607 utb
.modtime
= sb
.st_mtime
;
1608 file_size
= sb
.st_size
;
1610 open_flags
= O_WRONLY
;
1613 if (ctx
->do_append
!= 0)
1614 open_flags
|= O_APPEND
;
1616 open_flags
|= O_CREAT
| O_TRUNC
;
1620 open_flags
|= O_CREAT
| O_EXCL
;
1623 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1625 if (errno
!= EEXIST
)
1628 return_status
= FILE_SKIPALL
;
1631 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1632 if (return_status
== FILE_RETRY
)
1634 if (return_status
== FILE_SKIPALL
)
1635 ctx
->skip_all
= TRUE
;
1636 ctx
->do_append
= FALSE
;
1641 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1643 appending
= ctx
->do_append
;
1644 ctx
->do_append
= FALSE
;
1646 /* Find out the optimal buffer size. */
1647 while (mc_fstat (dest_desc
, &sb
) != 0)
1650 return_status
= FILE_SKIPALL
;
1653 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1654 if (return_status
== FILE_RETRY
)
1656 if (return_status
== FILE_SKIPALL
)
1657 ctx
->skip_all
= TRUE
;
1664 errno
= vfs_preallocate (dest_desc
, file_size
, (ctx
->do_append
!= 0) ? sb
.st_size
: 0);
1669 return_status
= FILE_SKIPALL
;
1673 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1674 if (return_status
== FILE_RETRY
)
1676 if (return_status
== FILE_SKIPALL
)
1677 ctx
->skip_all
= TRUE
;
1679 mc_close (dest_desc
);
1681 mc_unlink (dst_vpath
);
1682 dst_status
= DEST_NONE
;
1686 ctx
->eta_secs
= 0.0;
1689 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1690 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1692 file_progress_show (ctx
, 1, 1, "", TRUE
);
1693 return_status
= check_progress_buttons (ctx
);
1696 if (return_status
!= FILE_CONT
)
1700 off_t n_read_total
= 0;
1701 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1702 int secs
, update_secs
;
1703 const char *stalled_msg
= "";
1705 tv_last_update
= tv_transfer_start
;
1712 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1715 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0 && !ctx
->skip_all
)
1717 return_status
= file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
1718 if (return_status
== FILE_RETRY
)
1720 if (return_status
== FILE_SKIPALL
)
1721 ctx
->skip_all
= TRUE
;
1727 gettimeofday (&tv_current
, NULL
);
1732 n_read_total
+= n_read
;
1734 /* Windows NT ftp servers report that files have no
1735 * permissions: -------, so if we happen to have actually
1736 * read something, we should fix the permissions.
1738 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1739 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1740 gettimeofday (&tv_last_input
, NULL
);
1743 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
)
1747 n_read
-= n_written
;
1752 write_errno_nospace
= (n_written
< 0 && errno
== ENOSPC
);
1755 return_status
= FILE_SKIPALL
;
1758 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1760 if (return_status
== FILE_SKIP
)
1762 if (write_errno_nospace
)
1766 if (return_status
== FILE_SKIPALL
)
1768 ctx
->skip_all
= TRUE
;
1769 if (write_errno_nospace
)
1772 if (return_status
!= FILE_RETRY
)
1775 /* User pressed "Retry". Will the next mc_write() call be succesful?
1776 * Reset error flag to be ready for that. */
1777 write_errno_nospace
= FALSE
;
1781 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1783 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1784 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1786 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1788 copy_file_file_display_progress (tctx
, ctx
,
1790 tv_transfer_start
, file_size
, n_read_total
);
1791 tv_last_update
= tv_current
;
1793 is_first_time
= FALSE
;
1795 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1797 stalled_msg
= _("(stalled)");
1801 gboolean force_update
;
1804 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1806 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1808 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1809 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1812 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1817 return_status
= check_progress_buttons (ctx
);
1819 if (return_status
!= FILE_CONT
)
1827 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1830 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1832 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1833 if (temp_status
== FILE_RETRY
)
1835 if (temp_status
== FILE_ABORT
)
1836 return_status
= temp_status
;
1837 if (temp_status
== FILE_SKIPALL
)
1838 ctx
->skip_all
= TRUE
;
1842 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1844 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1845 if (temp_status
== FILE_RETRY
)
1847 if (temp_status
== FILE_SKIPALL
)
1848 ctx
->skip_all
= TRUE
;
1849 return_status
= temp_status
;
1853 if (dst_status
== DEST_SHORT
)
1855 /* Remove short file */
1858 /* In case of copy/move to full partition, keep source file
1859 * and remove incomplete destination one */
1860 if (!write_errno_nospace
)
1861 result
= query_dialog (Q_ ("DialogTitle|Copy"),
1862 _("Incomplete file was retrieved. Keep it?"),
1863 D_ERROR
, 2, _("&Delete"), _("&Keep"));
1865 mc_unlink (dst_vpath
);
1867 else if (dst_status
== DEST_FULL
)
1869 /* Copy has succeeded */
1870 if (!appending
&& ctx
->preserve_uidgid
)
1872 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1874 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1875 if (temp_status
== FILE_RETRY
)
1877 if (temp_status
== FILE_SKIPALL
)
1879 ctx
->skip_all
= TRUE
;
1880 return_status
= FILE_CONT
;
1882 if (temp_status
== FILE_SKIP
)
1883 return_status
= FILE_CONT
;
1892 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1894 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1895 if (temp_status
== FILE_RETRY
)
1897 if (temp_status
== FILE_SKIPALL
)
1899 ctx
->skip_all
= TRUE
;
1900 return_status
= FILE_CONT
;
1902 if (temp_status
== FILE_SKIP
)
1903 return_status
= FILE_CONT
;
1907 else if (!dst_exists
)
1909 src_mode
= umask (-1);
1911 src_mode
= 0100666 & ~src_mode
;
1912 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
1914 mc_utime (dst_vpath
, &utb
);
1918 if (return_status
== FILE_CONT
)
1919 return_status
= progress_update_one (tctx
, ctx
, file_size
);
1922 vfs_path_free (src_vpath
);
1923 vfs_path_free (dst_vpath
);
1924 return return_status
;
1927 /* --------------------------------------------------------------------------------------------- */
1929 * I think these copy_*_* functions should have a return type.
1930 * anyway, this function *must* have two directories as arguments.
1932 /* FIXME: This function needs to check the return values of the
1936 copy_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *_d
,
1937 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, GSList
* parent_dirs
)
1939 struct dirent
*next
;
1940 struct stat buf
, cbuf
;
1942 char *dest_dir
= NULL
;
1943 FileProgressStatus return_status
= FILE_CONT
;
1947 vfs_path_t
*src_vpath
, *dst_vpath
, *dest_dir_vpath
= NULL
;
1951 src_vpath
= vfs_path_from_str (s
);
1952 dst_vpath
= vfs_path_from_str (_d
);
1954 /* First get the mode of the source dir */
1957 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
1960 return_status
= FILE_SKIPALL
;
1963 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
1964 if (return_status
== FILE_RETRY
)
1965 goto retry_src_stat
;
1966 if (return_status
== FILE_SKIPALL
)
1967 ctx
->skip_all
= TRUE
;
1972 if (is_in_linklist (dest_dirs
, src_vpath
, &cbuf
))
1974 /* Don't copy a directory we created before (we don't want to copy
1975 infinitely if a directory is copied into itself) */
1976 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1977 return_status
= FILE_CONT
;
1981 /* Hmm, hardlink to directory??? - Norbert */
1982 /* FIXME: In this step we should do something
1983 in case the destination already exist */
1984 /* Check the hardlinks */
1985 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &cbuf
))
1987 /* We have made a hardlink - no more processing is necessary */
1991 if (!S_ISDIR (cbuf
.st_mode
))
1994 return_status
= FILE_SKIPALL
;
1997 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
1998 if (return_status
== FILE_RETRY
)
1999 goto retry_src_stat
;
2000 if (return_status
== FILE_SKIPALL
)
2001 ctx
->skip_all
= TRUE
;
2006 if (is_in_linklist (parent_dirs
, src_vpath
, &cbuf
))
2008 /* we found a cyclic symbolic link */
2009 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
2010 return_status
= FILE_SKIP
;
2014 lp
= g_new0 (struct link
, 1);
2015 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
2016 lp
->ino
= cbuf
.st_ino
;
2017 lp
->dev
= cbuf
.st_dev
;
2018 parent_dirs
= g_slist_prepend (parent_dirs
, lp
);
2021 /* Now, check if the dest dir exists, if not, create it. */
2022 if (mc_stat (dst_vpath
, &buf
) != 0)
2024 /* Here the dir doesn't exist : make it ! */
2027 if (mc_rename (src_vpath
, dst_vpath
) == 0)
2029 return_status
= FILE_CONT
;
2039 * If the destination directory exists, we want to copy the whole
2040 * directory, but we only want this to happen once.
2042 * Escape sequences added to the * to compiler warnings.
2043 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2044 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2046 if (!S_ISDIR (buf
.st_mode
))
2049 return_status
= FILE_SKIPALL
;
2052 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2053 if (return_status
== FILE_SKIPALL
)
2054 ctx
->skip_all
= TRUE
;
2055 if (return_status
== FILE_RETRY
)
2056 goto retry_dst_stat
;
2060 /* Dive into subdir if exists */
2061 if (toplevel
&& ctx
->dive_into_subdirs
)
2063 dest_dir
= mc_build_filename (d
, x_basename (s
), NULL
);
2069 dest_dir_vpath
= vfs_path_from_str (dest_dir
);
2073 dest_dir_vpath
= vfs_path_from_str (dest_dir
);
2074 while (my_mkdir (dest_dir_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2077 return_status
= FILE_SKIPALL
;
2080 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir
);
2081 if (return_status
== FILE_SKIPALL
)
2082 ctx
->skip_all
= TRUE
;
2084 if (return_status
!= FILE_RETRY
)
2088 lp
= g_new0 (struct link
, 1);
2089 mc_stat (dest_dir_vpath
, &buf
);
2090 lp
->vfs
= vfs_path_get_by_index (dest_dir_vpath
, -1)->class;
2091 lp
->ino
= buf
.st_ino
;
2092 lp
->dev
= buf
.st_dev
;
2093 dest_dirs
= g_slist_prepend (dest_dirs
, lp
);
2096 if (ctx
->preserve_uidgid
)
2098 while (mc_chown (dest_dir_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2101 return_status
= FILE_SKIPALL
;
2105 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir
);
2106 if (return_status
== FILE_SKIPALL
)
2107 ctx
->skip_all
= TRUE
;
2109 if (return_status
!= FILE_RETRY
)
2114 /* open the source dir for reading */
2115 reading
= mc_opendir (src_vpath
);
2116 if (reading
== NULL
)
2119 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2122 vfs_path_t
*tmp_vpath
;
2124 * Now, we don't want '.' and '..' to be created / copied at any time
2126 if (!strcmp (next
->d_name
, "."))
2128 if (!strcmp (next
->d_name
, ".."))
2131 /* get the filename and add it to the src directory */
2132 path
= mc_build_filename (s
, next
->d_name
, NULL
);
2133 tmp_vpath
= vfs_path_from_str (path
);
2135 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2136 if (S_ISDIR (buf
.st_mode
))
2140 mdpath
= mc_build_filename (dest_dir
, next
->d_name
, NULL
);
2142 * From here, we just intend to recursively copy subdirs, not
2143 * the double functionality of copying different when the target
2144 * dir already exists. So, we give the recursive call the flag 0
2145 * meaning no toplevel.
2148 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2155 dest_file
= mc_build_filename (dest_dir
, x_basename (path
), NULL
);
2156 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2159 if (do_delete
&& return_status
== FILE_CONT
)
2161 if (ctx
->erase_at_end
)
2163 lp
= g_new0 (struct link
, 1);
2164 lp
->src_vpath
= vfs_path_clone (tmp_vpath
);
2165 lp
->st_mode
= buf
.st_mode
;
2166 erase_list
= g_slist_append (erase_list
, lp
);
2168 else if (S_ISDIR (buf
.st_mode
))
2169 return_status
= erase_dir_iff_empty (ctx
, path
);
2171 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
2174 vfs_path_free (tmp_vpath
);
2176 mc_closedir (reading
);
2180 mc_chmod (dest_dir_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2181 utb
.actime
= cbuf
.st_atime
;
2182 utb
.modtime
= cbuf
.st_mtime
;
2183 mc_utime (dest_dir_vpath
, &utb
);
2187 cbuf
.st_mode
= umask (-1);
2188 umask (cbuf
.st_mode
);
2189 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2190 mc_chmod (dest_dir_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2195 vfs_path_free (dest_dir_vpath
);
2196 free_link (parent_dirs
->data
);
2197 g_slist_free_1 (parent_dirs
);
2200 vfs_path_free (src_vpath
);
2201 vfs_path_free (dst_vpath
);
2202 return return_status
;
2207 /* --------------------------------------------------------------------------------------------- */
2208 /* {{{ Move routines */
2211 move_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
2213 struct stat sbuf
, dbuf
, destbuf
;
2216 FileProgressStatus return_status
;
2217 gboolean move_over
= FALSE
;
2219 vfs_path_t
*src_vpath
, *dst_vpath
, *destdir_vpath
;
2221 src_vpath
= vfs_path_from_str (s
);
2222 dst_vpath
= vfs_path_from_str (d
);
2224 file_progress_show_source (ctx
, src_vpath
);
2225 file_progress_show_target (ctx
, dst_vpath
);
2227 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2229 return_status
= FILE_ABORT
;
2235 mc_stat (src_vpath
, &sbuf
);
2237 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2238 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2240 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2245 destdir
= g_strdup (d
); /* destination doesn't exist */
2246 else if (!ctx
->dive_into_subdirs
)
2248 destdir
= g_strdup (d
);
2252 destdir
= mc_build_filename (d
, x_basename (s
), NULL
);
2254 destdir_vpath
= vfs_path_from_str (destdir
);
2256 /* Check if the user inputted an existing dir */
2258 if (mc_stat (destdir_vpath
, &destbuf
) == 0)
2262 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
2264 if (return_status
!= FILE_CONT
)
2268 else if (ctx
->skip_all
)
2269 return_status
= FILE_SKIPALL
;
2272 if (S_ISDIR (destbuf
.st_mode
))
2273 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir
);
2275 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir
);
2276 if (return_status
== FILE_SKIPALL
)
2277 ctx
->skip_all
= TRUE
;
2278 if (return_status
== FILE_RETRY
)
2279 goto retry_dst_stat
;
2283 vfs_path_free (destdir_vpath
);
2288 if (mc_rename (src_vpath
, destdir_vpath
) == 0)
2290 return_status
= FILE_CONT
;
2298 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2299 if (return_status
== FILE_SKIPALL
)
2300 ctx
->skip_all
= TRUE
;
2301 if (return_status
== FILE_RETRY
)
2306 /* Failed because of filesystem boundary -> copy dir instead */
2307 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
2309 if (return_status
!= FILE_CONT
)
2312 file_progress_show_source (ctx
, NULL
);
2313 file_progress_show (ctx
, 0, 0, "", FALSE
);
2315 return_status
= check_progress_buttons (ctx
);
2316 if (return_status
!= FILE_CONT
)
2320 if (ctx
->erase_at_end
)
2322 for (; erase_list
!= NULL
&& return_status
!= FILE_ABORT
;)
2324 lp
= (struct link
*) erase_list
->data
;
2326 if (S_ISDIR (lp
->st_mode
))
2330 src_path
= vfs_path_to_str (lp
->src_vpath
);
2331 return_status
= erase_dir_iff_empty (ctx
, src_path
);
2335 return_status
= erase_file (tctx
, ctx
, lp
->src_vpath
);
2337 erase_list
= g_slist_remove (erase_list
, lp
);
2341 erase_dir_iff_empty (ctx
, s
);
2345 vfs_path_free (destdir_vpath
);
2346 erase_list
= free_linklist (erase_list
);
2348 vfs_path_free (src_vpath
);
2349 vfs_path_free (dst_vpath
);
2350 return return_status
;
2355 /* --------------------------------------------------------------------------------------------- */
2356 /* {{{ Erase routines */
2359 erase_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const vfs_path_t
* s_vpath
)
2361 FileProgressStatus error
;
2364 s
= vfs_path_to_str (s_vpath
);
2367 if (strcmp (s, "..") == 0)
2370 if (strcmp (s, ".") == 0)
2374 file_progress_show_deleting (ctx
, s
);
2375 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2382 /* The old way to detect a non empty directory was:
2383 error = my_rmdir (s);
2384 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2385 For the linux user space nfs server (nfs-server-2.2beta29-2)
2386 we would have to check also for EIO. I hope the new way is
2387 fool proof. (Norbert)
2389 error
= check_dir_is_empty (s_vpath
);
2392 error
= query_recursive (ctx
, s
);
2393 if (error
== FILE_CONT
)
2394 error
= recursive_erase (tctx
, ctx
, s
);
2399 while (my_rmdir (s
) == -1 && !ctx
->skip_all
)
2401 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
2402 if (error
!= FILE_RETRY
)
2415 /* --------------------------------------------------------------------------------------------- */
2416 /* {{{ Panel operate routines */
2419 compute_dir_size_create_ui (void)
2421 ComputeDirSizeUI
*ui
;
2423 const char *b_name
= N_("&Abort");
2429 ui
= g_new (ComputeDirSizeUI
, 1);
2431 ui
->dlg
= create_dlg (TRUE
, 0, 0, 7, COLS
/ 2, dialog_colors
, NULL
, NULL
,
2432 NULL
, _("Directory scanning"), DLG_CENTER
);
2433 ui
->dirname
= label_new (2, 3, "");
2434 add_widget (ui
->dlg
, ui
->dirname
);
2435 add_widget (ui
->dlg
, hline_new (3, -1, -1));
2436 add_widget_autopos (ui
->dlg
,
2437 button_new (4, 2, FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
),
2438 WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
, NULL
);
2440 /* We will manage the dialog without any help,
2441 that's why we have to call init_dlg */
2447 /* --------------------------------------------------------------------------------------------- */
2450 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
2454 /* schedule to update passive panel */
2455 other_panel
->dirty
= 1;
2457 /* close and destroy dialog */
2458 dlg_run_done (ui
->dlg
);
2459 destroy_dlg (ui
->dlg
);
2464 /* --------------------------------------------------------------------------------------------- */
2467 compute_dir_size_update_ui (const void *ui
, const vfs_path_t
* dirname_vpath
)
2469 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
2477 dirname
= vfs_path_to_str (dirname_vpath
);
2478 label_set_text (this->dirname
, str_trunc (dirname
, WIDGET (this->dlg
)->cols
- 6));
2481 event
.x
= -1; /* Don't show the GPM cursor */
2482 c
= tty_get_event (&event
, FALSE
, FALSE
);
2486 /* Reinitialize to avoid old values after events other than
2487 selecting a button */
2488 this->dlg
->ret_value
= FILE_CONT
;
2490 dlg_process_event (this->dlg
, c
, &event
);
2492 switch (this->dlg
->ret_value
)
2502 /* --------------------------------------------------------------------------------------------- */
2506 * Computes the number of bytes used by the files in a directory
2510 compute_dir_size (const vfs_path_t
* dirname_vpath
, const void *ui
,
2511 compute_dir_size_callback cback
,
2512 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
2517 struct dirent
*dirent
;
2518 FileProgressStatus ret
= FILE_CONT
;
2520 if (!compute_symlinks
)
2522 res
= mc_lstat (dirname_vpath
, &s
);
2526 /* don't scan symlink to directory */
2527 if (S_ISLNK (s
.st_mode
))
2530 *ret_total
+= (uintmax_t) s
.st_size
;
2535 dir
= mc_opendir (dirname_vpath
);
2540 while ((dirent
= mc_readdir (dir
)) != NULL
)
2542 vfs_path_t
*tmp_vpath
;
2544 ret
= (cback
!= NULL
) ? cback (ui
, dirname_vpath
) : FILE_CONT
;
2546 if (ret
!= FILE_CONT
)
2549 if (strcmp (dirent
->d_name
, ".") == 0)
2551 if (strcmp (dirent
->d_name
, "..") == 0)
2554 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, NULL
);
2555 res
= mc_lstat (tmp_vpath
, &s
);
2558 if (S_ISDIR (s
.st_mode
))
2560 size_t subdir_count
= 0;
2561 uintmax_t subdir_bytes
= 0;
2564 compute_dir_size (tmp_vpath
, ui
, cback
, &subdir_count
, &subdir_bytes
,
2567 if (ret
!= FILE_CONT
)
2569 vfs_path_free (tmp_vpath
);
2573 *ret_marked
+= subdir_count
;
2574 *ret_total
+= subdir_bytes
;
2579 *ret_total
+= (uintmax_t) s
.st_size
;
2582 vfs_path_free (tmp_vpath
);
2589 /* --------------------------------------------------------------------------------------------- */
2593 * Performs one of the operations on the selection on the source_panel
2594 * (copy, delete, move).
2596 * Returns TRUE if did change the directory
2597 * structure, Returns FALSE if user aborted
2599 * force_single forces operation on the current entry and affects
2600 * default destination. Current filename is used as default.
2604 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2606 WPanel
*panel
= (WPanel
*) source_panel
;
2607 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2608 || (get_current_type () == view_tree
);
2610 char *source
= NULL
;
2611 #ifdef WITH_FULL_PATHS
2612 vfs_path_t
*source_with_vpath
= NULL
;
2613 char *source_with_path_str
= NULL
;
2615 #define source_with_path source
2616 #endif /* !WITH_FULL_PATHS */
2618 vfs_path_t
*dest_vpath
= NULL
;
2620 char *save_cwd
= NULL
, *save_dest
= NULL
;
2621 struct stat src_stat
;
2622 gboolean ret_val
= TRUE
;
2624 FileProgressStatus value
;
2626 FileOpTotalContext
*tctx
;
2627 vfs_path_t
*tmp_vpath
;
2629 gboolean do_bg
= FALSE
; /* do background operation? */
2631 static gboolean i18n_flag
= FALSE
;
2634 for (i
= sizeof (op_names
) / sizeof (op_names
[0]); i
--;)
2635 op_names
[i
] = Q_ (op_names
[i
]);
2639 linklist
= free_linklist (linklist
);
2640 dest_dirs
= free_linklist (dest_dirs
);
2644 vfs_path_t
*source_vpath
;
2647 source
= g_strdup (selection (panel
)->fname
);
2649 source
= panel_get_file (panel
);
2651 if (strcmp (source
, "..") == 0)
2654 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2658 source_vpath
= vfs_path_from_str (source
);
2659 /* Update stat to get actual info */
2660 if (mc_lstat (source_vpath
, &src_stat
) != 0)
2662 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2663 path_trunc (source
, 30), unix_error_string (errno
));
2665 /* Directory was changed outside MC. Reload it forced */
2666 if (!panel
->is_panelized
)
2668 panel_update_flags_t flags
= UP_RELOAD
;
2670 /* don't update panelized panel */
2671 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2672 flags
|= UP_ONLY_CURRENT
;
2674 update_panels (flags
, UP_KEEPSEL
);
2676 vfs_path_free (source_vpath
);
2679 vfs_path_free (source_vpath
);
2682 ctx
= file_op_context_new (operation
);
2684 /* Show confirmation dialog */
2685 if (operation
!= OP_DELETE
)
2687 char *tmp_dest_dir
, *dest_dir
;
2690 /* Forced single operations default to the original name */
2692 tmp_dest_dir
= g_strdup (source
);
2693 else if (get_other_type () == view_listing
)
2694 tmp_dest_dir
= vfs_path_to_str (other_panel
->cwd_vpath
);
2696 tmp_dest_dir
= vfs_path_to_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
[0] != '\0'
2703 && tmp_dest_dir
[strlen (tmp_dest_dir
) - 1] != PATH_SEP
)
2705 /* add trailing separator */
2706 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2707 g_free (tmp_dest_dir
);
2712 dest_dir
= tmp_dest_dir
;
2714 if (dest_dir
== NULL
)
2720 /* Generate confirmation prompt */
2721 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2723 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2724 source
!= NULL
? (void *) source
2725 : (void *) &panel
->marked
, dest_dir
, &do_bg
);
2730 if (dest
== NULL
|| dest
[0] == '\0')
2736 dest_vpath
= vfs_path_from_str (dest
);
2738 else if (confirm_delete
)
2741 char fmd_buf
[BUF_MEDIUM
];
2743 /* Generate confirmation prompt */
2744 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2747 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2750 const int fmd_xlen
= 64;
2751 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2752 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2760 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2769 tctx
= file_op_total_context_new ();
2770 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2773 filegui_dialog_type_t dialog_type
;
2775 if (operation
== OP_DELETE
)
2776 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2779 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2780 ? FILEGUI_DIALOG_MULTI_ITEM
: FILEGUI_DIALOG_ONE_ITEM
;
2782 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2783 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2786 /* Background also need ctx->ui, but not full */
2788 file_op_context_create_ui_without_init (ctx
, TRUE
, dialog_type
);
2790 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
2793 #ifdef ENABLE_BACKGROUND
2794 /* Did the user select to do a background operation? */
2800 cwd_str
= vfs_path_to_str (panel
->cwd_vpath
);
2801 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", cwd_str
, (char *) NULL
));
2804 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2806 /* If we are the parent */
2809 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2811 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2812 vfs_path_free (dest_vpath
);
2814 /* file_op_context_destroy (ctx); */
2818 #endif /* ENABLE_BACKGROUND */
2820 /* Initialize things */
2821 /* We do not want to trash cache every time file is
2822 created/touched. However, this will make our cache contain
2824 if ((dest
!= NULL
) && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2825 save_dest
= g_strdup (dest
);
2827 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2828 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2829 save_cwd
= vfs_path_to_str (panel
->cwd_vpath
);
2831 /* Now, let's do the job */
2833 /* This code is only called by the tree and panel code */
2836 /* We now have ETA in all cases */
2838 /* One file: FIXME mc_chdir will take user out of any vfs */
2839 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2844 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2845 chdir_retcode
= mc_chdir (vpath
);
2846 vfs_path_free (vpath
);
2847 if (chdir_retcode
< 0)
2854 /* The source and src_stat variables have been initialized before */
2855 #ifdef WITH_FULL_PATHS
2856 if (g_path_is_absolute (source
))
2857 source_with_vpath
= vfs_path_from_str (source
);
2859 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2860 source_with_path_str
= vfs_path_to_str (source_with_vpath
);
2861 #endif /* WITH_FULL_PATHS */
2862 if (panel_operate_init_totals (operation
, panel
, source_with_path_str
, ctx
) == FILE_CONT
)
2864 if (operation
== OP_DELETE
)
2866 if (S_ISDIR (src_stat
.st_mode
))
2867 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2869 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2873 temp
= transform_source (ctx
, source_with_path_str
);
2875 value
= transform_error
;
2878 char *repl_dest
, *temp2
;
2880 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2881 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2885 vfs_path_free (dest_vpath
);
2887 dest_vpath
= vfs_path_from_str (dest
);
2892 /* we use file_mask_op_follow_links only with OP_COPY */
2893 ctx
->stat_func (source_with_vpath
, &src_stat
);
2895 if (S_ISDIR (src_stat
.st_mode
))
2896 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, dest
,
2897 TRUE
, FALSE
, FALSE
, NULL
);
2899 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, dest
);
2903 if (S_ISDIR (src_stat
.st_mode
))
2904 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, dest
);
2906 value
= move_file_file (tctx
, ctx
, source_with_path_str
, dest
);
2910 /* Unknown file operation */
2914 } /* Copy or move operation */
2916 if ((value
== FILE_CONT
) && !force_single
)
2917 unmark_files (panel
);
2924 /* Check destination for copy or move operation */
2925 while (operation
!= OP_DELETE
)
2928 struct stat dst_stat
;
2930 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2932 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2936 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2940 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
)
2942 /* Loop for every file, perform the actual copy operation */
2943 for (i
= 0; i
< panel
->count
; i
++)
2945 const char *source2
;
2947 if (!panel
->dir
.list
[i
].f
.marked
)
2948 continue; /* Skip the unmarked ones */
2950 source2
= panel
->dir
.list
[i
].fname
;
2951 src_stat
= panel
->dir
.list
[i
].st
;
2953 #ifdef WITH_FULL_PATHS
2954 g_free (source_with_path_str
);
2955 vfs_path_free (source_with_vpath
);
2956 if (g_path_is_absolute (source2
))
2957 source_with_vpath
= vfs_path_from_str (source2
);
2960 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2961 source_with_path_str
= vfs_path_to_str (source_with_vpath
);
2962 #endif /* WITH_FULL_PATHS */
2964 if (operation
== OP_DELETE
)
2966 if (S_ISDIR (src_stat
.st_mode
))
2967 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2969 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2973 temp
= transform_source (ctx
, source_with_path_str
);
2976 value
= transform_error
;
2979 char *temp2
, *temp3
, *repl_dest
;
2981 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2982 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2985 temp3
= source_with_path_str
;
2986 source_with_path_str
= strutils_shell_unescape (source_with_path_str
);
2989 temp2
= strutils_shell_unescape (temp2
);
2995 /* we use file_mask_op_follow_links only with OP_COPY */
2999 vpath
= vfs_path_from_str (source_with_path_str
);
3000 ctx
->stat_func (vpath
, &src_stat
);
3001 vfs_path_free (vpath
);
3003 if (S_ISDIR (src_stat
.st_mode
))
3004 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
,
3005 TRUE
, FALSE
, FALSE
, NULL
);
3007 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
3008 dest_dirs
= free_linklist (dest_dirs
);
3012 if (S_ISDIR (src_stat
.st_mode
))
3013 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
);
3015 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
3019 /* Unknown file operation */
3025 } /* Copy or move operation */
3027 if (value
== FILE_ABORT
)
3030 if (value
== FILE_CONT
)
3031 do_file_mark (panel
, i
, 0);
3033 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3035 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3036 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3039 if (operation
!= OP_DELETE
)
3040 file_progress_show (ctx
, 0, 0, "", FALSE
);
3042 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3046 } /* Loop for every file */
3048 } /* Many entries */
3052 if (save_cwd
!= NULL
)
3054 tmp_vpath
= vfs_path_from_str (save_cwd
);
3055 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3056 vfs_path_free (tmp_vpath
);
3060 if (save_dest
!= NULL
)
3062 tmp_vpath
= vfs_path_from_str (save_dest
);
3063 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3064 vfs_path_free (tmp_vpath
);
3068 linklist
= free_linklist (linklist
);
3069 dest_dirs
= free_linklist (dest_dirs
);
3070 #ifdef WITH_FULL_PATHS
3071 g_free (source_with_path_str
);
3072 vfs_path_free (source_with_vpath
);
3073 #endif /* WITH_FULL_PATHS */
3075 vfs_path_free (dest_vpath
);
3076 g_free (ctx
->dest_mask
);
3077 ctx
->dest_mask
= NULL
;
3079 #ifdef ENABLE_BACKGROUND
3080 /* Let our parent know we are saying bye bye */
3081 if (mc_global
.we_are_background
)
3083 int cur_pid
= getpid ();
3084 /* Send pid to parent with child context, it is fork and
3085 don't modify real parent ctx */
3087 parent_call ((void *) end_bg_process
, ctx
, 0);
3092 #endif /* ENABLE_BACKGROUND */
3094 file_op_total_context_destroy (tctx
);
3096 file_op_context_destroy (ctx
);
3104 /* --------------------------------------------------------------------------------------------- */
3105 /* {{{ Query/status report routines */
3106 /** Report error with one file */
3108 file_error (const char *format
, const char *file
)
3110 char buf
[BUF_MEDIUM
];
3112 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3114 return do_file_error (buf
);
3117 /* --------------------------------------------------------------------------------------------- */
3120 Cause emacs to enter folding mode for this file: