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 */
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 */
115 struct vfs_class
*vfs
;
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 struct link
*linklist
= NULL
;
178 /* the files-to-be-erased list */
179 static struct link
*erase_list
;
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 struct link
*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_linklist (struct link
**lc_linklist
)
231 struct link
*lp
, *lp2
;
233 for (lp
= *lc_linklist
; lp
!= NULL
; lp
= lp2
)
241 /* --------------------------------------------------------------------------------------------- */
244 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
247 vfs_path_element_t
*vpath_element
;
248 ino_t ino
= sb
->st_ino
;
249 dev_t dev
= sb
->st_dev
;
251 vpath
= vfs_path_from_str (path
);
252 vpath_element
= vfs_path_get_by_index (vpath
, -1);
256 if (lp
->vfs
== vpath_element
->class)
257 if (lp
->ino
== ino
&& lp
->dev
== dev
)
261 vfs_path_free (vpath
);
265 /* --------------------------------------------------------------------------------------------- */
267 * Check and made hardlink
269 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
270 * and a hardlink was succesfully made
274 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
277 vfs_path_t
*src_vpath
, *dst_vpath
;
279 struct vfs_class
*my_vfs
;
280 ino_t ino
= pstat
->st_ino
;
281 dev_t dev
= pstat
->st_dev
;
282 struct stat link_stat
;
285 src_vpath
= vfs_path_from_str (src_name
);
287 if ((vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
289 vfs_path_free (src_vpath
);
292 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
293 dst_vpath
= vfs_path_from_str (dst_name
);
295 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
296 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
)
298 struct vfs_class
*lp_name_class
;
300 vfs_path_t
*tmp_vpath
;
302 tmp_vpath
= vfs_path_from_str (lp
->name
);
303 lp_name_class
= vfs_path_get_by_index (tmp_vpath
, -1)->class;
304 stat_result
= mc_stat (tmp_vpath
, &link_stat
);
305 vfs_path_free (tmp_vpath
);
307 if (!stat_result
&& link_stat
.st_ino
== ino
308 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
310 struct vfs_class
*p_class
, *dst_name_class
;
312 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
315 dst_name_class
= vfs_path_get_by_index (dst_vpath
, -1)->class;
317 tmp_vpath
= vfs_path_from_str (p
);
318 p_class
= vfs_path_get_by_index (tmp_vpath
, -1)->class;
320 if (dst_name_class
== p_class
)
322 if (!mc_stat (tmp_vpath
, &link_stat
))
324 if (!mc_link (tmp_vpath
, dst_vpath
))
326 vfs_path_free (tmp_vpath
);
327 vfs_path_free (src_vpath
);
328 vfs_path_free (dst_vpath
);
333 vfs_path_free (tmp_vpath
);
336 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
337 vfs_path_free (src_vpath
);
338 vfs_path_free (dst_vpath
);
341 lp
= (struct link
*) g_try_malloc (sizeof (struct link
) + strlen (src_name
)
342 + strlen (dst_name
) + 1);
349 strcpy (lp
->name
, src_name
);
350 lpdstname
= lp
->name
+ strlen (lp
->name
) + 1;
351 strcpy (lpdstname
, dst_name
);
355 vfs_path_free (src_vpath
);
356 vfs_path_free (dst_vpath
);
360 /* --------------------------------------------------------------------------------------------- */
362 * Duplicate the contents of the symbolic link src_path in dst_path.
363 * Try to make a stable symlink if the option "stable symlink" was
364 * set in the file mask dialog.
365 * If dst_path is an existing symlink it will be deleted silently
366 * (upper levels take already care of existing files at dst_path).
369 static FileProgressStatus
370 make_symlink (FileOpContext
* ctx
, const char *src_path
, const char *dst_path
)
372 char link_target
[MC_MAXPATHLEN
];
374 FileProgressStatus return_status
;
376 vfs_path_t
*src_vpath
;
377 vfs_path_t
*dst_vpath
;
378 gboolean dst_is_symlink
;
379 vfs_path_t
*link_target_vpath
= NULL
;
381 src_vpath
= vfs_path_from_str (src_path
);
382 dst_vpath
= vfs_path_from_str (dst_path
);
383 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
386 len
= mc_readlink (src_vpath
, link_target
, MC_MAXPATHLEN
- 1);
390 return_status
= FILE_SKIPALL
;
393 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
394 if (return_status
== FILE_SKIPALL
)
395 ctx
->skip_all
= TRUE
;
396 if (return_status
== FILE_RETRY
)
397 goto retry_src_readlink
;
401 link_target
[len
] = 0;
403 if (ctx
->stable_symlinks
)
406 if (!vfs_file_is_local (src_vpath
) || !vfs_file_is_local (dst_vpath
))
408 message (D_ERROR
, MSG_ERROR
,
409 _("Cannot make stable symlinks across"
410 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
411 ctx
->stable_symlinks
= FALSE
;
415 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
420 const char *r
= strrchr (src_path
, PATH_SEP
);
424 p
= g_strndup (src_path
, r
- src_path
+ 1);
425 if (g_path_is_absolute (dst_path
))
426 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
428 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
430 if (vfs_path_tokens_count (q
) > 1)
432 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
434 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
435 s
= g_strconcat (p
, link_target
, (char *) NULL
);
437 g_strlcpy (link_target
, s
, sizeof (link_target
));
439 tmp_vpath2
= vfs_path_from_str (link_target
);
440 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
441 vfs_path_free (tmp_vpath1
);
442 vfs_path_free (tmp_vpath2
);
445 g_strlcpy (link_target
, s
, sizeof (link_target
));
454 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
457 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
460 return_status
= FILE_CONT
;
464 * if dst_exists, it is obvious that this had failed.
465 * We can delete the old symlink and try again...
469 if (mc_unlink (dst_vpath
) == 0)
470 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
473 return_status
= FILE_CONT
;
478 return_status
= FILE_SKIPALL
;
481 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
482 if (return_status
== FILE_SKIPALL
)
483 ctx
->skip_all
= TRUE
;
484 if (return_status
== FILE_RETRY
)
485 goto retry_dst_symlink
;
489 vfs_path_free (src_vpath
);
490 vfs_path_free (dst_vpath
);
491 vfs_path_free (link_target_vpath
);
492 return return_status
;
495 /* --------------------------------------------------------------------------------------------- */
497 static FileProgressStatus
498 progress_update_one (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, off_t add
)
500 struct timeval tv_current
;
501 static struct timeval tv_start
= { };
503 tctx
->progress_count
++;
504 tctx
->progress_bytes
+= (uintmax_t) add
;
506 if (tv_start
.tv_sec
== 0)
508 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
510 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
511 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
513 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
515 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
516 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
518 tv_start
.tv_sec
= tv_current
.tv_sec
;
521 return check_progress_buttons (ctx
);
524 /* --------------------------------------------------------------------------------------------- */
526 static FileProgressStatus
527 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
531 const char *head_msg
;
533 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
535 msg
= g_strdup_printf (fmt
, a
, b
);
536 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
540 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
543 /* --------------------------------------------------------------------------------------------- */
545 static FileProgressStatus
546 warn_same_file (const char *fmt
, const char *a
, const char *b
)
548 #ifdef ENABLE_BACKGROUND
553 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
554 const char *a
, const char *b
);
558 pntr
.f
= real_warn_same_file
;
560 if (mc_global
.we_are_background
)
561 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
563 return real_warn_same_file (Foreground
, fmt
, a
, b
);
566 /* --------------------------------------------------------------------------------------------- */
567 /* {{{ Query/status report routines */
569 static FileProgressStatus
570 real_do_file_error (enum OperationMode mode
, const char *error
)
575 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
577 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
599 /* --------------------------------------------------------------------------------------------- */
601 static FileProgressStatus
602 real_query_recursive (FileOpContext
* ctx
, enum OperationMode mode
, const char *s
)
606 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
608 const char *msg
= mode
== Foreground
609 ? _("\nDirectory not empty.\nDelete it recursively?")
610 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
611 text
= g_strconcat (_("Delete:"), " ", path_trunc (s
, 30), (char *) NULL
);
616 ctx
->recursive_result
=
617 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
618 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
620 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
625 switch (ctx
->recursive_result
)
628 case RECURSIVE_ALWAYS
:
632 case RECURSIVE_NEVER
:
635 case RECURSIVE_ABORT
:
641 /* --------------------------------------------------------------------------------------------- */
643 #ifdef ENABLE_BACKGROUND
644 static FileProgressStatus
645 do_file_error (const char *str
)
650 FileProgressStatus (*f
) (enum OperationMode
, const char *);
652 pntr
.f
= real_do_file_error
;
654 if (mc_global
.we_are_background
)
655 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
657 return real_do_file_error (Foreground
, str
);
660 /* --------------------------------------------------------------------------------------------- */
662 static FileProgressStatus
663 query_recursive (FileOpContext
* ctx
, const char *s
)
668 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
670 pntr
.f
= real_query_recursive
;
672 if (mc_global
.we_are_background
)
673 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
675 return real_query_recursive (ctx
, Foreground
, s
);
678 /* --------------------------------------------------------------------------------------------- */
680 static FileProgressStatus
681 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
682 struct stat
*_d_stat
)
687 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
688 struct stat
*, struct stat
*);
690 pntr
.f
= file_progress_real_query_replace
;
692 if (mc_global
.we_are_background
)
693 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
694 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
696 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
700 /* --------------------------------------------------------------------------------------------- */
702 static FileProgressStatus
703 do_file_error (const char *str
)
705 return real_do_file_error (Foreground
, str
);
708 /* --------------------------------------------------------------------------------------------- */
710 static FileProgressStatus
711 query_recursive (FileOpContext
* ctx
, const char *s
)
713 return real_query_recursive (ctx
, Foreground
, s
);
716 /* --------------------------------------------------------------------------------------------- */
718 static FileProgressStatus
719 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
720 struct stat
*_d_stat
)
722 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
725 #endif /* !ENABLE_BACKGROUND */
727 /* --------------------------------------------------------------------------------------------- */
728 /** Report error with two files */
730 static FileProgressStatus
731 files_error (const char *format
, const char *file1
, const char *file2
)
733 char buf
[BUF_MEDIUM
];
734 char *nfile1
= g_strdup (path_trunc (file1
, 15));
735 char *nfile2
= g_strdup (path_trunc (file2
, 15));
737 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
742 return do_file_error (buf
);
747 /* --------------------------------------------------------------------------------------------- */
750 copy_file_file_display_progress (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
751 struct timeval tv_current
, struct timeval tv_transfer_start
,
752 off_t file_size
, off_t n_read_total
)
756 /* 1. Update rotating dash after some time */
760 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
762 if (n_read_total
== 0)
766 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
767 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
770 /* 4. Compute BPS rate */
771 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
772 if (ctx
->bps_time
< 1)
774 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
776 /* 5. Compute total ETA and BPS */
777 if (ctx
->progress_bytes
!= 0)
779 uintmax_t remain_bytes
;
781 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
784 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
789 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
790 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
793 /* broken on lot of little files */
795 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
796 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
801 /* --------------------------------------------------------------------------------------------- */
803 /* {{{ Move routines */
804 static FileProgressStatus
805 move_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
807 struct stat src_stats
, dst_stats
;
808 FileProgressStatus return_status
= FILE_CONT
;
809 gboolean copy_done
= FALSE
;
810 gboolean old_ask_overwrite
;
811 vfs_path_t
*src_vpath
, *dst_vpath
;
813 file_progress_show_source (ctx
, s
);
814 file_progress_show_target (ctx
, d
);
815 if (check_progress_buttons (ctx
) == FILE_ABORT
)
819 src_vpath
= vfs_path_from_str (s
);
820 dst_vpath
= vfs_path_from_str (d
);
822 while (mc_lstat (src_vpath
, &src_stats
) != 0)
824 /* Source doesn't exist */
826 return_status
= FILE_SKIPALL
;
829 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
830 if (return_status
== FILE_SKIPALL
)
831 ctx
->skip_all
= TRUE
;
833 if (return_status
!= FILE_RETRY
)
835 vfs_path_free (src_vpath
);
836 vfs_path_free (dst_vpath
);
837 return return_status
;
841 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
843 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
844 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
846 if (S_ISDIR (dst_stats
.st_mode
))
848 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
850 vfs_path_free (src_vpath
);
851 vfs_path_free (dst_vpath
);
855 if (confirm_overwrite
)
857 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
858 if (return_status
!= FILE_CONT
)
860 vfs_path_free (src_vpath
);
861 vfs_path_free (dst_vpath
);
862 return return_status
;
865 /* Ok to overwrite */
870 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
872 return_status
= make_symlink (ctx
, s
, d
);
873 if (return_status
== FILE_CONT
)
874 goto retry_src_remove
;
877 vfs_path_free (src_vpath
);
878 vfs_path_free (dst_vpath
);
879 return return_status
;
883 if (mc_rename (src_vpath
, dst_vpath
) == 0)
885 vfs_path_free (src_vpath
);
886 vfs_path_free (dst_vpath
);
887 return progress_update_one (tctx
, ctx
, src_stats
.st_size
);
891 /* Comparison to EXDEV seems not to work in nfs if you're moving from
892 one nfs to the same, but on the server it is on two different
893 filesystems. Then nfs returns EIO instead of EXDEV.
894 Hope it will not hurt if we always in case of error try to copy/delete. */
896 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
901 return_status
= FILE_SKIPALL
;
904 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
905 if (return_status
== FILE_SKIPALL
)
906 ctx
->skip_all
= TRUE
;
907 if (return_status
== FILE_RETRY
)
910 vfs_path_free (src_vpath
);
911 vfs_path_free (dst_vpath
);
913 return return_status
;
917 /* Failed because filesystem boundary -> copy the file instead */
918 old_ask_overwrite
= tctx
->ask_overwrite
;
919 tctx
->ask_overwrite
= FALSE
;
920 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
921 tctx
->ask_overwrite
= old_ask_overwrite
;
922 if (return_status
!= FILE_CONT
)
924 vfs_path_free (src_vpath
);
925 vfs_path_free (dst_vpath
);
926 return return_status
;
931 file_progress_show_source (ctx
, NULL
);
932 file_progress_show (ctx
, 0, 0, "", FALSE
);
934 return_status
= check_progress_buttons (ctx
);
935 if (return_status
!= FILE_CONT
)
937 vfs_path_free (src_vpath
);
938 vfs_path_free (dst_vpath
);
939 return return_status
;
945 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
947 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
948 if (return_status
== FILE_RETRY
)
949 goto retry_src_remove
;
950 if (return_status
== FILE_SKIPALL
)
951 ctx
->skip_all
= TRUE
;
953 vfs_path_free (src_vpath
);
954 vfs_path_free (dst_vpath
);
955 return return_status
;
959 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
961 vfs_path_free (src_vpath
);
962 vfs_path_free (dst_vpath
);
964 return return_status
;
969 /* --------------------------------------------------------------------------------------------- */
970 /* {{{ Erase routines */
971 /** Don't update progress status if progress_count==NULL */
973 static FileProgressStatus
974 erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
978 vfs_path_t
*vpath
= vfs_path_from_str (s
);
980 file_progress_show_deleting (ctx
, s
);
981 if (check_progress_buttons (ctx
) == FILE_ABORT
)
985 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
987 /* ignore, most likely the mc_unlink fails, too */
991 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
993 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), s
);
994 if (return_status
== FILE_ABORT
)
996 vfs_path_free (vpath
);
997 return return_status
;
999 if (return_status
== FILE_RETRY
)
1001 if (return_status
== FILE_SKIPALL
)
1002 ctx
->skip_all
= TRUE
;
1006 vfs_path_free (vpath
);
1007 if (tctx
->progress_count
== 0)
1009 return progress_update_one (tctx
, ctx
, buf
.st_size
);
1012 /* --------------------------------------------------------------------------------------------- */
1015 Recursive remove of files
1017 skip ->warn every level, gets default
1018 skipall->remove as much as possible
1020 static FileProgressStatus
1021 recursive_erase (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
1023 struct dirent
*next
;
1027 FileProgressStatus return_status
= FILE_CONT
;
1030 if (strcmp (s
, "..") == 0)
1033 vpath
= vfs_path_from_str (s
);
1034 reading
= mc_opendir (vpath
);
1036 if (reading
== NULL
)
1038 return_status
= FILE_RETRY
;
1042 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1044 vfs_path_t
*tmp_vpath
;
1046 if (!strcmp (next
->d_name
, "."))
1048 if (!strcmp (next
->d_name
, ".."))
1050 path
= concat_dir_and_file (s
, next
->d_name
);
1051 tmp_vpath
= vfs_path_from_str (path
);
1052 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1055 mc_closedir (reading
);
1056 vfs_path_free (tmp_vpath
);
1057 return_status
= FILE_RETRY
;
1060 if (S_ISDIR (buf
.st_mode
))
1061 return_status
= recursive_erase (tctx
, ctx
, path
);
1063 return_status
= erase_file (tctx
, ctx
, path
);
1064 vfs_path_free (tmp_vpath
);
1067 mc_closedir (reading
);
1068 if (return_status
== FILE_ABORT
)
1071 file_progress_show_deleting (ctx
, s
);
1072 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1074 return_status
= FILE_ABORT
;
1079 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1081 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1082 if (return_status
== FILE_RETRY
)
1084 if (return_status
== FILE_ABORT
)
1086 if (return_status
== FILE_SKIPALL
)
1087 ctx
->skip_all
= TRUE
;
1092 vfs_path_free (vpath
);
1093 return return_status
;
1096 /* --------------------------------------------------------------------------------------------- */
1097 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1098 in the directory path points to, 0 else. */
1101 check_dir_is_empty (const char *path
)
1106 vfs_path_t
*vpath
= vfs_path_from_str (path
);
1108 dir
= mc_opendir (vpath
);
1111 vfs_path_free (vpath
);
1115 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
))
1117 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1118 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1119 continue; /* "." or ".." */
1125 vfs_path_free (vpath
);
1129 /* --------------------------------------------------------------------------------------------- */
1131 static FileProgressStatus
1132 erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
)
1134 FileProgressStatus error
;
1136 if (strcmp (s
, "..") == 0)
1139 if (strcmp (s
, ".") == 0)
1142 file_progress_show_deleting (ctx
, s
);
1143 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1147 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1150 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1152 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1153 if (error
== FILE_SKIPALL
)
1154 ctx
->skip_all
= TRUE
;
1155 if (error
!= FILE_RETRY
)
1164 /* --------------------------------------------------------------------------------------------- */
1165 /* {{{ Panel operate routines */
1168 * Return currently selected entry name or the name of the first marked
1169 * entry if there is one.
1173 panel_get_file (WPanel
* panel
)
1175 if (get_current_type () == view_tree
)
1179 tree
= (WTree
*) get_panel_widget (get_current_index ());
1180 return vfs_path_to_str (tree_selected_name (tree
));
1183 if (panel
->marked
!= 0)
1187 for (i
= 0; i
< panel
->count
; i
++)
1188 if (panel
->dir
.list
[i
].f
.marked
)
1189 return g_strdup (panel
->dir
.list
[i
].fname
);
1191 return g_strdup (panel
->dir
.list
[panel
->selected
].fname
);
1194 /* --------------------------------------------------------------------------------------------- */
1196 * panel_compute_totals:
1198 * compute the number of files and the number of bytes
1199 * used up by the whole selection, recursing directories
1200 * as required. In addition, it checks to see if it will
1201 * overwrite any files by doing the copy.
1204 static FileProgressStatus
1205 panel_compute_totals (const WPanel
* panel
, const void *ui
,
1206 compute_dir_size_callback cback
,
1207 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
1214 for (i
= 0; i
< panel
->count
; i
++)
1218 if (!panel
->dir
.list
[i
].f
.marked
)
1221 s
= &panel
->dir
.list
[i
].st
;
1223 if (S_ISDIR (s
->st_mode
))
1226 size_t subdir_count
= 0;
1227 uintmax_t subdir_bytes
= 0;
1228 FileProgressStatus status
;
1230 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, NULL
);
1232 compute_dir_size (p
, ui
, cback
, &subdir_count
, &subdir_bytes
, compute_symlinks
);
1235 if (status
!= FILE_CONT
)
1238 *ret_marked
+= subdir_count
;
1239 *ret_total
+= subdir_bytes
;
1244 *ret_total
+= (uintmax_t) s
->st_size
;
1251 /* --------------------------------------------------------------------------------------------- */
1253 /** Initialize variables for progress bars */
1254 static FileProgressStatus
1255 panel_operate_init_totals (FileOperation operation
,
1256 const WPanel
* panel
, const char *source
, FileOpContext
* ctx
)
1258 FileProgressStatus status
;
1260 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
)
1262 ComputeDirSizeUI
*ui
;
1264 ui
= compute_dir_size_create_ui ();
1267 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1268 &ctx
->progress_count
, &ctx
->progress_bytes
,
1274 p
= vfs_path_from_str (source
);
1275 status
= compute_dir_size (p
, ui
, compute_dir_size_update_ui
,
1276 &ctx
->progress_count
, &ctx
->progress_bytes
,
1281 compute_dir_size_destroy_ui (ui
);
1283 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1288 ctx
->progress_count
= panel
->marked
;
1289 ctx
->progress_bytes
= panel
->total
;
1290 ctx
->progress_totals_computed
= FALSE
;
1296 /* --------------------------------------------------------------------------------------------- */
1298 * Generate user prompt for panel operation.
1299 * single_source is the name if the source entry or NULL for multiple
1301 * src_stat is only used when single_source is not NULL.
1305 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1306 gboolean single_source
, const struct stat
*src_stat
)
1308 const char *sp
, *cp
;
1309 char format_string
[BUF_MEDIUM
];
1310 char *dp
= format_string
;
1311 gboolean build_question
= FALSE
;
1313 static gboolean i18n_flag
= FALSE
;
1318 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1319 op_names1
[i
] = Q_ (op_names1
[i
]);
1322 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1323 prompt_parts
[i
] = _(prompt_parts
[i
]);
1325 one_format
= _(one_format
);
1326 many_format
= _(many_format
);
1327 question_format
= _(question_format
);
1328 #endif /* ENABLE_NLS */
1332 sp
= single_source
? one_format
: many_format
;
1343 cp
= op_names1
[operation
];
1346 if (operation
== OP_DELETE
)
1349 build_question
= TRUE
;
1352 cp
= prompt_parts
[5];
1355 if (operation
== OP_DELETE
)
1358 build_question
= TRUE
;
1361 cp
= prompt_parts
[6];
1365 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1367 cp
= (panel
->marked
== panel
->dirs_marked
)
1369 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1390 char tmp
[BUF_MEDIUM
];
1392 memmove (tmp
, format_string
, sizeof (tmp
));
1393 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1396 return g_strdup (format_string
);
1399 /* --------------------------------------------------------------------------------------------- */
1401 #ifdef ENABLE_BACKGROUND
1403 end_bg_process (FileOpContext
* ctx
, enum OperationMode mode
)
1410 unregister_task_with_pid (pid
);
1411 /* file_op_context_destroy(ctx); */
1417 /* --------------------------------------------------------------------------------------------- */
1418 /*** public functions ****************************************************************************/
1419 /* --------------------------------------------------------------------------------------------- */
1422 copy_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
1423 const char *src_path
, const char *dst_path
)
1425 uid_t src_uid
= (uid_t
) - 1;
1426 gid_t src_gid
= (gid_t
) - 1;
1428 int src_desc
, dest_desc
= -1;
1429 int n_read
, n_written
;
1430 mode_t src_mode
= 0; /* The mode of the source file */
1431 struct stat sb
, sb2
;
1433 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1434 off_t file_size
= -1;
1435 FileProgressStatus return_status
, temp_status
;
1436 struct timeval tv_transfer_start
;
1437 dest_status_t dst_status
= DEST_NONE
;
1439 gboolean is_first_time
= TRUE
;
1440 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1442 /* FIXME: We should not be using global variables! */
1444 return_status
= FILE_RETRY
;
1446 file_progress_show_source (ctx
, src_path
);
1447 file_progress_show_target (ctx
, dst_path
);
1448 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1453 dst_vpath
= vfs_path_from_str (dst_path
);
1454 while (mc_stat (dst_vpath
, &sb2
) == 0)
1456 if (S_ISDIR (sb2
.st_mode
))
1459 return_status
= FILE_SKIPALL
;
1462 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1463 if (return_status
== FILE_SKIPALL
)
1464 ctx
->skip_all
= TRUE
;
1465 if (return_status
== FILE_RETRY
)
1474 src_vpath
= vfs_path_from_str (src_path
);
1475 while ((*ctx
->stat_func
) (src_vpath
, &sb
) != 0)
1478 return_status
= FILE_SKIPALL
;
1481 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1482 if (return_status
== FILE_SKIPALL
)
1483 ctx
->skip_all
= TRUE
;
1485 if (return_status
!= FILE_RETRY
)
1491 /* Destination already exists */
1492 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1494 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1495 src_path
, dst_path
);
1498 /* Should we replace destination? */
1499 if (tctx
->ask_overwrite
)
1502 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1503 if (return_status
!= FILE_CONT
)
1508 if (!ctx
->do_append
)
1510 /* Check the hardlinks */
1511 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_path
, dst_path
, &sb
))
1513 /* We have made a hardlink - no more processing is necessary */
1514 return_status
= FILE_CONT
;
1518 if (S_ISLNK (sb
.st_mode
))
1520 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1524 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1525 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1527 while (mc_mknod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0
1530 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1531 if (return_status
== FILE_RETRY
)
1533 if (return_status
== FILE_SKIPALL
)
1534 ctx
->skip_all
= TRUE
;
1539 while (ctx
->preserve_uidgid
&& mc_chown (dst_vpath
, sb
.st_uid
, sb
.st_gid
) != 0
1542 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1543 if (temp_status
== FILE_SKIP
)
1545 if (temp_status
== FILE_SKIPALL
)
1546 ctx
->skip_all
= TRUE
;
1547 if (temp_status
!= FILE_RETRY
)
1549 return_status
= temp_status
;
1554 while (ctx
->preserve
&& mc_chmod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
) != 0
1557 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1558 if (temp_status
== FILE_SKIP
)
1560 if (temp_status
== FILE_SKIPALL
)
1561 ctx
->skip_all
= TRUE
;
1562 if (temp_status
!= FILE_RETRY
)
1564 return_status
= temp_status
;
1569 return_status
= FILE_CONT
;
1574 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1576 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1578 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1579 if (return_status
== FILE_RETRY
)
1581 if (return_status
== FILE_SKIPALL
)
1582 ctx
->skip_all
= TRUE
;
1583 if (return_status
== FILE_SKIP
)
1589 if (ctx
->do_reget
!= 0)
1591 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1593 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1595 ctx
->do_append
= FALSE
;
1599 while (mc_fstat (src_desc
, &sb
) != 0)
1602 return_status
= FILE_SKIPALL
;
1605 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1606 if (return_status
== FILE_RETRY
)
1608 if (return_status
== FILE_SKIPALL
)
1609 ctx
->skip_all
= TRUE
;
1610 ctx
->do_append
= FALSE
;
1614 src_mode
= sb
.st_mode
;
1615 src_uid
= sb
.st_uid
;
1616 src_gid
= sb
.st_gid
;
1617 utb
.actime
= sb
.st_atime
;
1618 utb
.modtime
= sb
.st_mtime
;
1619 file_size
= sb
.st_size
;
1621 open_flags
= O_WRONLY
;
1624 if (ctx
->do_append
!= 0)
1625 open_flags
|= O_APPEND
;
1627 open_flags
|= O_CREAT
| O_TRUNC
;
1631 open_flags
|= O_CREAT
| O_EXCL
;
1634 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1636 if (errno
!= EEXIST
)
1639 return_status
= FILE_SKIPALL
;
1642 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1643 if (return_status
== FILE_RETRY
)
1645 if (return_status
== FILE_SKIPALL
)
1646 ctx
->skip_all
= TRUE
;
1647 ctx
->do_append
= FALSE
;
1652 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1654 appending
= ctx
->do_append
;
1655 ctx
->do_append
= FALSE
;
1657 /* Find out the optimal buffer size. */
1658 while (mc_fstat (dest_desc
, &sb
) != 0)
1661 return_status
= FILE_SKIPALL
;
1664 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1665 if (return_status
== FILE_RETRY
)
1667 if (return_status
== FILE_SKIPALL
)
1668 ctx
->skip_all
= TRUE
;
1675 errno
= vfs_preallocate (dest_desc
, file_size
, (ctx
->do_append
!= 0) ? sb
.st_size
: 0);
1680 return_status
= FILE_SKIPALL
;
1684 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1685 if (return_status
== FILE_RETRY
)
1687 if (return_status
== FILE_SKIPALL
)
1688 ctx
->skip_all
= TRUE
;
1690 mc_close (dest_desc
);
1692 mc_unlink (dst_vpath
);
1693 dst_status
= DEST_NONE
;
1697 ctx
->eta_secs
= 0.0;
1700 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1701 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1703 file_progress_show (ctx
, 1, 1, "", TRUE
);
1704 return_status
= check_progress_buttons (ctx
);
1707 if (return_status
!= FILE_CONT
)
1711 off_t n_read_total
= 0;
1712 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1713 int secs
, update_secs
;
1714 const char *stalled_msg
= "";
1716 tv_last_update
= tv_transfer_start
;
1723 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1726 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0 && !ctx
->skip_all
)
1728 return_status
= file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
1729 if (return_status
== FILE_RETRY
)
1731 if (return_status
== FILE_SKIPALL
)
1732 ctx
->skip_all
= TRUE
;
1738 gettimeofday (&tv_current
, NULL
);
1743 n_read_total
+= n_read
;
1745 /* Windows NT ftp servers report that files have no
1746 * permissions: -------, so if we happen to have actually
1747 * read something, we should fix the permissions.
1749 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1750 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1751 gettimeofday (&tv_last_input
, NULL
);
1754 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
&& !ctx
->skip_all
)
1758 n_read
-= n_written
;
1762 return_status
= file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1763 if (return_status
== FILE_SKIP
)
1765 if (return_status
== FILE_SKIPALL
)
1766 ctx
->skip_all
= TRUE
;
1767 if (return_status
!= FILE_RETRY
)
1772 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1774 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1775 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1777 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1779 copy_file_file_display_progress (tctx
, ctx
,
1781 tv_transfer_start
, file_size
, n_read_total
);
1782 tv_last_update
= tv_current
;
1784 is_first_time
= FALSE
;
1786 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1788 stalled_msg
= _("(stalled)");
1792 gboolean force_update
;
1795 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1797 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1799 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1800 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1803 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1808 return_status
= check_progress_buttons (ctx
);
1810 if (return_status
!= FILE_CONT
)
1818 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1821 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1823 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1824 if (temp_status
== FILE_RETRY
)
1826 if (temp_status
== FILE_ABORT
)
1827 return_status
= temp_status
;
1828 if (temp_status
== FILE_SKIPALL
)
1829 ctx
->skip_all
= TRUE
;
1833 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1835 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1836 if (temp_status
== FILE_RETRY
)
1838 if (temp_status
== FILE_SKIPALL
)
1839 ctx
->skip_all
= TRUE
;
1840 return_status
= temp_status
;
1844 if (dst_status
== DEST_SHORT
)
1846 /* Remove short file */
1849 result
= query_dialog (Q_ ("DialogTitle|Copy"),
1850 _("Incomplete file was retrieved. Keep it?"),
1851 D_ERROR
, 2, _("&Delete"), _("&Keep"));
1853 mc_unlink (dst_vpath
);
1855 else if (dst_status
== DEST_FULL
)
1857 /* Copy has succeeded */
1858 if (!appending
&& ctx
->preserve_uidgid
)
1860 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1862 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1863 if (temp_status
== FILE_RETRY
)
1865 if (temp_status
== FILE_SKIPALL
)
1867 ctx
->skip_all
= TRUE
;
1868 return_status
= FILE_CONT
;
1870 if (temp_status
== FILE_SKIP
)
1871 return_status
= FILE_CONT
;
1880 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1882 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1883 if (temp_status
== FILE_RETRY
)
1885 if (temp_status
== FILE_SKIPALL
)
1887 ctx
->skip_all
= TRUE
;
1888 return_status
= FILE_CONT
;
1890 if (temp_status
== FILE_SKIP
)
1891 return_status
= FILE_CONT
;
1895 else if (!dst_exists
)
1897 src_mode
= umask (-1);
1899 src_mode
= 0100666 & ~src_mode
;
1900 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
1902 mc_utime (dst_vpath
, &utb
);
1906 if (return_status
== FILE_CONT
)
1907 return_status
= progress_update_one (tctx
, ctx
, file_size
);
1910 vfs_path_free (src_vpath
);
1911 vfs_path_free (dst_vpath
);
1912 return return_status
;
1915 /* --------------------------------------------------------------------------------------------- */
1917 * I think these copy_*_* functions should have a return type.
1918 * anyway, this function *must* have two directories as arguments.
1920 /* FIXME: This function needs to check the return values of the
1924 copy_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *_d
,
1925 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, struct link
* parent_dirs
)
1927 struct dirent
*next
;
1928 struct stat buf
, cbuf
;
1930 char *dest_dir
= NULL
;
1931 FileProgressStatus return_status
= FILE_CONT
;
1935 vfs_path_t
*src_vpath
, *dst_vpath
, *dest_dir_vpath
= NULL
;
1939 src_vpath
= vfs_path_from_str (s
);
1940 dst_vpath
= vfs_path_from_str (_d
);
1942 /* First get the mode of the source dir */
1945 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
1948 return_status
= FILE_SKIPALL
;
1951 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
1952 if (return_status
== FILE_RETRY
)
1953 goto retry_src_stat
;
1954 if (return_status
== FILE_SKIPALL
)
1955 ctx
->skip_all
= TRUE
;
1960 if (is_in_linklist (dest_dirs
, s
, &cbuf
))
1962 /* Don't copy a directory we created before (we don't want to copy
1963 infinitely if a directory is copied into itself) */
1964 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1965 return_status
= FILE_CONT
;
1969 /* Hmm, hardlink to directory??? - Norbert */
1970 /* FIXME: In this step we should do something
1971 in case the destination already exist */
1972 /* Check the hardlinks */
1973 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (s
, d
, &cbuf
))
1975 /* We have made a hardlink - no more processing is necessary */
1979 if (!S_ISDIR (cbuf
.st_mode
))
1982 return_status
= FILE_SKIPALL
;
1985 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
1986 if (return_status
== FILE_RETRY
)
1987 goto retry_src_stat
;
1988 if (return_status
== FILE_SKIPALL
)
1989 ctx
->skip_all
= TRUE
;
1994 if (is_in_linklist (parent_dirs
, s
, &cbuf
))
1996 /* we found a cyclic symbolic link */
1997 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
1998 return_status
= FILE_SKIP
;
2002 lp
= g_new (struct link
, 1);
2003 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
2004 lp
->ino
= cbuf
.st_ino
;
2005 lp
->dev
= cbuf
.st_dev
;
2006 lp
->next
= parent_dirs
;
2010 /* Now, check if the dest dir exists, if not, create it. */
2011 if (mc_stat (dst_vpath
, &buf
) != 0)
2013 /* Here the dir doesn't exist : make it ! */
2016 if (mc_rename (src_vpath
, dst_vpath
) == 0)
2018 return_status
= FILE_CONT
;
2028 * If the destination directory exists, we want to copy the whole
2029 * directory, but we only want this to happen once.
2031 * Escape sequences added to the * to compiler warnings.
2032 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2033 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2035 if (!S_ISDIR (buf
.st_mode
))
2038 return_status
= FILE_SKIPALL
;
2041 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2042 if (return_status
== FILE_SKIPALL
)
2043 ctx
->skip_all
= TRUE
;
2044 if (return_status
== FILE_RETRY
)
2045 goto retry_dst_stat
;
2049 /* Dive into subdir if exists */
2050 if (toplevel
&& ctx
->dive_into_subdirs
)
2052 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
2061 dest_dir_vpath
= vfs_path_from_str (dest_dir
);
2062 while (my_mkdir (dest_dir_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2065 return_status
= FILE_SKIPALL
;
2068 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir
);
2069 if (return_status
== FILE_SKIPALL
)
2070 ctx
->skip_all
= TRUE
;
2072 if (return_status
!= FILE_RETRY
)
2076 lp
= g_new0 (struct link
, 1);
2077 mc_stat (dest_dir_vpath
, &buf
);
2078 lp
->vfs
= vfs_path_get_by_index (dest_dir_vpath
, -1)->class;
2079 lp
->ino
= buf
.st_ino
;
2080 lp
->dev
= buf
.st_dev
;
2081 lp
->next
= dest_dirs
;
2084 if (ctx
->preserve_uidgid
)
2086 while (mc_chown (dest_dir_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2089 return_status
= FILE_SKIPALL
;
2093 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir
);
2094 if (return_status
== FILE_SKIPALL
)
2095 ctx
->skip_all
= TRUE
;
2097 if (return_status
!= FILE_RETRY
)
2103 /* open the source dir for reading */
2104 reading
= mc_opendir (src_vpath
);
2105 if (reading
== NULL
)
2108 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2111 vfs_path_t
*tmp_vpath
;
2113 * Now, we don't want '.' and '..' to be created / copied at any time
2115 if (!strcmp (next
->d_name
, "."))
2117 if (!strcmp (next
->d_name
, ".."))
2120 /* get the filename and add it to the src directory */
2121 path
= concat_dir_and_file (s
, next
->d_name
);
2122 tmp_vpath
= vfs_path_from_str (path
);
2124 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2125 if (S_ISDIR (buf
.st_mode
))
2129 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
2131 * From here, we just intend to recursively copy subdirs, not
2132 * the double functionality of copying different when the target
2133 * dir already exists. So, we give the recursive call the flag 0
2134 * meaning no toplevel.
2137 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2144 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
2145 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2148 if (do_delete
&& return_status
== FILE_CONT
)
2150 if (ctx
->erase_at_end
)
2152 static struct link
*tail
;
2153 size_t len
= strlen (path
);
2154 lp
= g_malloc (sizeof (struct link
) + len
);
2155 strncpy (lp
->name
, path
, len
+ 1);
2156 lp
->st_mode
= buf
.st_mode
;
2158 if (erase_list
!= NULL
)
2164 erase_list
= tail
= lp
;
2168 if (S_ISDIR (buf
.st_mode
))
2170 return_status
= erase_dir_iff_empty (ctx
, path
);
2173 return_status
= erase_file (tctx
, ctx
, path
);
2177 vfs_path_free (tmp_vpath
);
2179 mc_closedir (reading
);
2183 mc_chmod (dest_dir_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2184 utb
.actime
= cbuf
.st_atime
;
2185 utb
.modtime
= cbuf
.st_mtime
;
2186 mc_utime (dest_dir_vpath
, &utb
);
2190 cbuf
.st_mode
= umask (-1);
2191 umask (cbuf
.st_mode
);
2192 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2193 mc_chmod (dest_dir_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2198 vfs_path_free (dest_dir_vpath
);
2199 g_free (parent_dirs
);
2202 vfs_path_free (src_vpath
);
2203 vfs_path_free (dst_vpath
);
2204 return return_status
;
2209 /* --------------------------------------------------------------------------------------------- */
2210 /* {{{ Move routines */
2213 move_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
2215 struct stat sbuf
, dbuf
, destbuf
;
2218 FileProgressStatus return_status
;
2219 gboolean move_over
= FALSE
;
2221 vfs_path_t
*src_vpath
, *dst_vpath
, *destdir_vpath
;
2223 src_vpath
= vfs_path_from_str (s
);
2224 dst_vpath
= vfs_path_from_str (d
);
2226 file_progress_show_source (ctx
, s
);
2227 file_progress_show_target (ctx
, d
);
2228 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2233 mc_stat (src_vpath
, &sbuf
);
2235 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2237 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2238 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2241 destdir
= g_strdup (d
); /* destination doesn't exist */
2242 else if (!ctx
->dive_into_subdirs
)
2244 destdir
= g_strdup (d
);
2248 destdir
= concat_dir_and_file (d
, x_basename (s
));
2250 destdir_vpath
= vfs_path_from_str (destdir
);
2252 /* Check if the user inputted an existing dir */
2254 if (mc_stat (destdir_vpath
, &destbuf
) == 0)
2258 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
2260 if (return_status
!= FILE_CONT
)
2264 else if (ctx
->skip_all
)
2265 return_status
= FILE_SKIPALL
;
2268 if (S_ISDIR (destbuf
.st_mode
))
2269 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir
);
2271 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir
);
2272 if (return_status
== FILE_SKIPALL
)
2273 ctx
->skip_all
= TRUE
;
2274 if (return_status
== FILE_RETRY
)
2275 goto retry_dst_stat
;
2278 vfs_path_free (destdir_vpath
);
2279 vfs_path_free (src_vpath
);
2280 vfs_path_free (dst_vpath
);
2281 return return_status
;
2285 if (mc_rename (src_vpath
, destdir_vpath
) == 0)
2287 return_status
= FILE_CONT
;
2295 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2296 if (return_status
== FILE_SKIPALL
)
2297 ctx
->skip_all
= TRUE
;
2298 if (return_status
== FILE_RETRY
)
2303 /* Failed because of filesystem boundary -> copy dir instead */
2304 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
2306 if (return_status
!= FILE_CONT
)
2309 file_progress_show_source (ctx
, NULL
);
2310 file_progress_show (ctx
, 0, 0, "", FALSE
);
2312 return_status
= check_progress_buttons (ctx
);
2313 if (return_status
!= FILE_CONT
)
2317 if (ctx
->erase_at_end
)
2319 for (; erase_list
&& return_status
!= FILE_ABORT
;)
2321 if (S_ISDIR (erase_list
->st_mode
))
2323 return_status
= erase_dir_iff_empty (ctx
, erase_list
->name
);
2326 return_status
= erase_file (tctx
, ctx
, erase_list
->name
);
2328 erase_list
= erase_list
->next
;
2332 erase_dir_iff_empty (ctx
, s
);
2336 vfs_path_free (destdir_vpath
);
2340 erase_list
= erase_list
->next
;
2343 vfs_path_free (src_vpath
);
2344 vfs_path_free (dst_vpath
);
2345 return return_status
;
2350 /* --------------------------------------------------------------------------------------------- */
2351 /* {{{ Erase routines */
2354 erase_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
2356 FileProgressStatus error
;
2358 if (strcmp (s
, "..") == 0)
2361 if (strcmp (s
, ".") == 0)
2364 file_progress_show_deleting (ctx
, s
);
2365 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2369 /* The old way to detect a non empty directory was:
2370 error = my_rmdir (s);
2371 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2372 For the linux user space nfs server (nfs-server-2.2beta29-2)
2373 we would have to check also for EIO. I hope the new way is
2374 fool proof. (Norbert)
2376 error
= check_dir_is_empty (s
);
2379 error
= query_recursive (ctx
, s
);
2380 if (error
== FILE_CONT
)
2381 return recursive_erase (tctx
, ctx
, s
);
2386 while (my_rmdir (s
) == -1 && !ctx
->skip_all
)
2388 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
2389 if (error
!= FILE_RETRY
)
2398 /* --------------------------------------------------------------------------------------------- */
2399 /* {{{ Panel operate routines */
2402 compute_dir_size_create_ui (void)
2404 ComputeDirSizeUI
*ui
;
2406 const char *b_name
= N_("&Abort");
2412 ui
= g_new (ComputeDirSizeUI
, 1);
2414 ui
->dlg
= create_dlg (TRUE
, 0, 0, 8, COLS
/ 2, dialog_colors
, NULL
,
2415 NULL
, _("Directory scanning"), DLG_CENTER
);
2416 ui
->dirname
= label_new (3, 3, "");
2417 add_widget (ui
->dlg
, ui
->dirname
);
2419 add_widget (ui
->dlg
,
2420 button_new (5, (ui
->dlg
->cols
- strlen (b_name
)) / 2,
2421 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
2423 /* We will manage the dialog without any help,
2424 that's why we have to call init_dlg */
2430 /* --------------------------------------------------------------------------------------------- */
2433 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
2437 /* schedule to update passive panel */
2438 other_panel
->dirty
= 1;
2440 /* close and destroy dialog */
2441 dlg_run_done (ui
->dlg
);
2442 destroy_dlg (ui
->dlg
);
2447 /* --------------------------------------------------------------------------------------------- */
2450 compute_dir_size_update_ui (const void *ui
, const vfs_path_t
* dirname_vpath
)
2452 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
2460 dirname
= vfs_path_to_str (dirname_vpath
);
2461 label_set_text (this->dirname
, str_trunc (dirname
, this->dlg
->cols
- 6));
2464 event
.x
= -1; /* Don't show the GPM cursor */
2465 c
= tty_get_event (&event
, FALSE
, FALSE
);
2469 /* Reinitialize to avoid old values after events other than
2470 selecting a button */
2471 this->dlg
->ret_value
= FILE_CONT
;
2473 dlg_process_event (this->dlg
, c
, &event
);
2475 switch (this->dlg
->ret_value
)
2485 /* --------------------------------------------------------------------------------------------- */
2489 * Computes the number of bytes used by the files in a directory
2493 compute_dir_size (const vfs_path_t
* dirname_vpath
, const void *ui
,
2494 compute_dir_size_callback cback
,
2495 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
2500 struct dirent
*dirent
;
2501 FileProgressStatus ret
= FILE_CONT
;
2503 if (!compute_symlinks
)
2505 res
= mc_lstat (dirname_vpath
, &s
);
2509 /* don't scan symlink to directory */
2510 if (S_ISLNK (s
.st_mode
))
2513 *ret_total
+= (uintmax_t) s
.st_size
;
2518 dir
= mc_opendir (dirname_vpath
);
2523 while ((dirent
= mc_readdir (dir
)) != NULL
)
2525 vfs_path_t
*tmp_vpath
;
2527 ret
= (cback
!= NULL
) ? cback (ui
, dirname_vpath
) : FILE_CONT
;
2529 if (ret
!= FILE_CONT
)
2532 if (strcmp (dirent
->d_name
, ".") == 0)
2534 if (strcmp (dirent
->d_name
, "..") == 0)
2537 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, NULL
);
2538 res
= mc_lstat (tmp_vpath
, &s
);
2541 if (S_ISDIR (s
.st_mode
))
2543 size_t subdir_count
= 0;
2544 uintmax_t subdir_bytes
= 0;
2547 compute_dir_size (tmp_vpath
, ui
, cback
, &subdir_count
, &subdir_bytes
,
2550 if (ret
!= FILE_CONT
)
2552 vfs_path_free (tmp_vpath
);
2556 *ret_marked
+= subdir_count
;
2557 *ret_total
+= subdir_bytes
;
2562 *ret_total
+= (uintmax_t) s
.st_size
;
2565 vfs_path_free (tmp_vpath
);
2572 /* --------------------------------------------------------------------------------------------- */
2576 * Performs one of the operations on the selection on the source_panel
2577 * (copy, delete, move).
2579 * Returns TRUE if did change the directory
2580 * structure, Returns FALSE if user aborted
2582 * force_single forces operation on the current entry and affects
2583 * default destination. Current filename is used as default.
2587 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2589 WPanel
*panel
= (WPanel
*) source_panel
;
2590 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2591 || (get_current_type () == view_tree
);
2593 char *source
= NULL
;
2594 #ifdef WITH_FULL_PATHS
2595 vfs_path_t
*source_with_vpath
= NULL
;
2596 char *source_with_path_str
= NULL
;
2598 #define source_with_path source
2599 #endif /* !WITH_FULL_PATHS */
2601 vfs_path_t
*dest_vpath
= NULL
;
2603 char *save_cwd
= NULL
, *save_dest
= NULL
;
2604 struct stat src_stat
;
2605 gboolean ret_val
= TRUE
;
2607 FileProgressStatus value
;
2609 FileOpTotalContext
*tctx
;
2610 vfs_path_t
*tmp_vpath
;
2612 gboolean do_bg
= FALSE
; /* do background operation? */
2614 static gboolean i18n_flag
= FALSE
;
2617 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
2618 op_names
[i
] = Q_ (op_names
[i
]);
2622 free_linklist (&linklist
);
2623 free_linklist (&dest_dirs
);
2627 vfs_path_t
*source_vpath
;
2630 source
= g_strdup (selection (panel
)->fname
);
2632 source
= panel_get_file (panel
);
2634 if (strcmp (source
, "..") == 0)
2637 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2641 source_vpath
= vfs_path_from_str (source
);
2642 /* Update stat to get actual info */
2643 if (mc_lstat (source_vpath
, &src_stat
) != 0)
2645 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2646 path_trunc (source
, 30), unix_error_string (errno
));
2648 /* Directory was changed outside MC. Reload it forced */
2649 if (!panel
->is_panelized
)
2651 panel_update_flags_t flags
= UP_RELOAD
;
2653 /* don't update panelized panel */
2654 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2655 flags
|= UP_ONLY_CURRENT
;
2657 update_panels (flags
, UP_KEEPSEL
);
2659 vfs_path_free (source_vpath
);
2662 vfs_path_free (source_vpath
);
2665 ctx
= file_op_context_new (operation
);
2667 /* Show confirmation dialog */
2668 if (operation
!= OP_DELETE
)
2670 char *tmp_dest_dir
, *dest_dir
;
2673 /* Forced single operations default to the original name */
2675 tmp_dest_dir
= g_strdup (source
);
2676 else if (get_other_type () == view_listing
)
2677 tmp_dest_dir
= vfs_path_to_str (other_panel
->cwd_vpath
);
2679 tmp_dest_dir
= vfs_path_to_str (panel
->cwd_vpath
);
2681 * Add trailing backslash only when do non-local ops.
2682 * It saves user from occasional file renames (when destination
2685 if (!force_single
&& tmp_dest_dir
[0] != '\0'
2686 && tmp_dest_dir
[strlen (tmp_dest_dir
) - 1] != PATH_SEP
)
2688 /* add trailing separator */
2689 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2690 g_free (tmp_dest_dir
);
2695 dest_dir
= tmp_dest_dir
;
2697 if (dest_dir
== NULL
)
2703 /* Generate confirmation prompt */
2704 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2706 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2707 source
!= NULL
? (void *) source
2708 : (void *) &panel
->marked
, dest_dir
, &do_bg
);
2713 if (dest
== NULL
|| dest
[0] == '\0')
2719 dest_vpath
= vfs_path_from_str (dest
);
2721 else if (confirm_delete
)
2724 char fmd_buf
[BUF_MEDIUM
];
2726 /* Generate confirmation prompt */
2727 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2730 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2733 const int fmd_xlen
= 64;
2734 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2735 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2743 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2752 tctx
= file_op_total_context_new ();
2753 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2756 filegui_dialog_type_t dialog_type
;
2758 if (operation
== OP_DELETE
)
2759 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2762 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2763 ? FILEGUI_DIALOG_MULTI_ITEM
: FILEGUI_DIALOG_ONE_ITEM
;
2765 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2766 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2769 /* Background also need ctx->ui, but not full */
2771 file_op_context_create_ui_without_init (ctx
, TRUE
, dialog_type
);
2773 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
2776 #ifdef ENABLE_BACKGROUND
2777 /* Did the user select to do a background operation? */
2783 cwd_str
= vfs_path_to_str (panel
->cwd_vpath
);
2784 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", cwd_str
, (char *) NULL
));
2787 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2789 /* If we are the parent */
2792 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2794 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2795 vfs_path_free (dest_vpath
);
2797 /* file_op_context_destroy (ctx); */
2801 #endif /* ENABLE_BACKGROUND */
2803 /* Initialize things */
2804 /* We do not want to trash cache every time file is
2805 created/touched. However, this will make our cache contain
2807 if ((dest
!= NULL
) && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2808 save_dest
= g_strdup (dest
);
2810 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2811 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2812 save_cwd
= vfs_path_to_str (panel
->cwd_vpath
);
2814 /* Now, let's do the job */
2816 /* This code is only called by the tree and panel code */
2819 /* We now have ETA in all cases */
2821 /* One file: FIXME mc_chdir will take user out of any vfs */
2822 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2827 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2828 chdir_retcode
= mc_chdir (vpath
);
2829 vfs_path_free (vpath
);
2830 if (chdir_retcode
< 0)
2837 /* The source and src_stat variables have been initialized before */
2838 #ifdef WITH_FULL_PATHS
2839 if (g_path_is_absolute (source
))
2840 source_with_vpath
= vfs_path_from_str (source
);
2842 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2843 source_with_path_str
= vfs_path_to_str (source_with_vpath
);
2844 #endif /* WITH_FULL_PATHS */
2845 if (panel_operate_init_totals (operation
, panel
, source_with_path_str
, ctx
) == FILE_CONT
)
2847 if (operation
== OP_DELETE
)
2849 if (S_ISDIR (src_stat
.st_mode
))
2850 value
= erase_dir (tctx
, ctx
, source_with_path_str
);
2852 value
= erase_file (tctx
, ctx
, source_with_path_str
);
2856 temp
= transform_source (ctx
, source_with_path_str
);
2858 value
= transform_error
;
2861 char *repl_dest
, *temp2
;
2863 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2864 temp2
= concat_dir_and_file (repl_dest
, temp
);
2868 vfs_path_free (dest_vpath
);
2870 dest_vpath
= vfs_path_from_str (dest
);
2875 /* we use file_mask_op_follow_links only with OP_COPY */
2876 ctx
->stat_func (source_with_vpath
, &src_stat
);
2878 if (S_ISDIR (src_stat
.st_mode
))
2879 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, dest
,
2880 TRUE
, FALSE
, FALSE
, NULL
);
2882 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, dest
);
2886 if (S_ISDIR (src_stat
.st_mode
))
2887 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, dest
);
2889 value
= move_file_file (tctx
, ctx
, source_with_path_str
, dest
);
2893 /* Unknown file operation */
2897 } /* Copy or move operation */
2899 if ((value
== FILE_CONT
) && !force_single
)
2900 unmark_files (panel
);
2907 /* Check destination for copy or move operation */
2908 while (operation
!= OP_DELETE
)
2911 struct stat dst_stat
;
2913 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2915 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2919 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2923 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
)
2925 /* Loop for every file, perform the actual copy operation */
2926 for (i
= 0; i
< panel
->count
; i
++)
2928 const char *source2
;
2930 if (!panel
->dir
.list
[i
].f
.marked
)
2931 continue; /* Skip the unmarked ones */
2933 source2
= panel
->dir
.list
[i
].fname
;
2934 src_stat
= panel
->dir
.list
[i
].st
;
2936 #ifdef WITH_FULL_PATHS
2937 g_free (source_with_path_str
);
2938 vfs_path_free (source_with_vpath
);
2939 if (g_path_is_absolute (source2
))
2940 source_with_vpath
= vfs_path_from_str (source2
);
2943 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2944 source_with_path_str
= vfs_path_to_str (source_with_vpath
);
2945 #endif /* WITH_FULL_PATHS */
2947 if (operation
== OP_DELETE
)
2949 if (S_ISDIR (src_stat
.st_mode
))
2950 value
= erase_dir (tctx
, ctx
, source_with_path_str
);
2952 value
= erase_file (tctx
, ctx
, source_with_path_str
);
2956 temp
= transform_source (ctx
, source_with_path_str
);
2959 value
= transform_error
;
2962 char *temp2
, *temp3
, *repl_dest
;
2964 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2965 temp2
= concat_dir_and_file (repl_dest
, temp
);
2968 temp3
= source_with_path_str
;
2969 source_with_path_str
= strutils_shell_unescape (source_with_path_str
);
2972 temp2
= strutils_shell_unescape (temp2
);
2978 /* we use file_mask_op_follow_links only with OP_COPY */
2982 vpath
= vfs_path_from_str (source_with_path_str
);
2983 ctx
->stat_func (vpath
, &src_stat
);
2984 vfs_path_free (vpath
);
2986 if (S_ISDIR (src_stat
.st_mode
))
2987 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
,
2988 TRUE
, FALSE
, FALSE
, NULL
);
2990 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
2991 free_linklist (&dest_dirs
);
2995 if (S_ISDIR (src_stat
.st_mode
))
2996 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
);
2998 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
3002 /* Unknown file operation */
3008 } /* Copy or move operation */
3010 if (value
== FILE_ABORT
)
3013 if (value
== FILE_CONT
)
3014 do_file_mark (panel
, i
, 0);
3016 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3018 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3019 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3022 if (operation
!= OP_DELETE
)
3023 file_progress_show (ctx
, 0, 0, "", FALSE
);
3025 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3029 } /* Loop for every file */
3031 } /* Many entries */
3035 if (save_cwd
!= NULL
)
3037 tmp_vpath
= vfs_path_from_str (save_cwd
);
3038 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3039 vfs_path_free (tmp_vpath
);
3043 if (save_dest
!= NULL
)
3045 tmp_vpath
= vfs_path_from_str (save_dest
);
3046 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3047 vfs_path_free (tmp_vpath
);
3051 free_linklist (&linklist
);
3052 free_linklist (&dest_dirs
);
3053 #ifdef WITH_FULL_PATHS
3054 g_free (source_with_path_str
);
3055 vfs_path_free (source_with_vpath
);
3056 #endif /* WITH_FULL_PATHS */
3058 vfs_path_free (dest_vpath
);
3059 g_free (ctx
->dest_mask
);
3060 ctx
->dest_mask
= NULL
;
3062 #ifdef ENABLE_BACKGROUND
3063 /* Let our parent know we are saying bye bye */
3064 if (mc_global
.we_are_background
)
3066 int cur_pid
= getpid ();
3067 /* Send pid to parent with child context, it is fork and
3068 don't modify real parent ctx */
3070 parent_call ((void *) end_bg_process
, ctx
, 0);
3075 #endif /* ENABLE_BACKGROUND */
3077 file_op_total_context_destroy (tctx
);
3079 file_op_context_destroy (ctx
);
3087 /* --------------------------------------------------------------------------------------------- */
3088 /* {{{ Query/status report routines */
3089 /** Report error with one file */
3091 file_error (const char *format
, const char *file
)
3093 char buf
[BUF_MEDIUM
];
3095 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3097 return do_file_error (buf
);
3100 /* --------------------------------------------------------------------------------------------- */
3103 Cause emacs to enter folding mode for this file: