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
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 */
85 #include "midnight.h" /* current_panel */
91 /*** global variables ****************************************************************************/
93 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
94 const char *op_names
[3] = {
95 N_("DialogTitle|Copy"),
96 N_("DialogTitle|Move"),
97 N_("DialogTitle|Delete")
100 /*** file scope macro definitions ****************************************************************/
102 /* Hack: the vfs code should not rely on this */
103 #define WITH_FULL_PATHS 1
105 #define FILEOP_UPDATE_INTERVAL 2
106 #define FILEOP_STALLING_INTERVAL 4
108 /*** file scope type declarations ****************************************************************/
110 /* This is a hard link cache */
114 struct vfs_class
*vfs
;
122 /* Status of the destination file */
125 DEST_NONE
= 0, /* Not created */
126 DEST_SHORT
= 1, /* Created, not fully copied */
127 DEST_FULL
= 2 /* Created, fully copied */
131 * This array introduced to avoid translation problems. The former (op_names)
132 * is assumed to be nouns, suitable in dialog box titles; this one should
133 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
134 * (I don't use spaces around the words, because someday they could be
135 * dropped, when widgets get smarter)
138 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
139 static const char *op_names1
[] = {
140 N_("FileOperation|Copy"),
141 N_("FileOperation|Move"),
142 N_("FileOperation|Delete")
146 * These are formats for building a prompt. Parts encoded as follows:
147 * %o - operation from op_names1
148 * %f - file/files or files/directories, as appropriate
149 * %m - "with source mask" or question mark for delete
150 * %s - source name (truncated)
151 * %d - number of marked files
152 * %e - "to:" or question mark for delete
154 * xgettext:no-c-format */
155 static const char *one_format
= N_("%o %f \"%s\"%m");
156 /* xgettext:no-c-format */
157 static const char *many_format
= N_("%o %d %f%m");
159 static const char *prompt_parts
[] = {
164 N_("files/directories"),
165 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
166 N_(" with source mask:"),
170 static const char *question_format
= N_("%s?");
172 /*** file scope variables ************************************************************************/
174 /* the hard link cache */
175 static struct link
*linklist
= NULL
;
177 /* the files-to-be-erased list */
178 static struct link
*erase_list
;
181 * In copy_dir_dir we use two additional single linked lists: The first -
182 * variable name `parent_dirs' - holds information about already copied
183 * directories and is used to detect cyclic symbolic links.
184 * The second (`dest_dirs' below) holds information about just created
185 * target directories and is used to detect when an directory is copied
186 * into itself (we don't want to copy infinitly).
187 * Both lists don't use the linkcount and name structure members of struct
190 static struct link
*dest_dirs
= NULL
;
192 static FileProgressStatus transform_error
= FILE_CONT
;
194 /*** file scope functions ************************************************************************/
195 /* --------------------------------------------------------------------------------------------- */
198 transform_source (FileOpContext
* ctx
, const char *source
)
203 s
= g_strdup (source
);
205 /* We remove \n from the filename since regex routines would use \n as an anchor */
206 /* this is just to be allowed to maniupulate file names with \n on it */
207 for (q
= s
; *q
!= '\0'; q
++)
211 fnsource
= (char *) x_basename (s
);
213 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
214 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
218 transform_error
= FILE_SKIP
;
225 /* --------------------------------------------------------------------------------------------- */
228 free_linklist (struct link
**lc_linklist
)
230 struct link
*lp
, *lp2
;
232 for (lp
= *lc_linklist
; lp
!= NULL
; lp
= lp2
)
240 /* --------------------------------------------------------------------------------------------- */
243 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
246 vfs_path_element_t
*vpath_element
;
247 ino_t ino
= sb
->st_ino
;
248 dev_t dev
= sb
->st_dev
;
250 vpath
= vfs_path_from_str (path
);
251 vpath_element
= vfs_path_get_by_index (vpath
, -1);
255 if (lp
->vfs
== vpath_element
->class)
256 if (lp
->ino
== ino
&& lp
->dev
== dev
)
260 vfs_path_free (vpath
);
264 /* --------------------------------------------------------------------------------------------- */
266 * Check and made hardlink
268 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
269 * and a hardlink was succesfully made
273 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
276 vfs_path_t
*src_vpath
, *dst_vpath
;
278 struct vfs_class
*my_vfs
;
279 ino_t ino
= pstat
->st_ino
;
280 dev_t dev
= pstat
->st_dev
;
281 struct stat link_stat
;
284 src_vpath
= vfs_path_from_str (src_name
);
286 if ((vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
288 vfs_path_free (src_vpath
);
291 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
292 dst_vpath
= vfs_path_from_str (dst_name
);
294 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
295 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
)
297 struct vfs_class
*lp_name_class
;
299 vfs_path_t
*tmp_vpath
;
301 tmp_vpath
= vfs_path_from_str (lp
->name
);
302 lp_name_class
= vfs_path_get_by_index (tmp_vpath
, -1)->class;
303 stat_result
= mc_stat (tmp_vpath
, &link_stat
);
304 vfs_path_free (tmp_vpath
);
306 if (!stat_result
&& link_stat
.st_ino
== ino
307 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
309 struct vfs_class
*p_class
, *dst_name_class
;
311 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
314 dst_name_class
= vfs_path_get_by_index (dst_vpath
, -1)->class;
316 tmp_vpath
= vfs_path_from_str (p
);
317 p_class
= vfs_path_get_by_index (tmp_vpath
, -1)->class;
319 if (dst_name_class
== p_class
)
321 if (!mc_stat (tmp_vpath
, &link_stat
))
323 if (!mc_link (tmp_vpath
, dst_vpath
))
325 vfs_path_free (tmp_vpath
);
326 vfs_path_free (src_vpath
);
327 vfs_path_free (dst_vpath
);
332 vfs_path_free (tmp_vpath
);
335 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
336 vfs_path_free (src_vpath
);
337 vfs_path_free (dst_vpath
);
340 lp
= (struct link
*) g_try_malloc (sizeof (struct link
) + strlen (src_name
)
341 + strlen (dst_name
) + 1);
348 strcpy (lp
->name
, src_name
);
349 lpdstname
= lp
->name
+ strlen (lp
->name
) + 1;
350 strcpy (lpdstname
, dst_name
);
354 vfs_path_free (src_vpath
);
355 vfs_path_free (dst_vpath
);
359 /* --------------------------------------------------------------------------------------------- */
361 * Duplicate the contents of the symbolic link src_path in dst_path.
362 * Try to make a stable symlink if the option "stable symlink" was
363 * set in the file mask dialog.
364 * If dst_path is an existing symlink it will be deleted silently
365 * (upper levels take already care of existing files at dst_path).
368 static FileProgressStatus
369 make_symlink (FileOpContext
* ctx
, const char *src_path
, const char *dst_path
)
371 char link_target
[MC_MAXPATHLEN
];
373 FileProgressStatus return_status
;
375 vfs_path_t
*src_vpath
;
376 vfs_path_t
*dst_vpath
;
377 gboolean dst_is_symlink
;
378 vfs_path_t
*link_target_vpath
= NULL
;
380 src_vpath
= vfs_path_from_str (src_path
);
381 dst_vpath
= vfs_path_from_str (dst_path
);
382 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
385 len
= mc_readlink (src_vpath
, link_target
, MC_MAXPATHLEN
- 1);
389 return_status
= FILE_SKIPALL
;
392 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
393 if (return_status
== FILE_SKIPALL
)
394 ctx
->skip_all
= TRUE
;
395 if (return_status
== FILE_RETRY
)
396 goto retry_src_readlink
;
400 link_target
[len
] = 0;
402 if (ctx
->stable_symlinks
)
405 if (!vfs_file_is_local (src_vpath
) || !vfs_file_is_local (dst_vpath
))
407 message (D_ERROR
, MSG_ERROR
,
408 _("Cannot make stable symlinks across"
409 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
410 ctx
->stable_symlinks
= FALSE
;
414 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
419 const char *r
= strrchr (src_path
, PATH_SEP
);
423 p
= g_strndup (src_path
, r
- src_path
+ 1);
424 if (g_path_is_absolute (dst_path
))
425 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
427 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
429 if (vfs_path_tokens_count (q
) > 1)
431 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
433 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
434 s
= g_strconcat (p
, link_target
, (char *) NULL
);
436 g_strlcpy (link_target
, s
, sizeof (link_target
));
438 tmp_vpath2
= vfs_path_from_str (link_target
);
439 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
440 vfs_path_free (tmp_vpath1
);
441 vfs_path_free (tmp_vpath2
);
444 g_strlcpy (link_target
, s
, sizeof (link_target
));
453 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
456 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
459 return_status
= FILE_CONT
;
463 * if dst_exists, it is obvious that this had failed.
464 * We can delete the old symlink and try again...
468 if (mc_unlink (dst_vpath
) == 0)
469 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
472 return_status
= FILE_CONT
;
477 return_status
= FILE_SKIPALL
;
480 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
481 if (return_status
== FILE_SKIPALL
)
482 ctx
->skip_all
= TRUE
;
483 if (return_status
== FILE_RETRY
)
484 goto retry_dst_symlink
;
488 vfs_path_free (src_vpath
);
489 vfs_path_free (dst_vpath
);
490 vfs_path_free (link_target_vpath
);
491 return return_status
;
494 /* --------------------------------------------------------------------------------------------- */
496 static FileProgressStatus
497 progress_update_one (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, off_t add
)
499 struct timeval tv_current
;
500 static struct timeval tv_start
= { };
502 tctx
->progress_count
++;
503 tctx
->progress_bytes
+= (uintmax_t) add
;
505 if (tv_start
.tv_sec
== 0)
507 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
509 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
510 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
512 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
514 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
515 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
517 tv_start
.tv_sec
= tv_current
.tv_sec
;
520 return check_progress_buttons (ctx
);
523 /* --------------------------------------------------------------------------------------------- */
525 static FileProgressStatus
526 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
530 const char *head_msg
;
532 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
534 msg
= g_strdup_printf (fmt
, a
, b
);
535 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
539 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
542 /* --------------------------------------------------------------------------------------------- */
544 static FileProgressStatus
545 warn_same_file (const char *fmt
, const char *a
, const char *b
)
547 #ifdef ENABLE_BACKGROUND
552 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
553 const char *a
, const char *b
);
557 pntr
.f
= real_warn_same_file
;
559 if (mc_global
.we_are_background
)
560 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
562 return real_warn_same_file (Foreground
, fmt
, a
, b
);
565 /* --------------------------------------------------------------------------------------------- */
566 /* {{{ Query/status report routines */
568 static FileProgressStatus
569 real_do_file_error (enum OperationMode mode
, const char *error
)
574 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
576 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
598 /* --------------------------------------------------------------------------------------------- */
600 static FileProgressStatus
601 real_query_recursive (FileOpContext
* ctx
, enum OperationMode mode
, const char *s
)
605 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
607 const char *msg
= mode
== Foreground
608 ? _("\nDirectory not empty.\nDelete it recursively?")
609 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
610 text
= g_strconcat (_("Delete:"), " ", path_trunc (s
, 30), (char *) NULL
);
615 ctx
->recursive_result
=
616 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
617 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
619 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
624 switch (ctx
->recursive_result
)
627 case RECURSIVE_ALWAYS
:
631 case RECURSIVE_NEVER
:
634 case RECURSIVE_ABORT
:
640 /* --------------------------------------------------------------------------------------------- */
642 #ifdef ENABLE_BACKGROUND
643 static FileProgressStatus
644 do_file_error (const char *str
)
649 FileProgressStatus (*f
) (enum OperationMode
, const char *);
651 pntr
.f
= real_do_file_error
;
653 if (mc_global
.we_are_background
)
654 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
656 return real_do_file_error (Foreground
, str
);
659 /* --------------------------------------------------------------------------------------------- */
661 static FileProgressStatus
662 query_recursive (FileOpContext
* ctx
, const char *s
)
667 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
669 pntr
.f
= real_query_recursive
;
671 if (mc_global
.we_are_background
)
672 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
674 return real_query_recursive (ctx
, Foreground
, s
);
677 /* --------------------------------------------------------------------------------------------- */
679 static FileProgressStatus
680 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
681 struct stat
*_d_stat
)
686 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
687 struct stat
*, struct stat
*);
689 pntr
.f
= file_progress_real_query_replace
;
691 if (mc_global
.we_are_background
)
692 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
693 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
695 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
699 /* --------------------------------------------------------------------------------------------- */
701 static FileProgressStatus
702 do_file_error (const char *str
)
704 return real_do_file_error (Foreground
, str
);
707 /* --------------------------------------------------------------------------------------------- */
709 static FileProgressStatus
710 query_recursive (FileOpContext
* ctx
, const char *s
)
712 return real_query_recursive (ctx
, Foreground
, s
);
715 /* --------------------------------------------------------------------------------------------- */
717 static FileProgressStatus
718 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
719 struct stat
*_d_stat
)
721 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
724 #endif /* !ENABLE_BACKGROUND */
726 /* --------------------------------------------------------------------------------------------- */
727 /** Report error with two files */
729 static FileProgressStatus
730 files_error (const char *format
, const char *file1
, const char *file2
)
732 char buf
[BUF_MEDIUM
];
733 char *nfile1
= g_strdup (path_trunc (file1
, 15));
734 char *nfile2
= g_strdup (path_trunc (file2
, 15));
736 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
741 return do_file_error (buf
);
746 /* --------------------------------------------------------------------------------------------- */
749 copy_file_file_display_progress (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
750 struct timeval tv_current
, struct timeval tv_transfer_start
,
751 off_t file_size
, off_t n_read_total
)
755 /* 1. Update rotating dash after some time */
759 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
761 if (n_read_total
== 0)
765 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
766 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
769 /* 4. Compute BPS rate */
770 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
771 if (ctx
->bps_time
< 1)
773 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
775 /* 5. Compute total ETA and BPS */
776 if (ctx
->progress_bytes
!= 0)
778 uintmax_t remain_bytes
;
780 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
783 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
788 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
789 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
792 /* broken on lot of little files */
794 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
795 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
800 /* --------------------------------------------------------------------------------------------- */
802 /* {{{ Move routines */
803 static FileProgressStatus
804 move_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
806 struct stat src_stats
, dst_stats
;
807 FileProgressStatus return_status
= FILE_CONT
;
808 gboolean copy_done
= FALSE
;
809 gboolean old_ask_overwrite
;
810 vfs_path_t
*src_vpath
, *dst_vpath
;
812 file_progress_show_source (ctx
, s
);
813 file_progress_show_target (ctx
, d
);
814 if (check_progress_buttons (ctx
) == FILE_ABORT
)
818 src_vpath
= vfs_path_from_str (s
);
819 dst_vpath
= vfs_path_from_str (d
);
821 while (mc_lstat (src_vpath
, &src_stats
) != 0)
823 /* Source doesn't exist */
825 return_status
= FILE_SKIPALL
;
828 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
829 if (return_status
== FILE_SKIPALL
)
830 ctx
->skip_all
= TRUE
;
832 if (return_status
!= FILE_RETRY
)
834 vfs_path_free (src_vpath
);
835 vfs_path_free (dst_vpath
);
836 return return_status
;
840 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
842 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
843 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
845 if (S_ISDIR (dst_stats
.st_mode
))
847 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
849 vfs_path_free (src_vpath
);
850 vfs_path_free (dst_vpath
);
854 if (confirm_overwrite
)
856 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
857 if (return_status
!= FILE_CONT
)
859 vfs_path_free (src_vpath
);
860 vfs_path_free (dst_vpath
);
861 return return_status
;
864 /* Ok to overwrite */
869 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
871 return_status
= make_symlink (ctx
, s
, d
);
872 if (return_status
== FILE_CONT
)
873 goto retry_src_remove
;
876 vfs_path_free (src_vpath
);
877 vfs_path_free (dst_vpath
);
878 return return_status
;
882 if (mc_rename (src_vpath
, dst_vpath
) == 0)
884 vfs_path_free (src_vpath
);
885 vfs_path_free (dst_vpath
);
886 return progress_update_one (tctx
, ctx
, src_stats
.st_size
);
890 /* Comparison to EXDEV seems not to work in nfs if you're moving from
891 one nfs to the same, but on the server it is on two different
892 filesystems. Then nfs returns EIO instead of EXDEV.
893 Hope it will not hurt if we always in case of error try to copy/delete. */
895 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
900 return_status
= FILE_SKIPALL
;
903 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
904 if (return_status
== FILE_SKIPALL
)
905 ctx
->skip_all
= TRUE
;
906 if (return_status
== FILE_RETRY
)
909 vfs_path_free (src_vpath
);
910 vfs_path_free (dst_vpath
);
912 return return_status
;
916 /* Failed because filesystem boundary -> copy the file instead */
917 old_ask_overwrite
= tctx
->ask_overwrite
;
918 tctx
->ask_overwrite
= FALSE
;
919 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
920 tctx
->ask_overwrite
= old_ask_overwrite
;
921 if (return_status
!= FILE_CONT
)
923 vfs_path_free (src_vpath
);
924 vfs_path_free (dst_vpath
);
925 return return_status
;
930 file_progress_show_source (ctx
, NULL
);
931 file_progress_show (ctx
, 0, 0, "", FALSE
);
933 return_status
= check_progress_buttons (ctx
);
934 if (return_status
!= FILE_CONT
)
936 vfs_path_free (src_vpath
);
937 vfs_path_free (dst_vpath
);
938 return return_status
;
944 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
946 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
947 if (return_status
== FILE_RETRY
)
948 goto retry_src_remove
;
949 if (return_status
== FILE_SKIPALL
)
950 ctx
->skip_all
= TRUE
;
952 vfs_path_free (src_vpath
);
953 vfs_path_free (dst_vpath
);
954 return return_status
;
958 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
960 vfs_path_free (src_vpath
);
961 vfs_path_free (dst_vpath
);
963 return return_status
;
968 /* --------------------------------------------------------------------------------------------- */
969 /* {{{ Erase routines */
970 /** Don't update progress status if progress_count==NULL */
972 static FileProgressStatus
973 erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
977 vfs_path_t
*vpath
= vfs_path_from_str (s
);
979 file_progress_show_deleting (ctx
, s
);
980 if (check_progress_buttons (ctx
) == FILE_ABORT
)
984 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
986 /* ignore, most likely the mc_unlink fails, too */
990 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
992 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), s
);
993 if (return_status
== FILE_ABORT
)
995 vfs_path_free (vpath
);
996 return return_status
;
998 if (return_status
== FILE_RETRY
)
1000 if (return_status
== FILE_SKIPALL
)
1001 ctx
->skip_all
= TRUE
;
1005 vfs_path_free (vpath
);
1006 if (tctx
->progress_count
== 0)
1008 return progress_update_one (tctx
, ctx
, buf
.st_size
);
1011 /* --------------------------------------------------------------------------------------------- */
1014 Recursive remove of files
1016 skip ->warn every level, gets default
1017 skipall->remove as much as possible
1019 static FileProgressStatus
1020 recursive_erase (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
1022 struct dirent
*next
;
1026 FileProgressStatus return_status
= FILE_CONT
;
1029 if (strcmp (s
, "..") == 0)
1032 vpath
= vfs_path_from_str (s
);
1033 reading
= mc_opendir (vpath
);
1035 if (reading
== NULL
)
1037 return_status
= FILE_RETRY
;
1041 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1043 vfs_path_t
*tmp_vpath
;
1045 if (!strcmp (next
->d_name
, "."))
1047 if (!strcmp (next
->d_name
, ".."))
1049 path
= concat_dir_and_file (s
, next
->d_name
);
1050 tmp_vpath
= vfs_path_from_str (path
);
1051 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1054 mc_closedir (reading
);
1055 vfs_path_free (tmp_vpath
);
1056 return_status
= FILE_RETRY
;
1059 if (S_ISDIR (buf
.st_mode
))
1060 return_status
= recursive_erase (tctx
, ctx
, path
);
1062 return_status
= erase_file (tctx
, ctx
, path
);
1063 vfs_path_free (tmp_vpath
);
1066 mc_closedir (reading
);
1067 if (return_status
== FILE_ABORT
)
1070 file_progress_show_deleting (ctx
, s
);
1071 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1073 return_status
= FILE_ABORT
;
1078 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1080 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1081 if (return_status
== FILE_RETRY
)
1083 if (return_status
== FILE_ABORT
)
1085 if (return_status
== FILE_SKIPALL
)
1086 ctx
->skip_all
= TRUE
;
1091 vfs_path_free (vpath
);
1092 return return_status
;
1095 /* --------------------------------------------------------------------------------------------- */
1096 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1097 in the directory path points to, 0 else. */
1100 check_dir_is_empty (const char *path
)
1105 vfs_path_t
*vpath
= vfs_path_from_str (path
);
1107 dir
= mc_opendir (vpath
);
1110 vfs_path_free (vpath
);
1114 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
))
1116 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1117 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1118 continue; /* "." or ".." */
1124 vfs_path_free (vpath
);
1128 /* --------------------------------------------------------------------------------------------- */
1130 static FileProgressStatus
1131 erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
)
1133 FileProgressStatus error
;
1135 if (strcmp (s
, "..") == 0)
1138 if (strcmp (s
, ".") == 0)
1141 file_progress_show_deleting (ctx
, s
);
1142 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1146 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1149 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1151 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1152 if (error
== FILE_SKIPALL
)
1153 ctx
->skip_all
= TRUE
;
1154 if (error
!= FILE_RETRY
)
1163 /* --------------------------------------------------------------------------------------------- */
1164 /* {{{ Panel operate routines */
1167 * Return currently selected entry name or the name of the first marked
1168 * entry if there is one.
1172 panel_get_file (WPanel
* panel
)
1174 if (get_current_type () == view_tree
)
1178 tree
= (WTree
*) get_panel_widget (get_current_index ());
1179 return vfs_path_to_str (tree_selected_name (tree
));
1182 if (panel
->marked
!= 0)
1186 for (i
= 0; i
< panel
->count
; i
++)
1187 if (panel
->dir
.list
[i
].f
.marked
)
1188 return g_strdup (panel
->dir
.list
[i
].fname
);
1190 return g_strdup (panel
->dir
.list
[panel
->selected
].fname
);
1193 /* --------------------------------------------------------------------------------------------- */
1195 * panel_compute_totals:
1197 * compute the number of files and the number of bytes
1198 * used up by the whole selection, recursing directories
1199 * as required. In addition, it checks to see if it will
1200 * overwrite any files by doing the copy.
1203 static FileProgressStatus
1204 panel_compute_totals (const WPanel
* panel
, const void *ui
,
1205 compute_dir_size_callback cback
,
1206 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
1213 for (i
= 0; i
< panel
->count
; i
++)
1217 if (!panel
->dir
.list
[i
].f
.marked
)
1220 s
= &panel
->dir
.list
[i
].st
;
1222 if (S_ISDIR (s
->st_mode
))
1225 size_t subdir_count
= 0;
1226 uintmax_t subdir_bytes
= 0;
1227 FileProgressStatus status
;
1229 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, NULL
);
1231 compute_dir_size (p
, ui
, cback
, &subdir_count
, &subdir_bytes
, compute_symlinks
);
1234 if (status
!= FILE_CONT
)
1237 *ret_marked
+= subdir_count
;
1238 *ret_total
+= subdir_bytes
;
1243 *ret_total
+= (uintmax_t) s
->st_size
;
1250 /* --------------------------------------------------------------------------------------------- */
1252 /** Initialize variables for progress bars */
1253 static FileProgressStatus
1254 panel_operate_init_totals (FileOperation operation
,
1255 const WPanel
* panel
, const char *source
, FileOpContext
* ctx
)
1257 FileProgressStatus status
;
1259 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
)
1261 ComputeDirSizeUI
*ui
;
1263 ui
= compute_dir_size_create_ui ();
1266 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1267 &ctx
->progress_count
, &ctx
->progress_bytes
,
1273 p
= vfs_path_from_str (source
);
1274 status
= compute_dir_size (p
, ui
, compute_dir_size_update_ui
,
1275 &ctx
->progress_count
, &ctx
->progress_bytes
,
1280 compute_dir_size_destroy_ui (ui
);
1282 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1287 ctx
->progress_count
= panel
->marked
;
1288 ctx
->progress_bytes
= panel
->total
;
1289 ctx
->progress_totals_computed
= FALSE
;
1295 /* --------------------------------------------------------------------------------------------- */
1297 * Generate user prompt for panel operation.
1298 * single_source is the name if the source entry or NULL for multiple
1300 * src_stat is only used when single_source is not NULL.
1304 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1305 gboolean single_source
, const struct stat
*src_stat
)
1307 const char *sp
, *cp
;
1308 char format_string
[BUF_MEDIUM
];
1309 char *dp
= format_string
;
1310 gboolean build_question
= FALSE
;
1312 static gboolean i18n_flag
= FALSE
;
1317 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1318 op_names1
[i
] = Q_ (op_names1
[i
]);
1321 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1322 prompt_parts
[i
] = _(prompt_parts
[i
]);
1324 one_format
= _(one_format
);
1325 many_format
= _(many_format
);
1326 question_format
= _(question_format
);
1327 #endif /* ENABLE_NLS */
1331 sp
= single_source
? one_format
: many_format
;
1342 cp
= op_names1
[operation
];
1345 if (operation
== OP_DELETE
)
1348 build_question
= TRUE
;
1351 cp
= prompt_parts
[5];
1354 if (operation
== OP_DELETE
)
1357 build_question
= TRUE
;
1360 cp
= prompt_parts
[6];
1364 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1366 cp
= (panel
->marked
== panel
->dirs_marked
)
1368 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1389 char tmp
[BUF_MEDIUM
];
1391 memmove (tmp
, format_string
, sizeof (tmp
));
1392 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1395 return g_strdup (format_string
);
1398 /* --------------------------------------------------------------------------------------------- */
1400 #ifdef ENABLE_BACKGROUND
1402 end_bg_process (FileOpContext
* ctx
, enum OperationMode mode
)
1409 unregister_task_with_pid (pid
);
1410 /* file_op_context_destroy(ctx); */
1416 /* --------------------------------------------------------------------------------------------- */
1417 /*** public functions ****************************************************************************/
1418 /* --------------------------------------------------------------------------------------------- */
1421 copy_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
1422 const char *src_path
, const char *dst_path
)
1424 uid_t src_uid
= (uid_t
) - 1;
1425 gid_t src_gid
= (gid_t
) - 1;
1427 int src_desc
, dest_desc
= -1;
1428 int n_read
, n_written
;
1429 mode_t src_mode
= 0; /* The mode of the source file */
1430 struct stat sb
, sb2
;
1432 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1433 off_t file_size
= -1;
1434 FileProgressStatus return_status
, temp_status
;
1435 struct timeval tv_transfer_start
;
1436 dest_status_t dst_status
= DEST_NONE
;
1438 gboolean is_first_time
= TRUE
;
1439 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1441 /* FIXME: We should not be using global variables! */
1443 return_status
= FILE_RETRY
;
1445 file_progress_show_source (ctx
, src_path
);
1446 file_progress_show_target (ctx
, dst_path
);
1447 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1452 dst_vpath
= vfs_path_from_str (dst_path
);
1453 while (mc_stat (dst_vpath
, &sb2
) == 0)
1455 if (S_ISDIR (sb2
.st_mode
))
1458 return_status
= FILE_SKIPALL
;
1461 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1462 if (return_status
== FILE_SKIPALL
)
1463 ctx
->skip_all
= TRUE
;
1464 if (return_status
== FILE_RETRY
)
1473 src_vpath
= vfs_path_from_str (src_path
);
1474 while ((*ctx
->stat_func
) (src_vpath
, &sb
) != 0)
1477 return_status
= FILE_SKIPALL
;
1480 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1481 if (return_status
== FILE_SKIPALL
)
1482 ctx
->skip_all
= TRUE
;
1484 if (return_status
!= FILE_RETRY
)
1490 /* Destination already exists */
1491 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1493 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1494 src_path
, dst_path
);
1497 /* Should we replace destination? */
1498 if (tctx
->ask_overwrite
)
1501 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1502 if (return_status
!= FILE_CONT
)
1507 if (!ctx
->do_append
)
1509 /* Check the hardlinks */
1510 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_path
, dst_path
, &sb
))
1512 /* We have made a hardlink - no more processing is necessary */
1513 return_status
= FILE_CONT
;
1517 if (S_ISLNK (sb
.st_mode
))
1519 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1523 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1524 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1526 while (mc_mknod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0
1529 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1530 if (return_status
== FILE_RETRY
)
1532 if (return_status
== FILE_SKIPALL
)
1533 ctx
->skip_all
= TRUE
;
1538 while (ctx
->preserve_uidgid
&& mc_chown (dst_vpath
, sb
.st_uid
, sb
.st_gid
) != 0
1541 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1542 if (temp_status
== FILE_SKIP
)
1544 if (temp_status
== FILE_SKIPALL
)
1545 ctx
->skip_all
= TRUE
;
1546 if (temp_status
!= FILE_RETRY
)
1548 return_status
= temp_status
;
1553 while (ctx
->preserve
&& mc_chmod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
) != 0
1556 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1557 if (temp_status
== FILE_SKIP
)
1559 if (temp_status
== FILE_SKIPALL
)
1560 ctx
->skip_all
= TRUE
;
1561 if (temp_status
!= FILE_RETRY
)
1563 return_status
= temp_status
;
1568 return_status
= FILE_CONT
;
1573 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1575 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1577 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1578 if (return_status
== FILE_RETRY
)
1580 if (return_status
== FILE_SKIPALL
)
1581 ctx
->skip_all
= TRUE
;
1582 if (return_status
== FILE_SKIP
)
1588 if (ctx
->do_reget
!= 0)
1590 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1592 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1594 ctx
->do_append
= FALSE
;
1598 while (mc_fstat (src_desc
, &sb
) != 0)
1601 return_status
= FILE_SKIPALL
;
1604 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1605 if (return_status
== FILE_RETRY
)
1607 if (return_status
== FILE_SKIPALL
)
1608 ctx
->skip_all
= TRUE
;
1609 ctx
->do_append
= FALSE
;
1613 src_mode
= sb
.st_mode
;
1614 src_uid
= sb
.st_uid
;
1615 src_gid
= sb
.st_gid
;
1616 utb
.actime
= sb
.st_atime
;
1617 utb
.modtime
= sb
.st_mtime
;
1618 file_size
= sb
.st_size
;
1620 open_flags
= O_WRONLY
;
1623 if (ctx
->do_append
!= 0)
1624 open_flags
|= O_APPEND
;
1626 open_flags
|= O_CREAT
| O_TRUNC
;
1630 open_flags
|= O_CREAT
| O_EXCL
;
1633 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1635 if (errno
!= EEXIST
)
1638 return_status
= FILE_SKIPALL
;
1641 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1642 if (return_status
== FILE_RETRY
)
1644 if (return_status
== FILE_SKIPALL
)
1645 ctx
->skip_all
= TRUE
;
1646 ctx
->do_append
= FALSE
;
1651 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1653 appending
= ctx
->do_append
;
1654 ctx
->do_append
= FALSE
;
1656 /* Find out the optimal buffer size. */
1657 while (mc_fstat (dest_desc
, &sb
) != 0)
1660 return_status
= FILE_SKIPALL
;
1663 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1664 if (return_status
== FILE_RETRY
)
1666 if (return_status
== FILE_SKIPALL
)
1667 ctx
->skip_all
= TRUE
;
1674 errno
= vfs_preallocate (dest_desc
, file_size
, (ctx
->do_append
!= 0) ? sb
.st_size
: 0);
1679 return_status
= FILE_SKIPALL
;
1683 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1684 if (return_status
== FILE_RETRY
)
1686 if (return_status
== FILE_SKIPALL
)
1687 ctx
->skip_all
= TRUE
;
1689 mc_close (dest_desc
);
1691 mc_unlink (dst_vpath
);
1692 dst_status
= DEST_NONE
;
1696 ctx
->eta_secs
= 0.0;
1699 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1700 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1702 file_progress_show (ctx
, 1, 1, "", TRUE
);
1703 return_status
= check_progress_buttons (ctx
);
1706 if (return_status
!= FILE_CONT
)
1710 off_t n_read_total
= 0;
1711 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1712 int secs
, update_secs
;
1713 const char *stalled_msg
= "";
1715 tv_last_update
= tv_transfer_start
;
1722 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1725 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0 && !ctx
->skip_all
)
1727 return_status
= file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
1728 if (return_status
== FILE_RETRY
)
1730 if (return_status
== FILE_SKIPALL
)
1731 ctx
->skip_all
= TRUE
;
1737 gettimeofday (&tv_current
, NULL
);
1742 n_read_total
+= n_read
;
1744 /* Windows NT ftp servers report that files have no
1745 * permissions: -------, so if we happen to have actually
1746 * read something, we should fix the permissions.
1748 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1749 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1750 gettimeofday (&tv_last_input
, NULL
);
1753 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
&& !ctx
->skip_all
)
1757 n_read
-= n_written
;
1761 return_status
= file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1762 if (return_status
== FILE_SKIP
)
1764 if (return_status
== FILE_SKIPALL
)
1765 ctx
->skip_all
= TRUE
;
1766 if (return_status
!= FILE_RETRY
)
1771 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1773 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1774 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1776 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1778 copy_file_file_display_progress (tctx
, ctx
,
1780 tv_transfer_start
, file_size
, n_read_total
);
1781 tv_last_update
= tv_current
;
1783 is_first_time
= FALSE
;
1785 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1787 stalled_msg
= _("(stalled)");
1791 gboolean force_update
;
1794 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1796 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1798 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1799 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1802 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1807 return_status
= check_progress_buttons (ctx
);
1809 if (return_status
!= FILE_CONT
)
1817 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1820 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1822 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1823 if (temp_status
== FILE_RETRY
)
1825 if (temp_status
== FILE_ABORT
)
1826 return_status
= temp_status
;
1827 if (temp_status
== FILE_SKIPALL
)
1828 ctx
->skip_all
= TRUE
;
1832 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1834 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1835 if (temp_status
== FILE_RETRY
)
1837 if (temp_status
== FILE_SKIPALL
)
1838 ctx
->skip_all
= TRUE
;
1839 return_status
= temp_status
;
1843 if (dst_status
== DEST_SHORT
)
1845 /* Remove short file */
1848 result
= query_dialog (Q_ ("DialogTitle|Copy"),
1849 _("Incomplete file was retrieved. Keep it?"),
1850 D_ERROR
, 2, _("&Delete"), _("&Keep"));
1852 mc_unlink (dst_vpath
);
1854 else if (dst_status
== DEST_FULL
)
1856 /* Copy has succeeded */
1857 if (!appending
&& ctx
->preserve_uidgid
)
1859 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1861 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1862 if (temp_status
== FILE_RETRY
)
1864 if (temp_status
== FILE_SKIPALL
)
1866 ctx
->skip_all
= TRUE
;
1867 return_status
= FILE_CONT
;
1869 if (temp_status
== FILE_SKIP
)
1870 return_status
= FILE_CONT
;
1879 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1881 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1882 if (temp_status
== FILE_RETRY
)
1884 if (temp_status
== FILE_SKIPALL
)
1886 ctx
->skip_all
= TRUE
;
1887 return_status
= FILE_CONT
;
1889 if (temp_status
== FILE_SKIP
)
1890 return_status
= FILE_CONT
;
1894 else if (!dst_exists
)
1896 src_mode
= umask (-1);
1898 src_mode
= 0100666 & ~src_mode
;
1899 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
1901 mc_utime (dst_vpath
, &utb
);
1905 if (return_status
== FILE_CONT
)
1906 return_status
= progress_update_one (tctx
, ctx
, file_size
);
1909 vfs_path_free (src_vpath
);
1910 vfs_path_free (dst_vpath
);
1911 return return_status
;
1914 /* --------------------------------------------------------------------------------------------- */
1916 * I think these copy_*_* functions should have a return type.
1917 * anyway, this function *must* have two directories as arguments.
1919 /* FIXME: This function needs to check the return values of the
1923 copy_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *_d
,
1924 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, struct link
* parent_dirs
)
1926 struct dirent
*next
;
1927 struct stat buf
, cbuf
;
1929 char *dest_dir
= NULL
;
1930 FileProgressStatus return_status
= FILE_CONT
;
1934 vfs_path_t
*src_vpath
, *dst_vpath
, *dest_dir_vpath
= NULL
;
1938 src_vpath
= vfs_path_from_str (s
);
1939 dst_vpath
= vfs_path_from_str (_d
);
1941 /* First get the mode of the source dir */
1944 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
1947 return_status
= FILE_SKIPALL
;
1950 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
1951 if (return_status
== FILE_RETRY
)
1952 goto retry_src_stat
;
1953 if (return_status
== FILE_SKIPALL
)
1954 ctx
->skip_all
= TRUE
;
1959 if (is_in_linklist (dest_dirs
, s
, &cbuf
))
1961 /* Don't copy a directory we created before (we don't want to copy
1962 infinitely if a directory is copied into itself) */
1963 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1964 return_status
= FILE_CONT
;
1968 /* Hmm, hardlink to directory??? - Norbert */
1969 /* FIXME: In this step we should do something
1970 in case the destination already exist */
1971 /* Check the hardlinks */
1972 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (s
, d
, &cbuf
))
1974 /* We have made a hardlink - no more processing is necessary */
1978 if (!S_ISDIR (cbuf
.st_mode
))
1981 return_status
= FILE_SKIPALL
;
1984 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
1985 if (return_status
== FILE_RETRY
)
1986 goto retry_src_stat
;
1987 if (return_status
== FILE_SKIPALL
)
1988 ctx
->skip_all
= TRUE
;
1993 if (is_in_linklist (parent_dirs
, s
, &cbuf
))
1995 /* we found a cyclic symbolic link */
1996 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
1997 return_status
= FILE_SKIP
;
2001 lp
= g_new (struct link
, 1);
2002 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
2003 lp
->ino
= cbuf
.st_ino
;
2004 lp
->dev
= cbuf
.st_dev
;
2005 lp
->next
= parent_dirs
;
2009 /* Now, check if the dest dir exists, if not, create it. */
2010 if (mc_stat (dst_vpath
, &buf
) != 0)
2012 /* Here the dir doesn't exist : make it ! */
2015 if (mc_rename (src_vpath
, dst_vpath
) == 0)
2017 return_status
= FILE_CONT
;
2027 * If the destination directory exists, we want to copy the whole
2028 * directory, but we only want this to happen once.
2030 * Escape sequences added to the * to compiler warnings.
2031 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2032 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2034 if (!S_ISDIR (buf
.st_mode
))
2037 return_status
= FILE_SKIPALL
;
2040 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2041 if (return_status
== FILE_SKIPALL
)
2042 ctx
->skip_all
= TRUE
;
2043 if (return_status
== FILE_RETRY
)
2044 goto retry_dst_stat
;
2048 /* Dive into subdir if exists */
2049 if (toplevel
&& ctx
->dive_into_subdirs
)
2051 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
2060 dest_dir_vpath
= vfs_path_from_str (dest_dir
);
2061 while (my_mkdir (dest_dir_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2064 return_status
= FILE_SKIPALL
;
2067 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir
);
2068 if (return_status
== FILE_SKIPALL
)
2069 ctx
->skip_all
= TRUE
;
2071 if (return_status
!= FILE_RETRY
)
2075 lp
= g_new0 (struct link
, 1);
2076 mc_stat (dest_dir_vpath
, &buf
);
2077 lp
->vfs
= vfs_path_get_by_index (dest_dir_vpath
, -1)->class;
2078 lp
->ino
= buf
.st_ino
;
2079 lp
->dev
= buf
.st_dev
;
2080 lp
->next
= dest_dirs
;
2083 if (ctx
->preserve_uidgid
)
2085 while (mc_chown (dest_dir_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2088 return_status
= FILE_SKIPALL
;
2092 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir
);
2093 if (return_status
== FILE_SKIPALL
)
2094 ctx
->skip_all
= TRUE
;
2096 if (return_status
!= FILE_RETRY
)
2102 /* open the source dir for reading */
2103 reading
= mc_opendir (src_vpath
);
2104 if (reading
== NULL
)
2107 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2110 vfs_path_t
*tmp_vpath
;
2112 * Now, we don't want '.' and '..' to be created / copied at any time
2114 if (!strcmp (next
->d_name
, "."))
2116 if (!strcmp (next
->d_name
, ".."))
2119 /* get the filename and add it to the src directory */
2120 path
= concat_dir_and_file (s
, next
->d_name
);
2121 tmp_vpath
= vfs_path_from_str (path
);
2123 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2124 if (S_ISDIR (buf
.st_mode
))
2128 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
2130 * From here, we just intend to recursively copy subdirs, not
2131 * the double functionality of copying different when the target
2132 * dir already exists. So, we give the recursive call the flag 0
2133 * meaning no toplevel.
2136 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2143 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
2144 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2147 if (do_delete
&& return_status
== FILE_CONT
)
2149 if (ctx
->erase_at_end
)
2151 static struct link
*tail
;
2152 size_t len
= strlen (path
);
2153 lp
= g_malloc (sizeof (struct link
) + len
);
2154 strncpy (lp
->name
, path
, len
+ 1);
2155 lp
->st_mode
= buf
.st_mode
;
2157 if (erase_list
!= NULL
)
2163 erase_list
= tail
= lp
;
2167 if (S_ISDIR (buf
.st_mode
))
2169 return_status
= erase_dir_iff_empty (ctx
, path
);
2172 return_status
= erase_file (tctx
, ctx
, path
);
2176 vfs_path_free (tmp_vpath
);
2178 mc_closedir (reading
);
2182 mc_chmod (dest_dir_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2183 utb
.actime
= cbuf
.st_atime
;
2184 utb
.modtime
= cbuf
.st_mtime
;
2185 mc_utime (dest_dir_vpath
, &utb
);
2189 cbuf
.st_mode
= umask (-1);
2190 umask (cbuf
.st_mode
);
2191 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2192 mc_chmod (dest_dir_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2197 vfs_path_free (dest_dir_vpath
);
2198 g_free (parent_dirs
);
2201 vfs_path_free (src_vpath
);
2202 vfs_path_free (dst_vpath
);
2203 return return_status
;
2208 /* --------------------------------------------------------------------------------------------- */
2209 /* {{{ Move routines */
2212 move_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
2214 struct stat sbuf
, dbuf
, destbuf
;
2217 FileProgressStatus return_status
;
2218 gboolean move_over
= FALSE
;
2220 vfs_path_t
*src_vpath
, *dst_vpath
, *destdir_vpath
;
2222 src_vpath
= vfs_path_from_str (s
);
2223 dst_vpath
= vfs_path_from_str (d
);
2225 file_progress_show_source (ctx
, s
);
2226 file_progress_show_target (ctx
, d
);
2227 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2232 mc_stat (src_vpath
, &sbuf
);
2234 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2236 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2237 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2240 destdir
= g_strdup (d
); /* destination doesn't exist */
2241 else if (!ctx
->dive_into_subdirs
)
2243 destdir
= g_strdup (d
);
2247 destdir
= concat_dir_and_file (d
, x_basename (s
));
2249 destdir_vpath
= vfs_path_from_str (destdir
);
2251 /* Check if the user inputted an existing dir */
2253 if (mc_stat (destdir_vpath
, &destbuf
) == 0)
2257 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
2259 if (return_status
!= FILE_CONT
)
2263 else if (ctx
->skip_all
)
2264 return_status
= FILE_SKIPALL
;
2267 if (S_ISDIR (destbuf
.st_mode
))
2268 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir
);
2270 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir
);
2271 if (return_status
== FILE_SKIPALL
)
2272 ctx
->skip_all
= TRUE
;
2273 if (return_status
== FILE_RETRY
)
2274 goto retry_dst_stat
;
2277 vfs_path_free (destdir_vpath
);
2278 vfs_path_free (src_vpath
);
2279 vfs_path_free (dst_vpath
);
2280 return return_status
;
2284 if (mc_rename (src_vpath
, destdir_vpath
) == 0)
2286 return_status
= FILE_CONT
;
2294 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2295 if (return_status
== FILE_SKIPALL
)
2296 ctx
->skip_all
= TRUE
;
2297 if (return_status
== FILE_RETRY
)
2302 /* Failed because of filesystem boundary -> copy dir instead */
2303 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
2305 if (return_status
!= FILE_CONT
)
2308 file_progress_show_source (ctx
, NULL
);
2309 file_progress_show (ctx
, 0, 0, "", FALSE
);
2311 return_status
= check_progress_buttons (ctx
);
2312 if (return_status
!= FILE_CONT
)
2316 if (ctx
->erase_at_end
)
2318 for (; erase_list
&& return_status
!= FILE_ABORT
;)
2320 if (S_ISDIR (erase_list
->st_mode
))
2322 return_status
= erase_dir_iff_empty (ctx
, erase_list
->name
);
2325 return_status
= erase_file (tctx
, ctx
, erase_list
->name
);
2327 erase_list
= erase_list
->next
;
2331 erase_dir_iff_empty (ctx
, s
);
2335 vfs_path_free (destdir_vpath
);
2339 erase_list
= erase_list
->next
;
2342 vfs_path_free (src_vpath
);
2343 vfs_path_free (dst_vpath
);
2344 return return_status
;
2349 /* --------------------------------------------------------------------------------------------- */
2350 /* {{{ Erase routines */
2353 erase_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
2355 FileProgressStatus error
;
2357 if (strcmp (s
, "..") == 0)
2360 if (strcmp (s
, ".") == 0)
2363 file_progress_show_deleting (ctx
, s
);
2364 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2368 /* The old way to detect a non empty directory was:
2369 error = my_rmdir (s);
2370 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2371 For the linux user space nfs server (nfs-server-2.2beta29-2)
2372 we would have to check also for EIO. I hope the new way is
2373 fool proof. (Norbert)
2375 error
= check_dir_is_empty (s
);
2378 error
= query_recursive (ctx
, s
);
2379 if (error
== FILE_CONT
)
2380 return recursive_erase (tctx
, ctx
, s
);
2385 while (my_rmdir (s
) == -1 && !ctx
->skip_all
)
2387 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
2388 if (error
!= FILE_RETRY
)
2397 /* --------------------------------------------------------------------------------------------- */
2398 /* {{{ Panel operate routines */
2401 compute_dir_size_create_ui (void)
2403 ComputeDirSizeUI
*ui
;
2405 const char *b_name
= N_("&Abort");
2411 ui
= g_new (ComputeDirSizeUI
, 1);
2413 ui
->dlg
= create_dlg (TRUE
, 0, 0, 8, COLS
/ 2, dialog_colors
, NULL
,
2414 NULL
, _("Directory scanning"), DLG_CENTER
);
2415 ui
->dirname
= label_new (3, 3, "");
2416 add_widget (ui
->dlg
, ui
->dirname
);
2418 add_widget (ui
->dlg
,
2419 button_new (5, (ui
->dlg
->cols
- strlen (b_name
)) / 2,
2420 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
2422 /* We will manage the dialog without any help,
2423 that's why we have to call init_dlg */
2429 /* --------------------------------------------------------------------------------------------- */
2432 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
2436 /* schedule to update passive panel */
2437 other_panel
->dirty
= 1;
2439 /* close and destroy dialog */
2440 dlg_run_done (ui
->dlg
);
2441 destroy_dlg (ui
->dlg
);
2446 /* --------------------------------------------------------------------------------------------- */
2449 compute_dir_size_update_ui (const void *ui
, const vfs_path_t
* dirname_vpath
)
2451 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
2459 dirname
= vfs_path_to_str (dirname_vpath
);
2460 label_set_text (this->dirname
, str_trunc (dirname
, this->dlg
->cols
- 6));
2463 event
.x
= -1; /* Don't show the GPM cursor */
2464 c
= tty_get_event (&event
, FALSE
, FALSE
);
2468 /* Reinitialize to avoid old values after events other than
2469 selecting a button */
2470 this->dlg
->ret_value
= FILE_CONT
;
2472 dlg_process_event (this->dlg
, c
, &event
);
2474 switch (this->dlg
->ret_value
)
2484 /* --------------------------------------------------------------------------------------------- */
2488 * Computes the number of bytes used by the files in a directory
2492 compute_dir_size (const vfs_path_t
* dirname_vpath
, const void *ui
,
2493 compute_dir_size_callback cback
,
2494 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
2499 struct dirent
*dirent
;
2500 FileProgressStatus ret
= FILE_CONT
;
2502 if (!compute_symlinks
)
2504 res
= mc_lstat (dirname_vpath
, &s
);
2508 /* don't scan symlink to directory */
2509 if (S_ISLNK (s
.st_mode
))
2512 *ret_total
+= (uintmax_t) s
.st_size
;
2517 dir
= mc_opendir (dirname_vpath
);
2522 while ((dirent
= mc_readdir (dir
)) != NULL
)
2524 vfs_path_t
*tmp_vpath
;
2526 ret
= (cback
!= NULL
) ? cback (ui
, dirname_vpath
) : FILE_CONT
;
2528 if (ret
!= FILE_CONT
)
2531 if (strcmp (dirent
->d_name
, ".") == 0)
2533 if (strcmp (dirent
->d_name
, "..") == 0)
2536 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, NULL
);
2537 res
= mc_lstat (tmp_vpath
, &s
);
2540 if (S_ISDIR (s
.st_mode
))
2542 size_t subdir_count
= 0;
2543 uintmax_t subdir_bytes
= 0;
2546 compute_dir_size (tmp_vpath
, ui
, cback
, &subdir_count
, &subdir_bytes
,
2549 if (ret
!= FILE_CONT
)
2551 vfs_path_free (tmp_vpath
);
2555 *ret_marked
+= subdir_count
;
2556 *ret_total
+= subdir_bytes
;
2561 *ret_total
+= (uintmax_t) s
.st_size
;
2564 vfs_path_free (tmp_vpath
);
2571 /* --------------------------------------------------------------------------------------------- */
2575 * Performs one of the operations on the selection on the source_panel
2576 * (copy, delete, move).
2578 * Returns TRUE if did change the directory
2579 * structure, Returns FALSE if user aborted
2581 * force_single forces operation on the current entry and affects
2582 * default destination. Current filename is used as default.
2586 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2588 WPanel
*panel
= (WPanel
*) source_panel
;
2589 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2590 || (get_current_type () == view_tree
);
2592 char *source
= NULL
;
2593 #ifdef WITH_FULL_PATHS
2594 vfs_path_t
*source_with_vpath
= NULL
;
2595 char *source_with_path_str
= NULL
;
2597 #define source_with_path source
2598 #endif /* !WITH_FULL_PATHS */
2600 vfs_path_t
*dest_vpath
= NULL
;
2602 char *save_cwd
= NULL
, *save_dest
= NULL
;
2603 struct stat src_stat
;
2604 gboolean ret_val
= TRUE
;
2606 FileProgressStatus value
;
2608 FileOpTotalContext
*tctx
;
2609 vfs_path_t
*tmp_vpath
;
2611 gboolean do_bg
= FALSE
; /* do background operation? */
2613 static gboolean i18n_flag
= FALSE
;
2616 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
2617 op_names
[i
] = Q_ (op_names
[i
]);
2621 free_linklist (&linklist
);
2622 free_linklist (&dest_dirs
);
2626 vfs_path_t
*source_vpath
;
2629 source
= g_strdup (selection (panel
)->fname
);
2631 source
= panel_get_file (panel
);
2633 if (strcmp (source
, "..") == 0)
2636 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2640 source_vpath
= vfs_path_from_str (source
);
2641 /* Update stat to get actual info */
2642 if (mc_lstat (source_vpath
, &src_stat
) != 0)
2644 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2645 path_trunc (source
, 30), unix_error_string (errno
));
2647 /* Directory was changed outside MC. Reload it forced */
2648 if (!panel
->is_panelized
)
2650 panel_update_flags_t flags
= UP_RELOAD
;
2652 /* don't update panelized panel */
2653 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2654 flags
|= UP_ONLY_CURRENT
;
2656 update_panels (flags
, UP_KEEPSEL
);
2658 vfs_path_free (source_vpath
);
2661 vfs_path_free (source_vpath
);
2664 ctx
= file_op_context_new (operation
);
2666 /* Show confirmation dialog */
2667 if (operation
!= OP_DELETE
)
2669 char *tmp_dest_dir
, *dest_dir
;
2672 /* Forced single operations default to the original name */
2674 tmp_dest_dir
= g_strdup (source
);
2675 else if (get_other_type () == view_listing
)
2676 tmp_dest_dir
= vfs_path_to_str (other_panel
->cwd_vpath
);
2678 tmp_dest_dir
= vfs_path_to_str (panel
->cwd_vpath
);
2680 * Add trailing backslash only when do non-local ops.
2681 * It saves user from occasional file renames (when destination
2684 if (!force_single
&& tmp_dest_dir
[0] != '\0'
2685 && tmp_dest_dir
[strlen (tmp_dest_dir
) - 1] != PATH_SEP
)
2687 /* add trailing separator */
2688 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2689 g_free (tmp_dest_dir
);
2694 dest_dir
= tmp_dest_dir
;
2696 if (dest_dir
== NULL
)
2702 /* Generate confirmation prompt */
2703 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2705 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2706 source
!= NULL
? (void *) source
2707 : (void *) &panel
->marked
, dest_dir
, &do_bg
);
2712 if (dest
== NULL
|| dest
[0] == '\0')
2718 dest_vpath
= vfs_path_from_str (dest
);
2720 else if (confirm_delete
)
2723 char fmd_buf
[BUF_MEDIUM
];
2725 /* Generate confirmation prompt */
2726 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2729 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2732 const int fmd_xlen
= 64;
2733 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2734 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2742 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2751 tctx
= file_op_total_context_new ();
2752 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2755 filegui_dialog_type_t dialog_type
;
2757 if (operation
== OP_DELETE
)
2758 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2761 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2762 ? FILEGUI_DIALOG_MULTI_ITEM
: FILEGUI_DIALOG_ONE_ITEM
;
2764 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2765 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2768 /* Background also need ctx->ui, but not full */
2770 file_op_context_create_ui_without_init (ctx
, TRUE
, dialog_type
);
2772 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
2775 #ifdef ENABLE_BACKGROUND
2776 /* Did the user select to do a background operation? */
2782 cwd_str
= vfs_path_to_str (panel
->cwd_vpath
);
2783 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", cwd_str
, (char *) NULL
));
2786 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2788 /* If we are the parent */
2791 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2793 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2794 vfs_path_free (dest_vpath
);
2796 /* file_op_context_destroy (ctx); */
2800 #endif /* ENABLE_BACKGROUND */
2802 /* Initialize things */
2803 /* We do not want to trash cache every time file is
2804 created/touched. However, this will make our cache contain
2806 if ((dest
!= NULL
) && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2807 save_dest
= g_strdup (dest
);
2809 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2810 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2811 save_cwd
= vfs_path_to_str (panel
->cwd_vpath
);
2813 /* Now, let's do the job */
2815 /* This code is only called by the tree and panel code */
2818 /* We now have ETA in all cases */
2820 /* One file: FIXME mc_chdir will take user out of any vfs */
2821 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2826 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2827 chdir_retcode
= mc_chdir (vpath
);
2828 vfs_path_free (vpath
);
2829 if (chdir_retcode
< 0)
2836 /* The source and src_stat variables have been initialized before */
2837 #ifdef WITH_FULL_PATHS
2838 if (g_path_is_absolute (source
))
2839 source_with_vpath
= vfs_path_from_str (source
);
2841 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2842 source_with_path_str
= vfs_path_to_str (source_with_vpath
);
2843 #endif /* WITH_FULL_PATHS */
2844 if (panel_operate_init_totals (operation
, panel
, source_with_path_str
, ctx
) == FILE_CONT
)
2846 if (operation
== OP_DELETE
)
2848 if (S_ISDIR (src_stat
.st_mode
))
2849 value
= erase_dir (tctx
, ctx
, source_with_path_str
);
2851 value
= erase_file (tctx
, ctx
, source_with_path_str
);
2855 temp
= transform_source (ctx
, source_with_path_str
);
2857 value
= transform_error
;
2860 char *repl_dest
, *temp2
;
2862 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2863 temp2
= concat_dir_and_file (repl_dest
, temp
);
2867 vfs_path_free (dest_vpath
);
2869 dest_vpath
= vfs_path_from_str (dest
);
2874 /* we use file_mask_op_follow_links only with OP_COPY */
2875 ctx
->stat_func (source_with_vpath
, &src_stat
);
2877 if (S_ISDIR (src_stat
.st_mode
))
2878 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, dest
,
2879 TRUE
, FALSE
, FALSE
, NULL
);
2881 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, dest
);
2885 if (S_ISDIR (src_stat
.st_mode
))
2886 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, dest
);
2888 value
= move_file_file (tctx
, ctx
, source_with_path_str
, dest
);
2892 /* Unknown file operation */
2896 } /* Copy or move operation */
2898 if ((value
== FILE_CONT
) && !force_single
)
2899 unmark_files (panel
);
2906 /* Check destination for copy or move operation */
2907 while (operation
!= OP_DELETE
)
2910 struct stat dst_stat
;
2912 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2914 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2918 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2922 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
)
2924 /* Loop for every file, perform the actual copy operation */
2925 for (i
= 0; i
< panel
->count
; i
++)
2927 const char *source2
;
2929 if (!panel
->dir
.list
[i
].f
.marked
)
2930 continue; /* Skip the unmarked ones */
2932 source2
= panel
->dir
.list
[i
].fname
;
2933 src_stat
= panel
->dir
.list
[i
].st
;
2935 #ifdef WITH_FULL_PATHS
2936 g_free (source_with_path_str
);
2937 vfs_path_free (source_with_vpath
);
2938 if (g_path_is_absolute (source2
))
2939 source_with_vpath
= vfs_path_from_str (source2
);
2942 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2943 source_with_path_str
= vfs_path_to_str (source_with_vpath
);
2944 #endif /* WITH_FULL_PATHS */
2946 if (operation
== OP_DELETE
)
2948 if (S_ISDIR (src_stat
.st_mode
))
2949 value
= erase_dir (tctx
, ctx
, source_with_path_str
);
2951 value
= erase_file (tctx
, ctx
, source_with_path_str
);
2955 temp
= transform_source (ctx
, source_with_path_str
);
2958 value
= transform_error
;
2961 char *temp2
, *temp3
, *repl_dest
;
2963 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2964 temp2
= concat_dir_and_file (repl_dest
, temp
);
2967 temp3
= source_with_path_str
;
2968 source_with_path_str
= strutils_shell_unescape (source_with_path_str
);
2971 temp2
= strutils_shell_unescape (temp2
);
2977 /* we use file_mask_op_follow_links only with OP_COPY */
2981 vpath
= vfs_path_from_str (source_with_path_str
);
2982 ctx
->stat_func (vpath
, &src_stat
);
2983 vfs_path_free (vpath
);
2985 if (S_ISDIR (src_stat
.st_mode
))
2986 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
,
2987 TRUE
, FALSE
, FALSE
, NULL
);
2989 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
2990 free_linklist (&dest_dirs
);
2994 if (S_ISDIR (src_stat
.st_mode
))
2995 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
);
2997 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
3001 /* Unknown file operation */
3007 } /* Copy or move operation */
3009 if (value
== FILE_ABORT
)
3012 if (value
== FILE_CONT
)
3013 do_file_mark (panel
, i
, 0);
3015 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3017 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3018 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3021 if (operation
!= OP_DELETE
)
3022 file_progress_show (ctx
, 0, 0, "", FALSE
);
3024 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3028 } /* Loop for every file */
3030 } /* Many entries */
3034 if (save_cwd
!= NULL
)
3036 tmp_vpath
= vfs_path_from_str (save_cwd
);
3037 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3038 vfs_path_free (tmp_vpath
);
3042 if (save_dest
!= NULL
)
3044 tmp_vpath
= vfs_path_from_str (save_dest
);
3045 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3046 vfs_path_free (tmp_vpath
);
3050 free_linklist (&linklist
);
3051 free_linklist (&dest_dirs
);
3052 #ifdef WITH_FULL_PATHS
3053 g_free (source_with_path_str
);
3054 vfs_path_free (source_with_vpath
);
3055 #endif /* WITH_FULL_PATHS */
3057 vfs_path_free (dest_vpath
);
3058 g_free (ctx
->dest_mask
);
3059 ctx
->dest_mask
= NULL
;
3061 #ifdef ENABLE_BACKGROUND
3062 /* Let our parent know we are saying bye bye */
3063 if (mc_global
.we_are_background
)
3065 int cur_pid
= getpid ();
3066 /* Send pid to parent with child context, it is fork and
3067 don't modify real parent ctx */
3069 parent_call ((void *) end_bg_process
, ctx
, 0);
3074 #endif /* ENABLE_BACKGROUND */
3076 file_op_total_context_destroy (tctx
);
3078 file_op_context_destroy (ctx
);
3086 /* --------------------------------------------------------------------------------------------- */
3087 /* {{{ Query/status report routines */
3088 /** Report error with one file */
3090 file_error (const char *format
, const char *file
)
3092 char buf
[BUF_MEDIUM
];
3094 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3096 return do_file_error (buf
);
3099 /* --------------------------------------------------------------------------------------------- */
3102 Cause emacs to enter folding mode for this file: