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
;
120 vfs_path_t
*src_vpath
;
121 vfs_path_t
*dst_vpath
;
124 /* Status of the destination file */
127 DEST_NONE
= 0, /* Not created */
128 DEST_SHORT
= 1, /* Created, not fully copied */
129 DEST_FULL
= 2 /* Created, fully copied */
133 * This array introduced to avoid translation problems. The former (op_names)
134 * is assumed to be nouns, suitable in dialog box titles; this one should
135 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
136 * (I don't use spaces around the words, because someday they could be
137 * dropped, when widgets get smarter)
140 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
141 static const char *op_names1
[] = {
142 N_("FileOperation|Copy"),
143 N_("FileOperation|Move"),
144 N_("FileOperation|Delete")
148 * These are formats for building a prompt. Parts encoded as follows:
149 * %o - operation from op_names1
150 * %f - file/files or files/directories, as appropriate
151 * %m - "with source mask" or question mark for delete
152 * %s - source name (truncated)
153 * %d - number of marked files
154 * %e - "to:" or question mark for delete
156 * xgettext:no-c-format */
157 static const char *one_format
= N_("%o %f \"%s\"%m");
158 /* xgettext:no-c-format */
159 static const char *many_format
= N_("%o %d %f%m");
161 static const char *prompt_parts
[] = {
166 N_("files/directories"),
167 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
168 N_(" with source mask:"),
172 static const char *question_format
= N_("%s?");
174 /*** file scope variables ************************************************************************/
176 /* the hard link cache */
177 static struct link
*linklist
= NULL
;
179 /* the files-to-be-erased list */
180 static struct link
*erase_list
;
183 * In copy_dir_dir we use two additional single linked lists: The first -
184 * variable name `parent_dirs' - holds information about already copied
185 * directories and is used to detect cyclic symbolic links.
186 * The second (`dest_dirs' below) holds information about just created
187 * target directories and is used to detect when an directory is copied
188 * into itself (we don't want to copy infinitly).
189 * Both lists don't use the linkcount and name structure members of struct
192 static struct link
*dest_dirs
= NULL
;
194 static FileProgressStatus transform_error
= FILE_CONT
;
196 /*** file scope functions ************************************************************************/
197 /* --------------------------------------------------------------------------------------------- */
200 transform_source (FileOpContext
* ctx
, const char *source
)
205 s
= g_strdup (source
);
207 /* We remove \n from the filename since regex routines would use \n as an anchor */
208 /* this is just to be allowed to maniupulate file names with \n on it */
209 for (q
= s
; *q
!= '\0'; q
++)
213 fnsource
= (char *) x_basename (s
);
215 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
216 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
220 transform_error
= FILE_SKIP
;
227 /* --------------------------------------------------------------------------------------------- */
230 free_linklist (struct link
**lc_linklist
)
232 struct link
*lp
, *lp2
;
234 for (lp
= *lc_linklist
; lp
!= NULL
; lp
= lp2
)
237 vfs_path_free (lp
->src_vpath
);
238 vfs_path_free (lp
->dst_vpath
);
244 /* --------------------------------------------------------------------------------------------- */
247 is_in_linklist (struct link
*lp
, const vfs_path_t
* vpath
, struct stat
*sb
)
249 struct vfs_class
*class;
250 ino_t ino
= sb
->st_ino
;
251 dev_t dev
= sb
->st_dev
;
253 class = vfs_path_get_last_path_vfs (vpath
);
257 if (lp
->vfs
== class)
258 if (lp
->ino
== ino
&& lp
->dev
== dev
)
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 vfs_path_t
* src_vpath
, const vfs_path_t
* dst_vpath
, struct stat
*pstat
)
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
;
283 if ((vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
286 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
288 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
289 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
)
291 struct vfs_class
*lp_name_class
;
294 lp_name_class
= vfs_path_get_last_path_vfs (lp
->src_vpath
);
295 stat_result
= mc_stat (lp
->src_vpath
, &link_stat
);
297 if (stat_result
== 0 && link_stat
.st_ino
== ino
298 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
300 struct vfs_class
*p_class
, *dst_name_class
;
302 dst_name_class
= vfs_path_get_last_path_vfs (dst_vpath
);
303 p_class
= vfs_path_get_last_path_vfs (lp
->dst_vpath
);
305 if (dst_name_class
== p_class
&&
306 mc_stat (lp
->dst_vpath
, &link_stat
) == 0 &&
307 mc_link (lp
->dst_vpath
, dst_vpath
) == 0)
311 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
315 lp
= g_new0 (struct link
, 1);
322 lp
->src_vpath
= vfs_path_clone (src_vpath
);
323 lp
->dst_vpath
= vfs_path_clone (dst_vpath
);
330 /* --------------------------------------------------------------------------------------------- */
332 * Duplicate the contents of the symbolic link src_path in dst_path.
333 * Try to make a stable symlink if the option "stable symlink" was
334 * set in the file mask dialog.
335 * If dst_path is an existing symlink it will be deleted silently
336 * (upper levels take already care of existing files at dst_path).
339 static FileProgressStatus
340 make_symlink (FileOpContext
* ctx
, const char *src_path
, const char *dst_path
)
342 char link_target
[MC_MAXPATHLEN
];
344 FileProgressStatus return_status
;
346 vfs_path_t
*src_vpath
;
347 vfs_path_t
*dst_vpath
;
348 gboolean dst_is_symlink
;
349 vfs_path_t
*link_target_vpath
= NULL
;
351 src_vpath
= vfs_path_from_str (src_path
);
352 dst_vpath
= vfs_path_from_str (dst_path
);
353 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
356 len
= mc_readlink (src_vpath
, link_target
, MC_MAXPATHLEN
- 1);
360 return_status
= FILE_SKIPALL
;
363 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
364 if (return_status
== FILE_SKIPALL
)
365 ctx
->skip_all
= TRUE
;
366 if (return_status
== FILE_RETRY
)
367 goto retry_src_readlink
;
371 link_target
[len
] = 0;
373 if (ctx
->stable_symlinks
)
376 if (!vfs_file_is_local (src_vpath
) || !vfs_file_is_local (dst_vpath
))
378 message (D_ERROR
, MSG_ERROR
,
379 _("Cannot make stable symlinks across"
380 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
381 ctx
->stable_symlinks
= FALSE
;
385 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
390 const char *r
= strrchr (src_path
, PATH_SEP
);
394 p
= g_strndup (src_path
, r
- src_path
+ 1);
395 if (g_path_is_absolute (dst_path
))
396 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
398 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
400 if (vfs_path_tokens_count (q
) > 1)
402 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
404 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
405 s
= g_strconcat (p
, link_target
, (char *) NULL
);
407 g_strlcpy (link_target
, s
, sizeof (link_target
));
409 tmp_vpath2
= vfs_path_from_str (link_target
);
410 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
411 vfs_path_free (tmp_vpath1
);
412 vfs_path_free (tmp_vpath2
);
415 g_strlcpy (link_target
, s
, sizeof (link_target
));
424 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
427 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
430 return_status
= FILE_CONT
;
434 * if dst_exists, it is obvious that this had failed.
435 * We can delete the old symlink and try again...
439 if (mc_unlink (dst_vpath
) == 0)
440 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
443 return_status
= FILE_CONT
;
448 return_status
= FILE_SKIPALL
;
451 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
452 if (return_status
== FILE_SKIPALL
)
453 ctx
->skip_all
= TRUE
;
454 if (return_status
== FILE_RETRY
)
455 goto retry_dst_symlink
;
459 vfs_path_free (src_vpath
);
460 vfs_path_free (dst_vpath
);
461 vfs_path_free (link_target_vpath
);
462 return return_status
;
465 /* --------------------------------------------------------------------------------------------- */
467 static FileProgressStatus
468 progress_update_one (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, off_t add
)
470 struct timeval tv_current
;
471 static struct timeval tv_start
= { };
473 tctx
->progress_count
++;
474 tctx
->progress_bytes
+= (uintmax_t) add
;
476 if (tv_start
.tv_sec
== 0)
478 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
480 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
481 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
483 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
485 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
486 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
488 tv_start
.tv_sec
= tv_current
.tv_sec
;
491 return check_progress_buttons (ctx
);
494 /* --------------------------------------------------------------------------------------------- */
496 static FileProgressStatus
497 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
501 const char *head_msg
;
503 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
505 msg
= g_strdup_printf (fmt
, a
, b
);
506 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
510 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
513 /* --------------------------------------------------------------------------------------------- */
515 static FileProgressStatus
516 warn_same_file (const char *fmt
, const char *a
, const char *b
)
518 #ifdef ENABLE_BACKGROUND
523 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
524 const char *a
, const char *b
);
528 pntr
.f
= real_warn_same_file
;
530 if (mc_global
.we_are_background
)
531 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
533 return real_warn_same_file (Foreground
, fmt
, a
, b
);
536 /* --------------------------------------------------------------------------------------------- */
537 /* {{{ Query/status report routines */
539 static FileProgressStatus
540 real_do_file_error (enum OperationMode mode
, const char *error
)
545 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
547 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
569 /* --------------------------------------------------------------------------------------------- */
571 static FileProgressStatus
572 real_query_recursive (FileOpContext
* ctx
, enum OperationMode mode
, const char *s
)
576 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
578 const char *msg
= mode
== Foreground
579 ? _("\nDirectory not empty.\nDelete it recursively?")
580 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
581 text
= g_strconcat (_("Delete:"), " ", path_trunc (s
, 30), (char *) NULL
);
586 ctx
->recursive_result
=
587 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
588 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
590 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
595 switch (ctx
->recursive_result
)
598 case RECURSIVE_ALWAYS
:
602 case RECURSIVE_NEVER
:
605 case RECURSIVE_ABORT
:
611 /* --------------------------------------------------------------------------------------------- */
613 #ifdef ENABLE_BACKGROUND
614 static FileProgressStatus
615 do_file_error (const char *str
)
620 FileProgressStatus (*f
) (enum OperationMode
, const char *);
622 pntr
.f
= real_do_file_error
;
624 if (mc_global
.we_are_background
)
625 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
627 return real_do_file_error (Foreground
, str
);
630 /* --------------------------------------------------------------------------------------------- */
632 static FileProgressStatus
633 query_recursive (FileOpContext
* ctx
, const char *s
)
638 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
640 pntr
.f
= real_query_recursive
;
642 if (mc_global
.we_are_background
)
643 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
645 return real_query_recursive (ctx
, Foreground
, s
);
648 /* --------------------------------------------------------------------------------------------- */
650 static FileProgressStatus
651 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
652 struct stat
*_d_stat
)
657 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
658 struct stat
*, struct stat
*);
660 pntr
.f
= file_progress_real_query_replace
;
662 if (mc_global
.we_are_background
)
663 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
664 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
666 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
670 /* --------------------------------------------------------------------------------------------- */
672 static FileProgressStatus
673 do_file_error (const char *str
)
675 return real_do_file_error (Foreground
, str
);
678 /* --------------------------------------------------------------------------------------------- */
680 static FileProgressStatus
681 query_recursive (FileOpContext
* ctx
, const char *s
)
683 return real_query_recursive (ctx
, Foreground
, s
);
686 /* --------------------------------------------------------------------------------------------- */
688 static FileProgressStatus
689 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
690 struct stat
*_d_stat
)
692 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
695 #endif /* !ENABLE_BACKGROUND */
697 /* --------------------------------------------------------------------------------------------- */
698 /** Report error with two files */
700 static FileProgressStatus
701 files_error (const char *format
, const char *file1
, const char *file2
)
703 char buf
[BUF_MEDIUM
];
704 char *nfile1
= g_strdup (path_trunc (file1
, 15));
705 char *nfile2
= g_strdup (path_trunc (file2
, 15));
707 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
712 return do_file_error (buf
);
717 /* --------------------------------------------------------------------------------------------- */
720 copy_file_file_display_progress (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
721 struct timeval tv_current
, struct timeval tv_transfer_start
,
722 off_t file_size
, off_t n_read_total
)
726 /* 1. Update rotating dash after some time */
730 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
732 if (n_read_total
== 0)
736 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
737 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
740 /* 4. Compute BPS rate */
741 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
742 if (ctx
->bps_time
< 1)
744 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
746 /* 5. Compute total ETA and BPS */
747 if (ctx
->progress_bytes
!= 0)
749 uintmax_t remain_bytes
;
751 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
754 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
759 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
760 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
763 /* broken on lot of little files */
765 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
766 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
771 /* --------------------------------------------------------------------------------------------- */
773 /* {{{ Move routines */
774 static FileProgressStatus
775 move_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
777 struct stat src_stats
, dst_stats
;
778 FileProgressStatus return_status
= FILE_CONT
;
779 gboolean copy_done
= FALSE
;
780 gboolean old_ask_overwrite
;
781 vfs_path_t
*src_vpath
, *dst_vpath
;
783 src_vpath
= vfs_path_from_str (s
);
784 dst_vpath
= vfs_path_from_str (d
);
786 file_progress_show_source (ctx
, src_vpath
);
787 file_progress_show_target (ctx
, dst_vpath
);
789 if (check_progress_buttons (ctx
) == FILE_ABORT
)
791 return_status
= FILE_ABORT
;
797 while (mc_lstat (src_vpath
, &src_stats
) != 0)
799 /* Source doesn't exist */
801 return_status
= FILE_SKIPALL
;
804 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
805 if (return_status
== FILE_SKIPALL
)
806 ctx
->skip_all
= TRUE
;
809 if (return_status
!= FILE_RETRY
)
813 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
815 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
817 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
821 if (S_ISDIR (dst_stats
.st_mode
))
823 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
825 return_status
= FILE_SKIP
;
829 if (confirm_overwrite
)
831 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
832 if (return_status
!= FILE_CONT
)
835 /* Ok to overwrite */
840 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
842 return_status
= make_symlink (ctx
, s
, d
);
843 if (return_status
== FILE_CONT
)
844 goto retry_src_remove
;
848 if (mc_rename (src_vpath
, dst_vpath
) == 0)
850 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
855 /* Comparison to EXDEV seems not to work in nfs if you're moving from
856 one nfs to the same, but on the server it is on two different
857 filesystems. Then nfs returns EIO instead of EXDEV.
858 Hope it will not hurt if we always in case of error try to copy/delete. */
860 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
865 return_status
= FILE_SKIPALL
;
868 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
869 if (return_status
== FILE_SKIPALL
)
870 ctx
->skip_all
= TRUE
;
871 if (return_status
== FILE_RETRY
)
879 /* Failed because filesystem boundary -> copy the file instead */
880 old_ask_overwrite
= tctx
->ask_overwrite
;
881 tctx
->ask_overwrite
= FALSE
;
882 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
883 tctx
->ask_overwrite
= old_ask_overwrite
;
884 if (return_status
!= FILE_CONT
)
889 file_progress_show_source (ctx
, NULL
);
890 file_progress_show (ctx
, 0, 0, "", FALSE
);
892 return_status
= check_progress_buttons (ctx
);
893 if (return_status
!= FILE_CONT
)
898 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
900 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
901 if (return_status
== FILE_RETRY
)
902 goto retry_src_remove
;
903 if (return_status
== FILE_SKIPALL
)
904 ctx
->skip_all
= TRUE
;
909 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
912 vfs_path_free (src_vpath
);
913 vfs_path_free (dst_vpath
);
915 return return_status
;
920 /* --------------------------------------------------------------------------------------------- */
921 /* {{{ Erase routines */
922 /** Don't update progress status if progress_count==NULL */
924 static FileProgressStatus
925 erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const vfs_path_t
* vpath
)
931 s
= vfs_path_to_str (vpath
);
932 file_progress_show_deleting (ctx
, s
);
933 if (check_progress_buttons (ctx
) == FILE_ABORT
)
940 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
942 /* ignore, most likely the mc_unlink fails, too */
946 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
948 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), s
);
949 if (return_status
== FILE_ABORT
)
952 return return_status
;
954 if (return_status
== FILE_RETRY
)
956 if (return_status
== FILE_SKIPALL
)
957 ctx
->skip_all
= TRUE
;
961 if (tctx
->progress_count
== 0)
963 return progress_update_one (tctx
, ctx
, buf
.st_size
);
966 /* --------------------------------------------------------------------------------------------- */
969 Recursive remove of files
971 skip ->warn every level, gets default
972 skipall->remove as much as possible
974 static FileProgressStatus
975 recursive_erase (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
981 FileProgressStatus return_status
= FILE_CONT
;
984 if (strcmp (s
, "..") == 0)
987 vpath
= vfs_path_from_str (s
);
988 reading
= mc_opendir (vpath
);
992 return_status
= FILE_RETRY
;
996 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
998 vfs_path_t
*tmp_vpath
;
1000 if (!strcmp (next
->d_name
, "."))
1002 if (!strcmp (next
->d_name
, ".."))
1004 path
= mc_build_filename (s
, next
->d_name
, NULL
);
1005 tmp_vpath
= vfs_path_from_str (path
);
1006 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1009 mc_closedir (reading
);
1010 vfs_path_free (tmp_vpath
);
1011 return_status
= FILE_RETRY
;
1014 if (S_ISDIR (buf
.st_mode
))
1015 return_status
= recursive_erase (tctx
, ctx
, path
);
1017 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
1018 vfs_path_free (tmp_vpath
);
1021 mc_closedir (reading
);
1022 if (return_status
== FILE_ABORT
)
1025 file_progress_show_deleting (ctx
, s
);
1026 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1028 return_status
= FILE_ABORT
;
1033 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1035 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1036 if (return_status
== FILE_RETRY
)
1038 if (return_status
== FILE_ABORT
)
1040 if (return_status
== FILE_SKIPALL
)
1041 ctx
->skip_all
= TRUE
;
1046 vfs_path_free (vpath
);
1047 return return_status
;
1050 /* --------------------------------------------------------------------------------------------- */
1051 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1052 in the directory path points to, 0 else. */
1055 check_dir_is_empty (const vfs_path_t
* vpath
)
1061 dir
= mc_opendir (vpath
);
1065 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
))
1067 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1068 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1069 continue; /* "." or ".." */
1078 /* --------------------------------------------------------------------------------------------- */
1080 static FileProgressStatus
1081 erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
)
1083 FileProgressStatus error
;
1084 vfs_path_t
*s_vpath
;
1086 if (strcmp (s
, "..") == 0)
1089 if (strcmp (s
, ".") == 0)
1092 file_progress_show_deleting (ctx
, s
);
1093 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1098 s_vpath
= vfs_path_from_str (s
);
1100 if (check_dir_is_empty (s_vpath
) == 1) /* not empty or error */
1102 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1104 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1105 if (error
== FILE_SKIPALL
)
1106 ctx
->skip_all
= TRUE
;
1107 if (error
!= FILE_RETRY
)
1109 vfs_path_free (s_vpath
);
1115 vfs_path_free (s_vpath
);
1121 /* --------------------------------------------------------------------------------------------- */
1122 /* {{{ Panel operate routines */
1125 * Return currently selected entry name or the name of the first marked
1126 * entry if there is one.
1130 panel_get_file (WPanel
* panel
)
1132 if (get_current_type () == view_tree
)
1136 tree
= (WTree
*) get_panel_widget (get_current_index ());
1137 return vfs_path_to_str (tree_selected_name (tree
));
1140 if (panel
->marked
!= 0)
1144 for (i
= 0; i
< panel
->count
; i
++)
1145 if (panel
->dir
.list
[i
].f
.marked
)
1146 return g_strdup (panel
->dir
.list
[i
].fname
);
1148 return g_strdup (panel
->dir
.list
[panel
->selected
].fname
);
1151 /* --------------------------------------------------------------------------------------------- */
1153 * panel_compute_totals:
1155 * compute the number of files and the number of bytes
1156 * used up by the whole selection, recursing directories
1157 * as required. In addition, it checks to see if it will
1158 * overwrite any files by doing the copy.
1161 static FileProgressStatus
1162 panel_compute_totals (const WPanel
* panel
, const void *ui
,
1163 compute_dir_size_callback cback
,
1164 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
1171 for (i
= 0; i
< panel
->count
; i
++)
1175 if (!panel
->dir
.list
[i
].f
.marked
)
1178 s
= &panel
->dir
.list
[i
].st
;
1180 if (S_ISDIR (s
->st_mode
))
1183 size_t subdir_count
= 0;
1184 uintmax_t subdir_bytes
= 0;
1185 FileProgressStatus status
;
1187 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, NULL
);
1189 compute_dir_size (p
, ui
, cback
, &subdir_count
, &subdir_bytes
, compute_symlinks
);
1192 if (status
!= FILE_CONT
)
1195 *ret_marked
+= subdir_count
;
1196 *ret_total
+= subdir_bytes
;
1201 *ret_total
+= (uintmax_t) s
->st_size
;
1208 /* --------------------------------------------------------------------------------------------- */
1210 /** Initialize variables for progress bars */
1211 static FileProgressStatus
1212 panel_operate_init_totals (FileOperation operation
,
1213 const WPanel
* panel
, const char *source
, FileOpContext
* ctx
)
1215 FileProgressStatus status
;
1217 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
)
1219 ComputeDirSizeUI
*ui
;
1221 ui
= compute_dir_size_create_ui ();
1224 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1225 &ctx
->progress_count
, &ctx
->progress_bytes
,
1231 p
= vfs_path_from_str (source
);
1232 status
= compute_dir_size (p
, ui
, compute_dir_size_update_ui
,
1233 &ctx
->progress_count
, &ctx
->progress_bytes
,
1238 compute_dir_size_destroy_ui (ui
);
1240 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1245 ctx
->progress_count
= panel
->marked
;
1246 ctx
->progress_bytes
= panel
->total
;
1247 ctx
->progress_totals_computed
= FALSE
;
1253 /* --------------------------------------------------------------------------------------------- */
1255 * Generate user prompt for panel operation.
1256 * single_source is the name if the source entry or NULL for multiple
1258 * src_stat is only used when single_source is not NULL.
1262 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1263 gboolean single_source
, const struct stat
*src_stat
)
1265 const char *sp
, *cp
;
1266 char format_string
[BUF_MEDIUM
];
1267 char *dp
= format_string
;
1268 gboolean build_question
= FALSE
;
1270 static gboolean i18n_flag
= FALSE
;
1275 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1276 op_names1
[i
] = Q_ (op_names1
[i
]);
1279 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1280 prompt_parts
[i
] = _(prompt_parts
[i
]);
1282 one_format
= _(one_format
);
1283 many_format
= _(many_format
);
1284 question_format
= _(question_format
);
1285 #endif /* ENABLE_NLS */
1289 sp
= single_source
? one_format
: many_format
;
1300 cp
= op_names1
[operation
];
1303 if (operation
== OP_DELETE
)
1306 build_question
= TRUE
;
1309 cp
= prompt_parts
[5];
1312 if (operation
== OP_DELETE
)
1315 build_question
= TRUE
;
1318 cp
= prompt_parts
[6];
1322 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1324 cp
= (panel
->marked
== panel
->dirs_marked
)
1326 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1347 char tmp
[BUF_MEDIUM
];
1349 memmove (tmp
, format_string
, sizeof (tmp
));
1350 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1353 return g_strdup (format_string
);
1356 /* --------------------------------------------------------------------------------------------- */
1358 #ifdef ENABLE_BACKGROUND
1360 end_bg_process (FileOpContext
* ctx
, enum OperationMode mode
)
1367 unregister_task_with_pid (pid
);
1368 /* file_op_context_destroy(ctx); */
1374 /* --------------------------------------------------------------------------------------------- */
1375 /*** public functions ****************************************************************************/
1376 /* --------------------------------------------------------------------------------------------- */
1379 copy_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
1380 const char *src_path
, const char *dst_path
)
1382 uid_t src_uid
= (uid_t
) (-1);
1383 gid_t src_gid
= (gid_t
) (-1);
1385 int src_desc
, dest_desc
= -1;
1386 int n_read
, n_written
;
1387 mode_t src_mode
= 0; /* The mode of the source file */
1388 struct stat sb
, sb2
;
1390 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1391 off_t file_size
= -1;
1392 FileProgressStatus return_status
, temp_status
;
1393 struct timeval tv_transfer_start
;
1394 dest_status_t dst_status
= DEST_NONE
;
1396 gboolean is_first_time
= TRUE
;
1397 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1399 /* FIXME: We should not be using global variables! */
1401 return_status
= FILE_RETRY
;
1403 dst_vpath
= vfs_path_from_str (dst_path
);
1404 src_vpath
= vfs_path_from_str (src_path
);
1406 file_progress_show_source (ctx
, src_vpath
);
1407 file_progress_show_target (ctx
, dst_vpath
);
1409 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1411 return_status
= FILE_ABORT
;
1417 while (mc_stat (dst_vpath
, &sb2
) == 0)
1419 if (S_ISDIR (sb2
.st_mode
))
1422 return_status
= FILE_SKIPALL
;
1425 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1426 if (return_status
== FILE_SKIPALL
)
1427 ctx
->skip_all
= TRUE
;
1428 if (return_status
== FILE_RETRY
)
1438 while ((*ctx
->stat_func
) (src_vpath
, &sb
) != 0)
1441 return_status
= FILE_SKIPALL
;
1444 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1445 if (return_status
== FILE_SKIPALL
)
1446 ctx
->skip_all
= TRUE
;
1449 if (return_status
!= FILE_RETRY
)
1455 /* Destination already exists */
1456 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1458 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1459 src_path
, dst_path
);
1463 /* Should we replace destination? */
1464 if (tctx
->ask_overwrite
)
1467 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1468 if (return_status
!= FILE_CONT
)
1473 if (!ctx
->do_append
)
1475 /* Check the hardlinks */
1476 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &sb
))
1478 /* We have made a hardlink - no more processing is necessary */
1479 return_status
= FILE_CONT
;
1483 if (S_ISLNK (sb
.st_mode
))
1485 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1489 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1490 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1492 while (mc_mknod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0
1495 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1496 if (return_status
== FILE_RETRY
)
1498 if (return_status
== FILE_SKIPALL
)
1499 ctx
->skip_all
= TRUE
;
1504 while (ctx
->preserve_uidgid
&& mc_chown (dst_vpath
, sb
.st_uid
, sb
.st_gid
) != 0
1507 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1508 if (temp_status
== FILE_SKIP
)
1510 if (temp_status
== FILE_SKIPALL
)
1511 ctx
->skip_all
= TRUE
;
1512 if (temp_status
!= FILE_RETRY
)
1514 return_status
= temp_status
;
1519 while (ctx
->preserve
&& mc_chmod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
) != 0
1522 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1523 if (temp_status
== FILE_SKIP
)
1525 if (temp_status
== FILE_SKIPALL
)
1526 ctx
->skip_all
= TRUE
;
1527 if (temp_status
!= FILE_RETRY
)
1529 return_status
= temp_status
;
1534 return_status
= FILE_CONT
;
1539 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1541 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1543 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1544 if (return_status
== FILE_RETRY
)
1546 if (return_status
== FILE_SKIPALL
)
1547 ctx
->skip_all
= TRUE
;
1548 if (return_status
== FILE_SKIP
)
1554 if (ctx
->do_reget
!= 0)
1556 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1558 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1560 ctx
->do_append
= FALSE
;
1564 while (mc_fstat (src_desc
, &sb
) != 0)
1567 return_status
= FILE_SKIPALL
;
1570 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1571 if (return_status
== FILE_RETRY
)
1573 if (return_status
== FILE_SKIPALL
)
1574 ctx
->skip_all
= TRUE
;
1575 ctx
->do_append
= FALSE
;
1580 src_mode
= sb
.st_mode
;
1581 src_uid
= sb
.st_uid
;
1582 src_gid
= sb
.st_gid
;
1583 utb
.actime
= sb
.st_atime
;
1584 utb
.modtime
= sb
.st_mtime
;
1585 file_size
= sb
.st_size
;
1587 open_flags
= O_WRONLY
;
1590 if (ctx
->do_append
!= 0)
1591 open_flags
|= O_APPEND
;
1593 open_flags
|= O_CREAT
| O_TRUNC
;
1597 open_flags
|= O_CREAT
| O_EXCL
;
1600 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1602 if (errno
!= EEXIST
)
1605 return_status
= FILE_SKIPALL
;
1608 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1609 if (return_status
== FILE_RETRY
)
1611 if (return_status
== FILE_SKIPALL
)
1612 ctx
->skip_all
= TRUE
;
1613 ctx
->do_append
= FALSE
;
1618 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1620 appending
= ctx
->do_append
;
1621 ctx
->do_append
= FALSE
;
1623 /* Find out the optimal buffer size. */
1624 while (mc_fstat (dest_desc
, &sb
) != 0)
1627 return_status
= FILE_SKIPALL
;
1630 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1631 if (return_status
== FILE_RETRY
)
1633 if (return_status
== FILE_SKIPALL
)
1634 ctx
->skip_all
= TRUE
;
1641 errno
= vfs_preallocate (dest_desc
, file_size
, (ctx
->do_append
!= 0) ? sb
.st_size
: 0);
1646 return_status
= FILE_SKIPALL
;
1650 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1651 if (return_status
== FILE_RETRY
)
1653 if (return_status
== FILE_SKIPALL
)
1654 ctx
->skip_all
= TRUE
;
1656 mc_close (dest_desc
);
1658 mc_unlink (dst_vpath
);
1659 dst_status
= DEST_NONE
;
1663 ctx
->eta_secs
= 0.0;
1666 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1667 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1669 file_progress_show (ctx
, 1, 1, "", TRUE
);
1670 return_status
= check_progress_buttons (ctx
);
1673 if (return_status
!= FILE_CONT
)
1677 off_t n_read_total
= 0;
1678 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1679 int secs
, update_secs
;
1680 const char *stalled_msg
= "";
1682 tv_last_update
= tv_transfer_start
;
1689 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1692 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0 && !ctx
->skip_all
)
1694 return_status
= file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
1695 if (return_status
== FILE_RETRY
)
1697 if (return_status
== FILE_SKIPALL
)
1698 ctx
->skip_all
= TRUE
;
1704 gettimeofday (&tv_current
, NULL
);
1709 n_read_total
+= n_read
;
1711 /* Windows NT ftp servers report that files have no
1712 * permissions: -------, so if we happen to have actually
1713 * read something, we should fix the permissions.
1715 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1716 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1717 gettimeofday (&tv_last_input
, NULL
);
1720 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
&& !ctx
->skip_all
)
1724 n_read
-= n_written
;
1728 return_status
= file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1729 if (return_status
== FILE_SKIP
)
1731 if (return_status
== FILE_SKIPALL
)
1732 ctx
->skip_all
= TRUE
;
1733 if (return_status
!= FILE_RETRY
)
1738 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1740 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1741 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1743 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1745 copy_file_file_display_progress (tctx
, ctx
,
1747 tv_transfer_start
, file_size
, n_read_total
);
1748 tv_last_update
= tv_current
;
1750 is_first_time
= FALSE
;
1752 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1754 stalled_msg
= _("(stalled)");
1758 gboolean force_update
;
1761 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1763 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1765 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1766 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1769 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1774 return_status
= check_progress_buttons (ctx
);
1776 if (return_status
!= FILE_CONT
)
1784 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1787 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1789 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1790 if (temp_status
== FILE_RETRY
)
1792 if (temp_status
== FILE_ABORT
)
1793 return_status
= temp_status
;
1794 if (temp_status
== FILE_SKIPALL
)
1795 ctx
->skip_all
= TRUE
;
1799 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1801 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1802 if (temp_status
== FILE_RETRY
)
1804 if (temp_status
== FILE_SKIPALL
)
1805 ctx
->skip_all
= TRUE
;
1806 return_status
= temp_status
;
1810 if (dst_status
== DEST_SHORT
)
1812 /* Remove short file */
1815 result
= query_dialog (Q_ ("DialogTitle|Copy"),
1816 _("Incomplete file was retrieved. Keep it?"),
1817 D_ERROR
, 2, _("&Delete"), _("&Keep"));
1819 mc_unlink (dst_vpath
);
1821 else if (dst_status
== DEST_FULL
)
1823 /* Copy has succeeded */
1824 if (!appending
&& ctx
->preserve_uidgid
)
1826 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1828 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1829 if (temp_status
== FILE_RETRY
)
1831 if (temp_status
== FILE_SKIPALL
)
1833 ctx
->skip_all
= TRUE
;
1834 return_status
= FILE_CONT
;
1836 if (temp_status
== FILE_SKIP
)
1837 return_status
= FILE_CONT
;
1846 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1848 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1849 if (temp_status
== FILE_RETRY
)
1851 if (temp_status
== FILE_SKIPALL
)
1853 ctx
->skip_all
= TRUE
;
1854 return_status
= FILE_CONT
;
1856 if (temp_status
== FILE_SKIP
)
1857 return_status
= FILE_CONT
;
1861 else if (!dst_exists
)
1863 src_mode
= umask (-1);
1865 src_mode
= 0100666 & ~src_mode
;
1866 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
1868 mc_utime (dst_vpath
, &utb
);
1872 if (return_status
== FILE_CONT
)
1873 return_status
= progress_update_one (tctx
, ctx
, file_size
);
1876 vfs_path_free (src_vpath
);
1877 vfs_path_free (dst_vpath
);
1878 return return_status
;
1881 /* --------------------------------------------------------------------------------------------- */
1883 * I think these copy_*_* functions should have a return type.
1884 * anyway, this function *must* have two directories as arguments.
1886 /* FIXME: This function needs to check the return values of the
1890 copy_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *_d
,
1891 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, struct link
* parent_dirs
)
1893 struct dirent
*next
;
1894 struct stat buf
, cbuf
;
1896 char *dest_dir
= NULL
;
1897 FileProgressStatus return_status
= FILE_CONT
;
1901 vfs_path_t
*src_vpath
, *dst_vpath
, *dest_dir_vpath
= NULL
;
1905 src_vpath
= vfs_path_from_str (s
);
1906 dst_vpath
= vfs_path_from_str (_d
);
1908 /* First get the mode of the source dir */
1911 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
1914 return_status
= FILE_SKIPALL
;
1917 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
1918 if (return_status
== FILE_RETRY
)
1919 goto retry_src_stat
;
1920 if (return_status
== FILE_SKIPALL
)
1921 ctx
->skip_all
= TRUE
;
1926 if (is_in_linklist (dest_dirs
, src_vpath
, &cbuf
))
1928 /* Don't copy a directory we created before (we don't want to copy
1929 infinitely if a directory is copied into itself) */
1930 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1931 return_status
= FILE_CONT
;
1935 /* Hmm, hardlink to directory??? - Norbert */
1936 /* FIXME: In this step we should do something
1937 in case the destination already exist */
1938 /* Check the hardlinks */
1939 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &cbuf
))
1941 /* We have made a hardlink - no more processing is necessary */
1945 if (!S_ISDIR (cbuf
.st_mode
))
1948 return_status
= FILE_SKIPALL
;
1951 return_status
= file_error (_("Source \"%s\" is not a directory\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 (parent_dirs
, src_vpath
, &cbuf
))
1962 /* we found a cyclic symbolic link */
1963 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
1964 return_status
= FILE_SKIP
;
1968 lp
= g_new0 (struct link
, 1);
1969 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
1970 lp
->ino
= cbuf
.st_ino
;
1971 lp
->dev
= cbuf
.st_dev
;
1972 lp
->next
= parent_dirs
;
1976 /* Now, check if the dest dir exists, if not, create it. */
1977 if (mc_stat (dst_vpath
, &buf
) != 0)
1979 /* Here the dir doesn't exist : make it ! */
1982 if (mc_rename (src_vpath
, dst_vpath
) == 0)
1984 return_status
= FILE_CONT
;
1994 * If the destination directory exists, we want to copy the whole
1995 * directory, but we only want this to happen once.
1997 * Escape sequences added to the * to compiler warnings.
1998 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1999 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2001 if (!S_ISDIR (buf
.st_mode
))
2004 return_status
= FILE_SKIPALL
;
2007 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2008 if (return_status
== FILE_SKIPALL
)
2009 ctx
->skip_all
= TRUE
;
2010 if (return_status
== FILE_RETRY
)
2011 goto retry_dst_stat
;
2015 /* Dive into subdir if exists */
2016 if (toplevel
&& ctx
->dive_into_subdirs
)
2018 dest_dir
= mc_build_filename (d
, x_basename (s
), NULL
);
2027 dest_dir_vpath
= vfs_path_from_str (dest_dir
);
2028 while (my_mkdir (dest_dir_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2031 return_status
= FILE_SKIPALL
;
2034 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir
);
2035 if (return_status
== FILE_SKIPALL
)
2036 ctx
->skip_all
= TRUE
;
2038 if (return_status
!= FILE_RETRY
)
2042 lp
= g_new0 (struct link
, 1);
2043 mc_stat (dest_dir_vpath
, &buf
);
2044 lp
->vfs
= vfs_path_get_by_index (dest_dir_vpath
, -1)->class;
2045 lp
->ino
= buf
.st_ino
;
2046 lp
->dev
= buf
.st_dev
;
2047 lp
->next
= dest_dirs
;
2050 if (ctx
->preserve_uidgid
)
2052 while (mc_chown (dest_dir_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2055 return_status
= FILE_SKIPALL
;
2059 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir
);
2060 if (return_status
== FILE_SKIPALL
)
2061 ctx
->skip_all
= TRUE
;
2063 if (return_status
!= FILE_RETRY
)
2069 /* open the source dir for reading */
2070 reading
= mc_opendir (src_vpath
);
2071 if (reading
== NULL
)
2074 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2077 vfs_path_t
*tmp_vpath
;
2079 * Now, we don't want '.' and '..' to be created / copied at any time
2081 if (!strcmp (next
->d_name
, "."))
2083 if (!strcmp (next
->d_name
, ".."))
2086 /* get the filename and add it to the src directory */
2087 path
= mc_build_filename (s
, next
->d_name
, NULL
);
2088 tmp_vpath
= vfs_path_from_str (path
);
2090 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2091 if (S_ISDIR (buf
.st_mode
))
2095 mdpath
= mc_build_filename (dest_dir
, next
->d_name
, NULL
);
2097 * From here, we just intend to recursively copy subdirs, not
2098 * the double functionality of copying different when the target
2099 * dir already exists. So, we give the recursive call the flag 0
2100 * meaning no toplevel.
2103 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2110 dest_file
= mc_build_filename (dest_dir
, x_basename (path
), NULL
);
2111 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2114 if (do_delete
&& return_status
== FILE_CONT
)
2116 if (ctx
->erase_at_end
)
2118 static struct link
*tail
;
2120 lp
= g_new0 (struct link
, 1);
2121 lp
->src_vpath
= vfs_path_clone (tmp_vpath
);
2122 lp
->st_mode
= buf
.st_mode
;
2125 if (erase_list
!= NULL
)
2131 erase_list
= tail
= lp
;
2135 if (S_ISDIR (buf
.st_mode
))
2137 return_status
= erase_dir_iff_empty (ctx
, path
);
2140 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
2144 vfs_path_free (tmp_vpath
);
2146 mc_closedir (reading
);
2150 mc_chmod (dest_dir_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2151 utb
.actime
= cbuf
.st_atime
;
2152 utb
.modtime
= cbuf
.st_mtime
;
2153 mc_utime (dest_dir_vpath
, &utb
);
2157 cbuf
.st_mode
= umask (-1);
2158 umask (cbuf
.st_mode
);
2159 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2160 mc_chmod (dest_dir_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2165 vfs_path_free (dest_dir_vpath
);
2166 g_free (parent_dirs
);
2169 vfs_path_free (src_vpath
);
2170 vfs_path_free (dst_vpath
);
2171 return return_status
;
2176 /* --------------------------------------------------------------------------------------------- */
2177 /* {{{ Move routines */
2180 move_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
2182 struct stat sbuf
, dbuf
, destbuf
;
2185 FileProgressStatus return_status
;
2186 gboolean move_over
= FALSE
;
2188 vfs_path_t
*src_vpath
, *dst_vpath
, *destdir_vpath
;
2190 src_vpath
= vfs_path_from_str (s
);
2191 dst_vpath
= vfs_path_from_str (d
);
2193 file_progress_show_source (ctx
, src_vpath
);
2194 file_progress_show_target (ctx
, dst_vpath
);
2196 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2198 return_status
= FILE_ABORT
;
2204 mc_stat (src_vpath
, &sbuf
);
2206 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2207 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2209 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2214 destdir
= g_strdup (d
); /* destination doesn't exist */
2215 else if (!ctx
->dive_into_subdirs
)
2217 destdir
= g_strdup (d
);
2221 destdir
= mc_build_filename (d
, x_basename (s
), NULL
);
2223 destdir_vpath
= vfs_path_from_str (destdir
);
2225 /* Check if the user inputted an existing dir */
2227 if (mc_stat (destdir_vpath
, &destbuf
) == 0)
2231 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
2233 if (return_status
!= FILE_CONT
)
2237 else if (ctx
->skip_all
)
2238 return_status
= FILE_SKIPALL
;
2241 if (S_ISDIR (destbuf
.st_mode
))
2242 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir
);
2244 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir
);
2245 if (return_status
== FILE_SKIPALL
)
2246 ctx
->skip_all
= TRUE
;
2247 if (return_status
== FILE_RETRY
)
2248 goto retry_dst_stat
;
2252 vfs_path_free (destdir_vpath
);
2257 if (mc_rename (src_vpath
, destdir_vpath
) == 0)
2259 return_status
= FILE_CONT
;
2267 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2268 if (return_status
== FILE_SKIPALL
)
2269 ctx
->skip_all
= TRUE
;
2270 if (return_status
== FILE_RETRY
)
2275 /* Failed because of filesystem boundary -> copy dir instead */
2276 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
2278 if (return_status
!= FILE_CONT
)
2281 file_progress_show_source (ctx
, NULL
);
2282 file_progress_show (ctx
, 0, 0, "", FALSE
);
2284 return_status
= check_progress_buttons (ctx
);
2285 if (return_status
!= FILE_CONT
)
2289 if (ctx
->erase_at_end
)
2291 for (; erase_list
&& return_status
!= FILE_ABORT
;)
2293 if (S_ISDIR (erase_list
->st_mode
))
2297 src_path
= vfs_path_to_str (erase_list
->src_vpath
);
2298 return_status
= erase_dir_iff_empty (ctx
, src_path
);
2302 return_status
= erase_file (tctx
, ctx
, erase_list
->src_vpath
);
2304 erase_list
= erase_list
->next
;
2308 erase_dir_iff_empty (ctx
, s
);
2312 vfs_path_free (destdir_vpath
);
2316 erase_list
= erase_list
->next
;
2320 vfs_path_free (src_vpath
);
2321 vfs_path_free (dst_vpath
);
2322 return return_status
;
2327 /* --------------------------------------------------------------------------------------------- */
2328 /* {{{ Erase routines */
2331 erase_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const vfs_path_t
* s_vpath
)
2333 FileProgressStatus error
;
2336 s
= vfs_path_to_str (s_vpath
);
2339 if (strcmp (s, "..") == 0)
2342 if (strcmp (s, ".") == 0)
2346 file_progress_show_deleting (ctx
, s
);
2347 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2354 /* The old way to detect a non empty directory was:
2355 error = my_rmdir (s);
2356 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2357 For the linux user space nfs server (nfs-server-2.2beta29-2)
2358 we would have to check also for EIO. I hope the new way is
2359 fool proof. (Norbert)
2361 error
= check_dir_is_empty (s_vpath
);
2364 error
= query_recursive (ctx
, s
);
2365 if (error
== FILE_CONT
)
2366 error
= recursive_erase (tctx
, ctx
, s
);
2371 while (my_rmdir (s
) == -1 && !ctx
->skip_all
)
2373 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
2374 if (error
!= FILE_RETRY
)
2387 /* --------------------------------------------------------------------------------------------- */
2388 /* {{{ Panel operate routines */
2391 compute_dir_size_create_ui (void)
2393 ComputeDirSizeUI
*ui
;
2395 const char *b_name
= N_("&Abort");
2401 ui
= g_new (ComputeDirSizeUI
, 1);
2403 ui
->dlg
= create_dlg (TRUE
, 0, 0, 8, COLS
/ 2, dialog_colors
, NULL
,
2404 NULL
, _("Directory scanning"), DLG_CENTER
);
2405 ui
->dirname
= label_new (3, 3, "");
2406 add_widget (ui
->dlg
, ui
->dirname
);
2408 add_widget (ui
->dlg
,
2409 button_new (5, (ui
->dlg
->cols
- strlen (b_name
)) / 2,
2410 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
2412 /* We will manage the dialog without any help,
2413 that's why we have to call init_dlg */
2419 /* --------------------------------------------------------------------------------------------- */
2422 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
2426 /* schedule to update passive panel */
2427 other_panel
->dirty
= 1;
2429 /* close and destroy dialog */
2430 dlg_run_done (ui
->dlg
);
2431 destroy_dlg (ui
->dlg
);
2436 /* --------------------------------------------------------------------------------------------- */
2439 compute_dir_size_update_ui (const void *ui
, const vfs_path_t
* dirname_vpath
)
2441 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
2449 dirname
= vfs_path_to_str (dirname_vpath
);
2450 label_set_text (this->dirname
, str_trunc (dirname
, this->dlg
->cols
- 6));
2453 event
.x
= -1; /* Don't show the GPM cursor */
2454 c
= tty_get_event (&event
, FALSE
, FALSE
);
2458 /* Reinitialize to avoid old values after events other than
2459 selecting a button */
2460 this->dlg
->ret_value
= FILE_CONT
;
2462 dlg_process_event (this->dlg
, c
, &event
);
2464 switch (this->dlg
->ret_value
)
2474 /* --------------------------------------------------------------------------------------------- */
2478 * Computes the number of bytes used by the files in a directory
2482 compute_dir_size (const vfs_path_t
* dirname_vpath
, const void *ui
,
2483 compute_dir_size_callback cback
,
2484 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
2489 struct dirent
*dirent
;
2490 FileProgressStatus ret
= FILE_CONT
;
2492 if (!compute_symlinks
)
2494 res
= mc_lstat (dirname_vpath
, &s
);
2498 /* don't scan symlink to directory */
2499 if (S_ISLNK (s
.st_mode
))
2502 *ret_total
+= (uintmax_t) s
.st_size
;
2507 dir
= mc_opendir (dirname_vpath
);
2512 while ((dirent
= mc_readdir (dir
)) != NULL
)
2514 vfs_path_t
*tmp_vpath
;
2516 ret
= (cback
!= NULL
) ? cback (ui
, dirname_vpath
) : FILE_CONT
;
2518 if (ret
!= FILE_CONT
)
2521 if (strcmp (dirent
->d_name
, ".") == 0)
2523 if (strcmp (dirent
->d_name
, "..") == 0)
2526 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, NULL
);
2527 res
= mc_lstat (tmp_vpath
, &s
);
2530 if (S_ISDIR (s
.st_mode
))
2532 size_t subdir_count
= 0;
2533 uintmax_t subdir_bytes
= 0;
2536 compute_dir_size (tmp_vpath
, ui
, cback
, &subdir_count
, &subdir_bytes
,
2539 if (ret
!= FILE_CONT
)
2541 vfs_path_free (tmp_vpath
);
2545 *ret_marked
+= subdir_count
;
2546 *ret_total
+= subdir_bytes
;
2551 *ret_total
+= (uintmax_t) s
.st_size
;
2554 vfs_path_free (tmp_vpath
);
2561 /* --------------------------------------------------------------------------------------------- */
2565 * Performs one of the operations on the selection on the source_panel
2566 * (copy, delete, move).
2568 * Returns TRUE if did change the directory
2569 * structure, Returns FALSE if user aborted
2571 * force_single forces operation on the current entry and affects
2572 * default destination. Current filename is used as default.
2576 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2578 WPanel
*panel
= (WPanel
*) source_panel
;
2579 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2580 || (get_current_type () == view_tree
);
2582 char *source
= NULL
;
2583 #ifdef WITH_FULL_PATHS
2584 vfs_path_t
*source_with_vpath
= NULL
;
2585 char *source_with_path_str
= NULL
;
2587 #define source_with_path source
2588 #endif /* !WITH_FULL_PATHS */
2590 vfs_path_t
*dest_vpath
= NULL
;
2592 char *save_cwd
= NULL
, *save_dest
= NULL
;
2593 struct stat src_stat
;
2594 gboolean ret_val
= TRUE
;
2596 FileProgressStatus value
;
2598 FileOpTotalContext
*tctx
;
2599 vfs_path_t
*tmp_vpath
;
2601 gboolean do_bg
= FALSE
; /* do background operation? */
2603 static gboolean i18n_flag
= FALSE
;
2606 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
2607 op_names
[i
] = Q_ (op_names
[i
]);
2611 free_linklist (&linklist
);
2612 free_linklist (&dest_dirs
);
2616 vfs_path_t
*source_vpath
;
2619 source
= g_strdup (selection (panel
)->fname
);
2621 source
= panel_get_file (panel
);
2623 if (strcmp (source
, "..") == 0)
2626 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2630 source_vpath
= vfs_path_from_str (source
);
2631 /* Update stat to get actual info */
2632 if (mc_lstat (source_vpath
, &src_stat
) != 0)
2634 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2635 path_trunc (source
, 30), unix_error_string (errno
));
2637 /* Directory was changed outside MC. Reload it forced */
2638 if (!panel
->is_panelized
)
2640 panel_update_flags_t flags
= UP_RELOAD
;
2642 /* don't update panelized panel */
2643 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2644 flags
|= UP_ONLY_CURRENT
;
2646 update_panels (flags
, UP_KEEPSEL
);
2648 vfs_path_free (source_vpath
);
2651 vfs_path_free (source_vpath
);
2654 ctx
= file_op_context_new (operation
);
2656 /* Show confirmation dialog */
2657 if (operation
!= OP_DELETE
)
2659 char *tmp_dest_dir
, *dest_dir
;
2662 /* Forced single operations default to the original name */
2664 tmp_dest_dir
= g_strdup (source
);
2665 else if (get_other_type () == view_listing
)
2666 tmp_dest_dir
= vfs_path_to_str (other_panel
->cwd_vpath
);
2668 tmp_dest_dir
= vfs_path_to_str (panel
->cwd_vpath
);
2670 * Add trailing backslash only when do non-local ops.
2671 * It saves user from occasional file renames (when destination
2674 if (!force_single
&& tmp_dest_dir
[0] != '\0'
2675 && tmp_dest_dir
[strlen (tmp_dest_dir
) - 1] != PATH_SEP
)
2677 /* add trailing separator */
2678 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2679 g_free (tmp_dest_dir
);
2684 dest_dir
= tmp_dest_dir
;
2686 if (dest_dir
== NULL
)
2692 /* Generate confirmation prompt */
2693 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2695 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2696 source
!= NULL
? (void *) source
2697 : (void *) &panel
->marked
, dest_dir
, &do_bg
);
2702 if (dest
== NULL
|| dest
[0] == '\0')
2708 dest_vpath
= vfs_path_from_str (dest
);
2710 else if (confirm_delete
)
2713 char fmd_buf
[BUF_MEDIUM
];
2715 /* Generate confirmation prompt */
2716 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2719 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2722 const int fmd_xlen
= 64;
2723 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2724 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2732 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2741 tctx
= file_op_total_context_new ();
2742 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2745 filegui_dialog_type_t dialog_type
;
2747 if (operation
== OP_DELETE
)
2748 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2751 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2752 ? FILEGUI_DIALOG_MULTI_ITEM
: FILEGUI_DIALOG_ONE_ITEM
;
2754 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2755 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2758 /* Background also need ctx->ui, but not full */
2760 file_op_context_create_ui_without_init (ctx
, TRUE
, dialog_type
);
2762 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
2765 #ifdef ENABLE_BACKGROUND
2766 /* Did the user select to do a background operation? */
2772 cwd_str
= vfs_path_to_str (panel
->cwd_vpath
);
2773 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", cwd_str
, (char *) NULL
));
2776 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2778 /* If we are the parent */
2781 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2783 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2784 vfs_path_free (dest_vpath
);
2786 /* file_op_context_destroy (ctx); */
2790 #endif /* ENABLE_BACKGROUND */
2792 /* Initialize things */
2793 /* We do not want to trash cache every time file is
2794 created/touched. However, this will make our cache contain
2796 if ((dest
!= NULL
) && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2797 save_dest
= g_strdup (dest
);
2799 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2800 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2801 save_cwd
= vfs_path_to_str (panel
->cwd_vpath
);
2803 /* Now, let's do the job */
2805 /* This code is only called by the tree and panel code */
2808 /* We now have ETA in all cases */
2810 /* One file: FIXME mc_chdir will take user out of any vfs */
2811 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2816 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2817 chdir_retcode
= mc_chdir (vpath
);
2818 vfs_path_free (vpath
);
2819 if (chdir_retcode
< 0)
2826 /* The source and src_stat variables have been initialized before */
2827 #ifdef WITH_FULL_PATHS
2828 if (g_path_is_absolute (source
))
2829 source_with_vpath
= vfs_path_from_str (source
);
2831 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2832 source_with_path_str
= vfs_path_to_str (source_with_vpath
);
2833 #endif /* WITH_FULL_PATHS */
2834 if (panel_operate_init_totals (operation
, panel
, source_with_path_str
, ctx
) == FILE_CONT
)
2836 if (operation
== OP_DELETE
)
2838 if (S_ISDIR (src_stat
.st_mode
))
2839 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2841 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2845 temp
= transform_source (ctx
, source_with_path_str
);
2847 value
= transform_error
;
2850 char *repl_dest
, *temp2
;
2852 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2853 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2857 vfs_path_free (dest_vpath
);
2859 dest_vpath
= vfs_path_from_str (dest
);
2864 /* we use file_mask_op_follow_links only with OP_COPY */
2865 ctx
->stat_func (source_with_vpath
, &src_stat
);
2867 if (S_ISDIR (src_stat
.st_mode
))
2868 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, dest
,
2869 TRUE
, FALSE
, FALSE
, NULL
);
2871 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, dest
);
2875 if (S_ISDIR (src_stat
.st_mode
))
2876 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, dest
);
2878 value
= move_file_file (tctx
, ctx
, source_with_path_str
, dest
);
2882 /* Unknown file operation */
2886 } /* Copy or move operation */
2888 if ((value
== FILE_CONT
) && !force_single
)
2889 unmark_files (panel
);
2896 /* Check destination for copy or move operation */
2897 while (operation
!= OP_DELETE
)
2900 struct stat dst_stat
;
2902 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2904 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2908 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2912 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
)
2914 /* Loop for every file, perform the actual copy operation */
2915 for (i
= 0; i
< panel
->count
; i
++)
2917 const char *source2
;
2919 if (!panel
->dir
.list
[i
].f
.marked
)
2920 continue; /* Skip the unmarked ones */
2922 source2
= panel
->dir
.list
[i
].fname
;
2923 src_stat
= panel
->dir
.list
[i
].st
;
2925 #ifdef WITH_FULL_PATHS
2926 g_free (source_with_path_str
);
2927 vfs_path_free (source_with_vpath
);
2928 if (g_path_is_absolute (source2
))
2929 source_with_vpath
= vfs_path_from_str (source2
);
2932 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2933 source_with_path_str
= vfs_path_to_str (source_with_vpath
);
2934 #endif /* WITH_FULL_PATHS */
2936 if (operation
== OP_DELETE
)
2938 if (S_ISDIR (src_stat
.st_mode
))
2939 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2941 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2945 temp
= transform_source (ctx
, source_with_path_str
);
2948 value
= transform_error
;
2951 char *temp2
, *temp3
, *repl_dest
;
2953 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2954 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2957 temp3
= source_with_path_str
;
2958 source_with_path_str
= strutils_shell_unescape (source_with_path_str
);
2961 temp2
= strutils_shell_unescape (temp2
);
2967 /* we use file_mask_op_follow_links only with OP_COPY */
2971 vpath
= vfs_path_from_str (source_with_path_str
);
2972 ctx
->stat_func (vpath
, &src_stat
);
2973 vfs_path_free (vpath
);
2975 if (S_ISDIR (src_stat
.st_mode
))
2976 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
,
2977 TRUE
, FALSE
, FALSE
, NULL
);
2979 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
2980 free_linklist (&dest_dirs
);
2984 if (S_ISDIR (src_stat
.st_mode
))
2985 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
);
2987 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
2991 /* Unknown file operation */
2997 } /* Copy or move operation */
2999 if (value
== FILE_ABORT
)
3002 if (value
== FILE_CONT
)
3003 do_file_mark (panel
, i
, 0);
3005 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3007 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3008 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3011 if (operation
!= OP_DELETE
)
3012 file_progress_show (ctx
, 0, 0, "", FALSE
);
3014 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3018 } /* Loop for every file */
3020 } /* Many entries */
3024 if (save_cwd
!= NULL
)
3026 tmp_vpath
= vfs_path_from_str (save_cwd
);
3027 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3028 vfs_path_free (tmp_vpath
);
3032 if (save_dest
!= NULL
)
3034 tmp_vpath
= vfs_path_from_str (save_dest
);
3035 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3036 vfs_path_free (tmp_vpath
);
3040 free_linklist (&linklist
);
3041 free_linklist (&dest_dirs
);
3042 #ifdef WITH_FULL_PATHS
3043 g_free (source_with_path_str
);
3044 vfs_path_free (source_with_vpath
);
3045 #endif /* WITH_FULL_PATHS */
3047 vfs_path_free (dest_vpath
);
3048 g_free (ctx
->dest_mask
);
3049 ctx
->dest_mask
= NULL
;
3051 #ifdef ENABLE_BACKGROUND
3052 /* Let our parent know we are saying bye bye */
3053 if (mc_global
.we_are_background
)
3055 int cur_pid
= getpid ();
3056 /* Send pid to parent with child context, it is fork and
3057 don't modify real parent ctx */
3059 parent_call ((void *) end_bg_process
, ctx
, 0);
3064 #endif /* ENABLE_BACKGROUND */
3066 file_op_total_context_destroy (tctx
);
3068 file_op_context_destroy (ctx
);
3076 /* --------------------------------------------------------------------------------------------- */
3077 /* {{{ Query/status report routines */
3078 /** Report error with one file */
3080 file_error (const char *format
, const char *file
)
3082 char buf
[BUF_MEDIUM
];
3084 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3086 return do_file_error (buf
);
3089 /* --------------------------------------------------------------------------------------------- */
3092 Cause emacs to enter folding mode for this file: