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 #include "src/background.h"
77 #include "layout.h" /* rotate_dash() */
79 /* Needed for current_panel, other_panel and WTree */
84 #include "midnight.h" /* current_panel */
90 /*** global variables ****************************************************************************/
92 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
93 const char *op_names
[3] = {
94 N_("DialogTitle|Copy"),
95 N_("DialogTitle|Move"),
96 N_("DialogTitle|Delete")
99 /*** file scope macro definitions ****************************************************************/
101 /* Hack: the vfs code should not rely on this */
102 #define WITH_FULL_PATHS 1
104 #define FILEOP_UPDATE_INTERVAL 2
105 #define FILEOP_STALLING_INTERVAL 4
107 /*** file scope type declarations ****************************************************************/
109 /* This is a hard link cache */
112 const struct vfs_class
*vfs
;
117 vfs_path_t
*src_vpath
;
118 vfs_path_t
*dst_vpath
;
121 /* Status of the destination file */
124 DEST_NONE
= 0, /* Not created */
125 DEST_SHORT
= 1, /* Created, not fully copied */
126 DEST_FULL
= 2 /* Created, fully copied */
130 * This array introduced to avoid translation problems. The former (op_names)
131 * is assumed to be nouns, suitable in dialog box titles; this one should
132 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
133 * (I don't use spaces around the words, because someday they could be
134 * dropped, when widgets get smarter)
137 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
138 static const char *op_names1
[] = {
139 N_("FileOperation|Copy"),
140 N_("FileOperation|Move"),
141 N_("FileOperation|Delete")
145 * These are formats for building a prompt. Parts encoded as follows:
146 * %o - operation from op_names1
147 * %f - file/files or files/directories, as appropriate
148 * %m - "with source mask" or question mark for delete
149 * %s - source name (truncated)
150 * %d - number of marked files
151 * %e - "to:" or question mark for delete
153 * xgettext:no-c-format */
154 static const char *one_format
= N_("%o %f \"%s\"%m");
155 /* xgettext:no-c-format */
156 static const char *many_format
= N_("%o %d %f%m");
158 static const char *prompt_parts
[] = {
163 N_("files/directories"),
164 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
165 N_(" with source mask:"),
169 static const char *question_format
= N_("%s?");
171 /*** file scope variables ************************************************************************/
173 /* the hard link cache */
174 static GSList
*linklist
= NULL
;
176 /* the files-to-be-erased list */
177 static GSList
*erase_list
= NULL
;
180 * In copy_dir_dir we use two additional single linked lists: The first -
181 * variable name `parent_dirs' - holds information about already copied
182 * directories and is used to detect cyclic symbolic links.
183 * The second (`dest_dirs' below) holds information about just created
184 * target directories and is used to detect when an directory is copied
185 * into itself (we don't want to copy infinitly).
186 * Both lists don't use the linkcount and name structure members of struct
189 static GSList
*dest_dirs
= NULL
;
191 static FileProgressStatus transform_error
= FILE_CONT
;
193 /*** file scope functions ************************************************************************/
194 /* --------------------------------------------------------------------------------------------- */
197 transform_source (FileOpContext
* ctx
, const char *source
)
202 s
= g_strdup (source
);
204 /* We remove \n from the filename since regex routines would use \n as an anchor */
205 /* this is just to be allowed to maniupulate file names with \n on it */
206 for (q
= s
; *q
!= '\0'; q
++)
210 fnsource
= (char *) x_basename (s
);
212 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
213 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
217 transform_error
= FILE_SKIP
;
224 /* --------------------------------------------------------------------------------------------- */
227 free_link (void *data
)
229 struct link
*lp
= (struct link
*) data
;
231 vfs_path_free (lp
->src_vpath
);
232 vfs_path_free (lp
->dst_vpath
);
236 /* --------------------------------------------------------------------------------------------- */
239 free_linklist (GSList
*lp
)
241 g_slist_foreach (lp
, (GFunc
) free_link
, NULL
);
247 /* --------------------------------------------------------------------------------------------- */
250 is_in_linklist (const GSList
*lp
, const vfs_path_t
* vpath
, const struct stat
*sb
)
252 const struct vfs_class
*class;
253 ino_t ino
= sb
->st_ino
;
254 dev_t dev
= sb
->st_dev
;
256 class = vfs_path_get_last_path_vfs (vpath
);
258 for (; lp
!= NULL
; lp
= g_slist_next (lp
))
260 const struct link
*lnk
= (const struct link
*) lp
->data
;
262 if (lnk
->vfs
== class && lnk
->ino
== ino
&& lnk
->dev
== dev
)
268 /* --------------------------------------------------------------------------------------------- */
270 * Check and made hardlink
272 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
273 * and a hardlink was succesfully made
277 check_hardlinks (const vfs_path_t
* src_vpath
, const vfs_path_t
* dst_vpath
, struct stat
*pstat
)
282 const struct vfs_class
*my_vfs
;
283 ino_t ino
= pstat
->st_ino
;
284 dev_t dev
= pstat
->st_dev
;
285 struct stat link_stat
;
287 if ((vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
290 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
292 for (lp
= linklist
; lp
!= NULL
; lp
= g_slist_next (lp
))
294 lnk
= (struct link
*) lp
->data
;
296 if (lnk
->vfs
== my_vfs
&& lnk
->ino
== ino
&& lnk
->dev
== dev
)
298 const struct vfs_class
*lp_name_class
;
301 lp_name_class
= vfs_path_get_last_path_vfs (lnk
->src_vpath
);
302 stat_result
= mc_stat (lnk
->src_vpath
, &link_stat
);
304 if (stat_result
== 0 && link_stat
.st_ino
== ino
305 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
307 const struct vfs_class
*p_class
, *dst_name_class
;
309 dst_name_class
= vfs_path_get_last_path_vfs (dst_vpath
);
310 p_class
= vfs_path_get_last_path_vfs (lnk
->dst_vpath
);
312 if (dst_name_class
== p_class
&&
313 mc_stat (lnk
->dst_vpath
, &link_stat
) == 0 &&
314 mc_link (lnk
->dst_vpath
, dst_vpath
) == 0)
318 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
323 lnk
= g_new0 (struct link
, 1);
329 lnk
->src_vpath
= vfs_path_clone (src_vpath
);
330 lnk
->dst_vpath
= vfs_path_clone (dst_vpath
);
331 linklist
= g_slist_prepend (linklist
, lnk
);
337 /* --------------------------------------------------------------------------------------------- */
339 * Duplicate the contents of the symbolic link src_path in dst_path.
340 * Try to make a stable symlink if the option "stable symlink" was
341 * set in the file mask dialog.
342 * If dst_path is an existing symlink it will be deleted silently
343 * (upper levels take already care of existing files at dst_path).
346 static FileProgressStatus
347 make_symlink (FileOpContext
* ctx
, const char *src_path
, const char *dst_path
)
349 char link_target
[MC_MAXPATHLEN
];
351 FileProgressStatus return_status
;
353 vfs_path_t
*src_vpath
;
354 vfs_path_t
*dst_vpath
;
355 gboolean dst_is_symlink
;
356 vfs_path_t
*link_target_vpath
= NULL
;
358 src_vpath
= vfs_path_from_str (src_path
);
359 dst_vpath
= vfs_path_from_str (dst_path
);
360 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
363 len
= mc_readlink (src_vpath
, link_target
, MC_MAXPATHLEN
- 1);
367 return_status
= FILE_SKIPALL
;
370 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
371 if (return_status
== FILE_SKIPALL
)
372 ctx
->skip_all
= TRUE
;
373 if (return_status
== FILE_RETRY
)
374 goto retry_src_readlink
;
378 link_target
[len
] = 0;
380 if (ctx
->stable_symlinks
)
383 if (!vfs_file_is_local (src_vpath
) || !vfs_file_is_local (dst_vpath
))
385 message (D_ERROR
, MSG_ERROR
,
386 _("Cannot make stable symlinks across"
387 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
388 ctx
->stable_symlinks
= FALSE
;
392 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
397 const char *r
= strrchr (src_path
, PATH_SEP
);
401 p
= g_strndup (src_path
, r
- src_path
+ 1);
402 if (g_path_is_absolute (dst_path
))
403 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
405 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
407 if (vfs_path_tokens_count (q
) > 1)
409 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
411 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
412 s
= g_strconcat (p
, link_target
, (char *) NULL
);
414 g_strlcpy (link_target
, s
, sizeof (link_target
));
416 tmp_vpath2
= vfs_path_from_str (link_target
);
417 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
418 vfs_path_free (tmp_vpath1
);
419 vfs_path_free (tmp_vpath2
);
422 g_strlcpy (link_target
, s
, sizeof (link_target
));
431 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
434 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
437 return_status
= FILE_CONT
;
441 * if dst_exists, it is obvious that this had failed.
442 * We can delete the old symlink and try again...
446 if (mc_unlink (dst_vpath
) == 0)
447 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
450 return_status
= FILE_CONT
;
455 return_status
= FILE_SKIPALL
;
458 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
459 if (return_status
== FILE_SKIPALL
)
460 ctx
->skip_all
= TRUE
;
461 if (return_status
== FILE_RETRY
)
462 goto retry_dst_symlink
;
466 vfs_path_free (src_vpath
);
467 vfs_path_free (dst_vpath
);
468 vfs_path_free (link_target_vpath
);
469 return return_status
;
472 /* --------------------------------------------------------------------------------------------- */
474 static FileProgressStatus
475 progress_update_one (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, off_t add
)
477 struct timeval tv_current
;
478 static struct timeval tv_start
= { };
480 tctx
->progress_count
++;
481 tctx
->progress_bytes
+= (uintmax_t) add
;
483 if (tv_start
.tv_sec
== 0)
485 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
487 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
488 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
490 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
492 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
493 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
495 tv_start
.tv_sec
= tv_current
.tv_sec
;
498 return check_progress_buttons (ctx
);
501 /* --------------------------------------------------------------------------------------------- */
503 static FileProgressStatus
504 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
508 const char *head_msg
;
510 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
512 msg
= g_strdup_printf (fmt
, a
, b
);
513 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
517 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
520 /* --------------------------------------------------------------------------------------------- */
522 static FileProgressStatus
523 warn_same_file (const char *fmt
, const char *a
, const char *b
)
525 #ifdef WITH_BACKGROUND
530 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
531 const char *a
, const char *b
);
535 pntr
.f
= real_warn_same_file
;
537 if (mc_global
.we_are_background
)
538 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
540 return real_warn_same_file (Foreground
, fmt
, a
, b
);
543 /* --------------------------------------------------------------------------------------------- */
544 /* {{{ Query/status report routines */
546 static FileProgressStatus
547 real_do_file_error (enum OperationMode mode
, const char *error
)
552 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
554 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
576 /* --------------------------------------------------------------------------------------------- */
578 static FileProgressStatus
579 real_query_recursive (FileOpContext
* ctx
, enum OperationMode mode
, const char *s
)
583 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
585 const char *msg
= mode
== Foreground
586 ? _("\nDirectory not empty.\nDelete it recursively?")
587 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
588 text
= g_strconcat (_("Delete:"), " ", path_trunc (s
, 30), (char *) NULL
);
593 ctx
->recursive_result
=
594 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
595 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
597 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
602 switch (ctx
->recursive_result
)
605 case RECURSIVE_ALWAYS
:
609 case RECURSIVE_NEVER
:
612 case RECURSIVE_ABORT
:
618 /* --------------------------------------------------------------------------------------------- */
620 #ifdef WITH_BACKGROUND
621 static FileProgressStatus
622 do_file_error (const char *str
)
627 FileProgressStatus (*f
) (enum OperationMode
, const char *);
629 pntr
.f
= real_do_file_error
;
631 if (mc_global
.we_are_background
)
632 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
634 return real_do_file_error (Foreground
, str
);
637 /* --------------------------------------------------------------------------------------------- */
639 static FileProgressStatus
640 query_recursive (FileOpContext
* ctx
, const char *s
)
645 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
647 pntr
.f
= real_query_recursive
;
649 if (mc_global
.we_are_background
)
650 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
652 return real_query_recursive (ctx
, Foreground
, s
);
655 /* --------------------------------------------------------------------------------------------- */
657 static FileProgressStatus
658 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
659 struct stat
*_d_stat
)
664 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
665 struct stat
*, struct stat
*);
667 pntr
.f
= file_progress_real_query_replace
;
669 if (mc_global
.we_are_background
)
670 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
671 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
673 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
677 /* --------------------------------------------------------------------------------------------- */
679 static FileProgressStatus
680 do_file_error (const char *str
)
682 return real_do_file_error (Foreground
, str
);
685 /* --------------------------------------------------------------------------------------------- */
687 static FileProgressStatus
688 query_recursive (FileOpContext
* ctx
, const char *s
)
690 return real_query_recursive (ctx
, Foreground
, s
);
693 /* --------------------------------------------------------------------------------------------- */
695 static FileProgressStatus
696 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
697 struct stat
*_d_stat
)
699 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
702 #endif /* !WITH_BACKGROUND */
704 /* --------------------------------------------------------------------------------------------- */
705 /** Report error with two files */
707 static FileProgressStatus
708 files_error (const char *format
, const char *file1
, const char *file2
)
710 char buf
[BUF_MEDIUM
];
711 char *nfile1
= g_strdup (path_trunc (file1
, 15));
712 char *nfile2
= g_strdup (path_trunc (file2
, 15));
714 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
719 return do_file_error (buf
);
724 /* --------------------------------------------------------------------------------------------- */
727 copy_file_file_display_progress (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
728 struct timeval tv_current
, struct timeval tv_transfer_start
,
729 off_t file_size
, off_t n_read_total
)
733 /* 1. Update rotating dash after some time */
737 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
739 if (n_read_total
== 0)
743 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
744 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
747 /* 4. Compute BPS rate */
748 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
749 if (ctx
->bps_time
< 1)
751 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
753 /* 5. Compute total ETA and BPS */
754 if (ctx
->progress_bytes
!= 0)
756 uintmax_t remain_bytes
;
758 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
761 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
766 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
767 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
770 /* broken on lot of little files */
772 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
773 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
778 /* --------------------------------------------------------------------------------------------- */
780 /* {{{ Move routines */
781 static FileProgressStatus
782 move_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
784 struct stat src_stats
, dst_stats
;
785 FileProgressStatus return_status
= FILE_CONT
;
786 gboolean copy_done
= FALSE
;
787 gboolean old_ask_overwrite
;
788 vfs_path_t
*src_vpath
, *dst_vpath
;
790 src_vpath
= vfs_path_from_str (s
);
791 dst_vpath
= vfs_path_from_str (d
);
793 file_progress_show_source (ctx
, src_vpath
);
794 file_progress_show_target (ctx
, dst_vpath
);
796 if (check_progress_buttons (ctx
) == FILE_ABORT
)
798 return_status
= FILE_ABORT
;
804 while (mc_lstat (src_vpath
, &src_stats
) != 0)
806 /* Source doesn't exist */
808 return_status
= FILE_SKIPALL
;
811 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
812 if (return_status
== FILE_SKIPALL
)
813 ctx
->skip_all
= TRUE
;
816 if (return_status
!= FILE_RETRY
)
820 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
822 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
824 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
828 if (S_ISDIR (dst_stats
.st_mode
))
830 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
832 return_status
= FILE_SKIP
;
836 if (confirm_overwrite
)
838 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
839 if (return_status
!= FILE_CONT
)
842 /* Ok to overwrite */
847 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
849 return_status
= make_symlink (ctx
, s
, d
);
850 if (return_status
== FILE_CONT
)
851 goto retry_src_remove
;
855 if (mc_rename (src_vpath
, dst_vpath
) == 0)
857 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
862 /* Comparison to EXDEV seems not to work in nfs if you're moving from
863 one nfs to the same, but on the server it is on two different
864 filesystems. Then nfs returns EIO instead of EXDEV.
865 Hope it will not hurt if we always in case of error try to copy/delete. */
867 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
872 return_status
= FILE_SKIPALL
;
875 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
876 if (return_status
== FILE_SKIPALL
)
877 ctx
->skip_all
= TRUE
;
878 if (return_status
== FILE_RETRY
)
886 /* Failed because filesystem boundary -> copy the file instead */
887 old_ask_overwrite
= tctx
->ask_overwrite
;
888 tctx
->ask_overwrite
= FALSE
;
889 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
890 tctx
->ask_overwrite
= old_ask_overwrite
;
891 if (return_status
!= FILE_CONT
)
896 file_progress_show_source (ctx
, NULL
);
897 file_progress_show (ctx
, 0, 0, "", FALSE
);
899 return_status
= check_progress_buttons (ctx
);
900 if (return_status
!= FILE_CONT
)
905 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
907 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
908 if (return_status
== FILE_RETRY
)
909 goto retry_src_remove
;
910 if (return_status
== FILE_SKIPALL
)
911 ctx
->skip_all
= TRUE
;
916 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
919 vfs_path_free (src_vpath
);
920 vfs_path_free (dst_vpath
);
922 return return_status
;
927 /* --------------------------------------------------------------------------------------------- */
928 /* {{{ Erase routines */
929 /** Don't update progress status if progress_count==NULL */
931 static FileProgressStatus
932 erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const vfs_path_t
* vpath
)
938 s
= vfs_path_to_str (vpath
);
939 file_progress_show_deleting (ctx
, s
);
940 if (check_progress_buttons (ctx
) == FILE_ABORT
)
947 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
949 /* ignore, most likely the mc_unlink fails, too */
953 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
955 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), s
);
956 if (return_status
== FILE_ABORT
)
959 return return_status
;
961 if (return_status
== FILE_RETRY
)
963 if (return_status
== FILE_SKIPALL
)
964 ctx
->skip_all
= TRUE
;
968 if (tctx
->progress_count
== 0)
970 return progress_update_one (tctx
, ctx
, buf
.st_size
);
973 /* --------------------------------------------------------------------------------------------- */
976 Recursive remove of files
978 skip ->warn every level, gets default
979 skipall->remove as much as possible
981 static FileProgressStatus
982 recursive_erase (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
988 FileProgressStatus return_status
= FILE_CONT
;
991 if (strcmp (s
, "..") == 0)
994 vpath
= vfs_path_from_str (s
);
995 reading
= mc_opendir (vpath
);
999 return_status
= FILE_RETRY
;
1003 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1005 vfs_path_t
*tmp_vpath
;
1007 if (!strcmp (next
->d_name
, "."))
1009 if (!strcmp (next
->d_name
, ".."))
1011 path
= mc_build_filename (s
, next
->d_name
, NULL
);
1012 tmp_vpath
= vfs_path_from_str (path
);
1013 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1016 mc_closedir (reading
);
1017 vfs_path_free (tmp_vpath
);
1018 return_status
= FILE_RETRY
;
1021 if (S_ISDIR (buf
.st_mode
))
1022 return_status
= recursive_erase (tctx
, ctx
, path
);
1024 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
1025 vfs_path_free (tmp_vpath
);
1028 mc_closedir (reading
);
1029 if (return_status
== FILE_ABORT
)
1032 file_progress_show_deleting (ctx
, s
);
1033 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1035 return_status
= FILE_ABORT
;
1040 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1042 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1043 if (return_status
== FILE_RETRY
)
1045 if (return_status
== FILE_ABORT
)
1047 if (return_status
== FILE_SKIPALL
)
1048 ctx
->skip_all
= TRUE
;
1053 vfs_path_free (vpath
);
1054 return return_status
;
1057 /* --------------------------------------------------------------------------------------------- */
1058 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1059 in the directory path points to, 0 else. */
1062 check_dir_is_empty (const vfs_path_t
* vpath
)
1068 dir
= mc_opendir (vpath
);
1072 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
))
1074 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1075 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1076 continue; /* "." or ".." */
1085 /* --------------------------------------------------------------------------------------------- */
1087 static FileProgressStatus
1088 erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
)
1090 FileProgressStatus error
;
1091 vfs_path_t
*s_vpath
;
1093 if (strcmp (s
, "..") == 0)
1096 if (strcmp (s
, ".") == 0)
1099 file_progress_show_deleting (ctx
, s
);
1100 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1105 s_vpath
= vfs_path_from_str (s
);
1107 if (check_dir_is_empty (s_vpath
) == 1) /* not empty or error */
1109 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1111 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1112 if (error
== FILE_SKIPALL
)
1113 ctx
->skip_all
= TRUE
;
1114 if (error
!= FILE_RETRY
)
1116 vfs_path_free (s_vpath
);
1122 vfs_path_free (s_vpath
);
1128 /* --------------------------------------------------------------------------------------------- */
1129 /* {{{ Panel operate routines */
1132 * Return currently selected entry name or the name of the first marked
1133 * entry if there is one.
1137 panel_get_file (WPanel
* panel
)
1139 if (get_current_type () == view_tree
)
1143 tree
= (WTree
*) get_panel_widget (get_current_index ());
1144 return vfs_path_to_str (tree_selected_name (tree
));
1147 if (panel
->marked
!= 0)
1151 for (i
= 0; i
< panel
->count
; i
++)
1152 if (panel
->dir
.list
[i
].f
.marked
)
1153 return g_strdup (panel
->dir
.list
[i
].fname
);
1155 return g_strdup (panel
->dir
.list
[panel
->selected
].fname
);
1158 /* --------------------------------------------------------------------------------------------- */
1160 * panel_compute_totals:
1162 * compute the number of files and the number of bytes
1163 * used up by the whole selection, recursing directories
1164 * as required. In addition, it checks to see if it will
1165 * overwrite any files by doing the copy.
1168 static FileProgressStatus
1169 panel_compute_totals (const WPanel
* panel
, const void *ui
,
1170 compute_dir_size_callback cback
,
1171 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
1178 for (i
= 0; i
< panel
->count
; i
++)
1182 if (!panel
->dir
.list
[i
].f
.marked
)
1185 s
= &panel
->dir
.list
[i
].st
;
1187 if (S_ISDIR (s
->st_mode
))
1190 size_t subdir_count
= 0;
1191 uintmax_t subdir_bytes
= 0;
1192 FileProgressStatus status
;
1194 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, NULL
);
1196 compute_dir_size (p
, ui
, cback
, &subdir_count
, &subdir_bytes
, compute_symlinks
);
1199 if (status
!= FILE_CONT
)
1202 *ret_marked
+= subdir_count
;
1203 *ret_total
+= subdir_bytes
;
1208 *ret_total
+= (uintmax_t) s
->st_size
;
1215 /* --------------------------------------------------------------------------------------------- */
1217 /** Initialize variables for progress bars */
1218 static FileProgressStatus
1219 panel_operate_init_totals (FileOperation operation
,
1220 const WPanel
* panel
, const char *source
, FileOpContext
* ctx
)
1222 FileProgressStatus status
;
1224 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
)
1226 ComputeDirSizeUI
*ui
;
1228 ui
= compute_dir_size_create_ui ();
1231 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1232 &ctx
->progress_count
, &ctx
->progress_bytes
,
1238 p
= vfs_path_from_str (source
);
1239 status
= compute_dir_size (p
, ui
, compute_dir_size_update_ui
,
1240 &ctx
->progress_count
, &ctx
->progress_bytes
,
1245 compute_dir_size_destroy_ui (ui
);
1247 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1252 ctx
->progress_count
= panel
->marked
;
1253 ctx
->progress_bytes
= panel
->total
;
1254 ctx
->progress_totals_computed
= FALSE
;
1260 /* --------------------------------------------------------------------------------------------- */
1262 * Generate user prompt for panel operation.
1263 * single_source is the name if the source entry or NULL for multiple
1265 * src_stat is only used when single_source is not NULL.
1269 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1270 gboolean single_source
, const struct stat
*src_stat
)
1272 const char *sp
, *cp
;
1273 char format_string
[BUF_MEDIUM
];
1274 char *dp
= format_string
;
1275 gboolean build_question
= FALSE
;
1277 static gboolean i18n_flag
= FALSE
;
1282 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1283 op_names1
[i
] = Q_ (op_names1
[i
]);
1286 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1287 prompt_parts
[i
] = _(prompt_parts
[i
]);
1289 one_format
= _(one_format
);
1290 many_format
= _(many_format
);
1291 question_format
= _(question_format
);
1292 #endif /* ENABLE_NLS */
1296 sp
= single_source
? one_format
: many_format
;
1307 cp
= op_names1
[operation
];
1310 if (operation
== OP_DELETE
)
1313 build_question
= TRUE
;
1316 cp
= prompt_parts
[5];
1319 if (operation
== OP_DELETE
)
1322 build_question
= TRUE
;
1325 cp
= prompt_parts
[6];
1329 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1331 cp
= (panel
->marked
== panel
->dirs_marked
)
1333 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1354 char tmp
[BUF_MEDIUM
];
1356 memmove (tmp
, format_string
, sizeof (tmp
));
1357 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1360 return g_strdup (format_string
);
1363 /* --------------------------------------------------------------------------------------------- */
1365 #ifdef WITH_BACKGROUND
1367 end_bg_process (FileOpContext
* ctx
, enum OperationMode mode
)
1374 unregister_task_with_pid (pid
);
1375 /* file_op_context_destroy(ctx); */
1381 /* --------------------------------------------------------------------------------------------- */
1382 /*** public functions ****************************************************************************/
1383 /* --------------------------------------------------------------------------------------------- */
1386 copy_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
1387 const char *src_path
, const char *dst_path
)
1389 uid_t src_uid
= (uid_t
) (-1);
1390 gid_t src_gid
= (gid_t
) (-1);
1392 int src_desc
, dest_desc
= -1;
1393 int n_read
, n_written
;
1394 mode_t src_mode
= 0; /* The mode of the source file */
1395 struct stat sb
, sb2
;
1397 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1398 off_t file_size
= -1;
1399 FileProgressStatus return_status
, temp_status
;
1400 struct timeval tv_transfer_start
;
1401 dest_status_t dst_status
= DEST_NONE
;
1403 gboolean is_first_time
= TRUE
;
1404 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1406 /* FIXME: We should not be using global variables! */
1408 return_status
= FILE_RETRY
;
1410 dst_vpath
= vfs_path_from_str (dst_path
);
1411 src_vpath
= vfs_path_from_str (src_path
);
1413 file_progress_show_source (ctx
, src_vpath
);
1414 file_progress_show_target (ctx
, dst_vpath
);
1416 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1418 return_status
= FILE_ABORT
;
1424 while (mc_stat (dst_vpath
, &sb2
) == 0)
1426 if (S_ISDIR (sb2
.st_mode
))
1429 return_status
= FILE_SKIPALL
;
1432 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1433 if (return_status
== FILE_SKIPALL
)
1434 ctx
->skip_all
= TRUE
;
1435 if (return_status
== FILE_RETRY
)
1445 while ((*ctx
->stat_func
) (src_vpath
, &sb
) != 0)
1448 return_status
= FILE_SKIPALL
;
1451 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1452 if (return_status
== FILE_SKIPALL
)
1453 ctx
->skip_all
= TRUE
;
1456 if (return_status
!= FILE_RETRY
)
1462 /* Destination already exists */
1463 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1465 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1466 src_path
, dst_path
);
1470 /* Should we replace destination? */
1471 if (tctx
->ask_overwrite
)
1474 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1475 if (return_status
!= FILE_CONT
)
1480 if (!ctx
->do_append
)
1482 /* Check the hardlinks */
1483 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &sb
))
1485 /* We have made a hardlink - no more processing is necessary */
1486 return_status
= FILE_CONT
;
1490 if (S_ISLNK (sb
.st_mode
))
1492 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1496 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1497 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1499 while (mc_mknod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0
1502 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1503 if (return_status
== FILE_RETRY
)
1505 if (return_status
== FILE_SKIPALL
)
1506 ctx
->skip_all
= TRUE
;
1511 while (ctx
->preserve_uidgid
&& mc_chown (dst_vpath
, sb
.st_uid
, sb
.st_gid
) != 0
1514 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1515 if (temp_status
== FILE_SKIP
)
1517 if (temp_status
== FILE_SKIPALL
)
1518 ctx
->skip_all
= TRUE
;
1519 if (temp_status
!= FILE_RETRY
)
1521 return_status
= temp_status
;
1526 while (ctx
->preserve
&& mc_chmod (dst_vpath
, sb
.st_mode
& ctx
->umask_kill
) != 0
1529 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1530 if (temp_status
== FILE_SKIP
)
1532 if (temp_status
== FILE_SKIPALL
)
1533 ctx
->skip_all
= TRUE
;
1534 if (temp_status
!= FILE_RETRY
)
1536 return_status
= temp_status
;
1541 return_status
= FILE_CONT
;
1546 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1548 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1550 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1551 if (return_status
== FILE_RETRY
)
1553 if (return_status
== FILE_SKIPALL
)
1554 ctx
->skip_all
= TRUE
;
1555 if (return_status
== FILE_SKIP
)
1561 if (ctx
->do_reget
!= 0)
1563 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1565 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1567 ctx
->do_append
= FALSE
;
1571 while (mc_fstat (src_desc
, &sb
) != 0)
1574 return_status
= FILE_SKIPALL
;
1577 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1578 if (return_status
== FILE_RETRY
)
1580 if (return_status
== FILE_SKIPALL
)
1581 ctx
->skip_all
= TRUE
;
1582 ctx
->do_append
= FALSE
;
1587 src_mode
= sb
.st_mode
;
1588 src_uid
= sb
.st_uid
;
1589 src_gid
= sb
.st_gid
;
1590 utb
.actime
= sb
.st_atime
;
1591 utb
.modtime
= sb
.st_mtime
;
1592 file_size
= sb
.st_size
;
1594 open_flags
= O_WRONLY
;
1597 if (ctx
->do_append
!= 0)
1598 open_flags
|= O_APPEND
;
1600 open_flags
|= O_CREAT
| O_TRUNC
;
1604 open_flags
|= O_CREAT
| O_EXCL
;
1607 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1609 if (errno
!= EEXIST
)
1612 return_status
= FILE_SKIPALL
;
1615 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1616 if (return_status
== FILE_RETRY
)
1618 if (return_status
== FILE_SKIPALL
)
1619 ctx
->skip_all
= TRUE
;
1620 ctx
->do_append
= FALSE
;
1625 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1627 appending
= ctx
->do_append
;
1628 ctx
->do_append
= FALSE
;
1630 /* Find out the optimal buffer size. */
1631 while (mc_fstat (dest_desc
, &sb
) != 0)
1634 return_status
= FILE_SKIPALL
;
1637 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1638 if (return_status
== FILE_RETRY
)
1640 if (return_status
== FILE_SKIPALL
)
1641 ctx
->skip_all
= TRUE
;
1648 errno
= vfs_preallocate (dest_desc
, file_size
, (ctx
->do_append
!= 0) ? sb
.st_size
: 0);
1653 return_status
= FILE_SKIPALL
;
1657 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1658 if (return_status
== FILE_RETRY
)
1660 if (return_status
== FILE_SKIPALL
)
1661 ctx
->skip_all
= TRUE
;
1663 mc_close (dest_desc
);
1665 mc_unlink (dst_vpath
);
1666 dst_status
= DEST_NONE
;
1670 ctx
->eta_secs
= 0.0;
1673 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1674 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1676 file_progress_show (ctx
, 1, 1, "", TRUE
);
1677 return_status
= check_progress_buttons (ctx
);
1680 if (return_status
!= FILE_CONT
)
1684 off_t n_read_total
= 0;
1685 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1686 int secs
, update_secs
;
1687 const char *stalled_msg
= "";
1689 tv_last_update
= tv_transfer_start
;
1696 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1699 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0 && !ctx
->skip_all
)
1701 return_status
= file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
1702 if (return_status
== FILE_RETRY
)
1704 if (return_status
== FILE_SKIPALL
)
1705 ctx
->skip_all
= TRUE
;
1711 gettimeofday (&tv_current
, NULL
);
1716 n_read_total
+= n_read
;
1718 /* Windows NT ftp servers report that files have no
1719 * permissions: -------, so if we happen to have actually
1720 * read something, we should fix the permissions.
1722 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1723 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1724 gettimeofday (&tv_last_input
, NULL
);
1727 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
&& !ctx
->skip_all
)
1731 n_read
-= n_written
;
1735 return_status
= file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1736 if (return_status
== FILE_SKIP
)
1738 if (return_status
== FILE_SKIPALL
)
1739 ctx
->skip_all
= TRUE
;
1740 if (return_status
!= FILE_RETRY
)
1745 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1747 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1748 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1750 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1752 copy_file_file_display_progress (tctx
, ctx
,
1754 tv_transfer_start
, file_size
, n_read_total
);
1755 tv_last_update
= tv_current
;
1757 is_first_time
= FALSE
;
1759 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1761 stalled_msg
= _("(stalled)");
1765 gboolean force_update
;
1768 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1770 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1772 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1773 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1776 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1781 return_status
= check_progress_buttons (ctx
);
1783 if (return_status
!= FILE_CONT
)
1791 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1794 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1796 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1797 if (temp_status
== FILE_RETRY
)
1799 if (temp_status
== FILE_ABORT
)
1800 return_status
= temp_status
;
1801 if (temp_status
== FILE_SKIPALL
)
1802 ctx
->skip_all
= TRUE
;
1806 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1808 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1809 if (temp_status
== FILE_RETRY
)
1811 if (temp_status
== FILE_SKIPALL
)
1812 ctx
->skip_all
= TRUE
;
1813 return_status
= temp_status
;
1817 if (dst_status
== DEST_SHORT
)
1819 /* Remove short file */
1822 result
= query_dialog (Q_ ("DialogTitle|Copy"),
1823 _("Incomplete file was retrieved. Keep it?"),
1824 D_ERROR
, 2, _("&Delete"), _("&Keep"));
1826 mc_unlink (dst_vpath
);
1828 else if (dst_status
== DEST_FULL
)
1830 /* Copy has succeeded */
1831 if (!appending
&& ctx
->preserve_uidgid
)
1833 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1835 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1836 if (temp_status
== FILE_RETRY
)
1838 if (temp_status
== FILE_SKIPALL
)
1840 ctx
->skip_all
= TRUE
;
1841 return_status
= FILE_CONT
;
1843 if (temp_status
== FILE_SKIP
)
1844 return_status
= FILE_CONT
;
1853 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1855 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1856 if (temp_status
== FILE_RETRY
)
1858 if (temp_status
== FILE_SKIPALL
)
1860 ctx
->skip_all
= TRUE
;
1861 return_status
= FILE_CONT
;
1863 if (temp_status
== FILE_SKIP
)
1864 return_status
= FILE_CONT
;
1870 src_mode
= umask (-1);
1872 src_mode
= 0100666 & ~src_mode
;
1873 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
1875 mc_utime (dst_vpath
, &utb
);
1879 if (return_status
== FILE_CONT
)
1880 return_status
= progress_update_one (tctx
, ctx
, file_size
);
1883 vfs_path_free (src_vpath
);
1884 vfs_path_free (dst_vpath
);
1885 return return_status
;
1888 /* --------------------------------------------------------------------------------------------- */
1890 * I think these copy_*_* functions should have a return type.
1891 * anyway, this function *must* have two directories as arguments.
1893 /* FIXME: This function needs to check the return values of the
1897 copy_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *_d
,
1898 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, GSList
* parent_dirs
)
1900 struct dirent
*next
;
1901 struct stat buf
, cbuf
;
1903 char *dest_dir
= NULL
;
1904 FileProgressStatus return_status
= FILE_CONT
;
1908 vfs_path_t
*src_vpath
, *dst_vpath
, *dest_dir_vpath
= NULL
;
1912 src_vpath
= vfs_path_from_str (s
);
1913 dst_vpath
= vfs_path_from_str (_d
);
1915 /* First get the mode of the source dir */
1918 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
1921 return_status
= FILE_SKIPALL
;
1924 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
1925 if (return_status
== FILE_RETRY
)
1926 goto retry_src_stat
;
1927 if (return_status
== FILE_SKIPALL
)
1928 ctx
->skip_all
= TRUE
;
1933 if (is_in_linklist (dest_dirs
, src_vpath
, &cbuf
))
1935 /* Don't copy a directory we created before (we don't want to copy
1936 infinitely if a directory is copied into itself) */
1937 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1938 return_status
= FILE_CONT
;
1942 /* Hmm, hardlink to directory??? - Norbert */
1943 /* FIXME: In this step we should do something
1944 in case the destination already exist */
1945 /* Check the hardlinks */
1946 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &cbuf
))
1948 /* We have made a hardlink - no more processing is necessary */
1952 if (!S_ISDIR (cbuf
.st_mode
))
1955 return_status
= FILE_SKIPALL
;
1958 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
1959 if (return_status
== FILE_RETRY
)
1960 goto retry_src_stat
;
1961 if (return_status
== FILE_SKIPALL
)
1962 ctx
->skip_all
= TRUE
;
1967 if (is_in_linklist (parent_dirs
, src_vpath
, &cbuf
))
1969 /* we found a cyclic symbolic link */
1970 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
1971 return_status
= FILE_SKIP
;
1975 lp
= g_new0 (struct link
, 1);
1976 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
1977 lp
->ino
= cbuf
.st_ino
;
1978 lp
->dev
= cbuf
.st_dev
;
1979 parent_dirs
= g_slist_prepend (parent_dirs
, lp
);
1982 /* Now, check if the dest dir exists, if not, create it. */
1983 if (mc_stat (dst_vpath
, &buf
) != 0)
1985 /* Here the dir doesn't exist : make it ! */
1988 if (mc_rename (src_vpath
, dst_vpath
) == 0)
1990 return_status
= FILE_CONT
;
2000 * If the destination directory exists, we want to copy the whole
2001 * directory, but we only want this to happen once.
2003 * Escape sequences added to the * to compiler warnings.
2004 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2005 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2007 if (!S_ISDIR (buf
.st_mode
))
2010 return_status
= FILE_SKIPALL
;
2013 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2014 if (return_status
== FILE_SKIPALL
)
2015 ctx
->skip_all
= TRUE
;
2016 if (return_status
== FILE_RETRY
)
2017 goto retry_dst_stat
;
2021 /* Dive into subdir if exists */
2022 if (toplevel
&& ctx
->dive_into_subdirs
)
2024 dest_dir
= mc_build_filename (d
, x_basename (s
), NULL
);
2033 dest_dir_vpath
= vfs_path_from_str (dest_dir
);
2034 while (my_mkdir (dest_dir_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2037 return_status
= FILE_SKIPALL
;
2040 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir
);
2041 if (return_status
== FILE_SKIPALL
)
2042 ctx
->skip_all
= TRUE
;
2044 if (return_status
!= FILE_RETRY
)
2048 lp
= g_new0 (struct link
, 1);
2049 mc_stat (dest_dir_vpath
, &buf
);
2050 lp
->vfs
= vfs_path_get_by_index (dest_dir_vpath
, -1)->class;
2051 lp
->ino
= buf
.st_ino
;
2052 lp
->dev
= buf
.st_dev
;
2053 dest_dirs
= g_slist_prepend (dest_dirs
, lp
);
2055 if (ctx
->preserve_uidgid
)
2057 while (mc_chown (dest_dir_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2060 return_status
= FILE_SKIPALL
;
2064 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir
);
2065 if (return_status
== FILE_SKIPALL
)
2066 ctx
->skip_all
= TRUE
;
2068 if (return_status
!= FILE_RETRY
)
2074 /* open the source dir for reading */
2075 reading
= mc_opendir (src_vpath
);
2076 if (reading
== NULL
)
2079 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2082 vfs_path_t
*tmp_vpath
;
2084 * Now, we don't want '.' and '..' to be created / copied at any time
2086 if (!strcmp (next
->d_name
, "."))
2088 if (!strcmp (next
->d_name
, ".."))
2091 /* get the filename and add it to the src directory */
2092 path
= mc_build_filename (s
, next
->d_name
, NULL
);
2093 tmp_vpath
= vfs_path_from_str (path
);
2095 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2096 if (S_ISDIR (buf
.st_mode
))
2100 mdpath
= mc_build_filename (dest_dir
, next
->d_name
, NULL
);
2102 * From here, we just intend to recursively copy subdirs, not
2103 * the double functionality of copying different when the target
2104 * dir already exists. So, we give the recursive call the flag 0
2105 * meaning no toplevel.
2108 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2115 dest_file
= mc_build_filename (dest_dir
, x_basename (path
), NULL
);
2116 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2119 if (do_delete
&& return_status
== FILE_CONT
)
2121 if (ctx
->erase_at_end
)
2123 lp
= g_new0 (struct link
, 1);
2124 lp
->src_vpath
= vfs_path_clone (tmp_vpath
);
2125 lp
->st_mode
= buf
.st_mode
;
2126 erase_list
= g_slist_append (erase_list
, lp
);
2128 else if (S_ISDIR (buf
.st_mode
))
2129 return_status
= erase_dir_iff_empty (ctx
, path
);
2131 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
2134 vfs_path_free (tmp_vpath
);
2136 mc_closedir (reading
);
2140 mc_chmod (dest_dir_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2141 utb
.actime
= cbuf
.st_atime
;
2142 utb
.modtime
= cbuf
.st_mtime
;
2143 mc_utime (dest_dir_vpath
, &utb
);
2147 cbuf
.st_mode
= umask (-1);
2148 umask (cbuf
.st_mode
);
2149 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2150 mc_chmod (dest_dir_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2155 vfs_path_free (dest_dir_vpath
);
2156 free_link (parent_dirs
->data
);
2157 g_slist_free_1 (parent_dirs
);
2160 vfs_path_free (src_vpath
);
2161 vfs_path_free (dst_vpath
);
2162 return return_status
;
2167 /* --------------------------------------------------------------------------------------------- */
2168 /* {{{ Move routines */
2171 move_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
2173 struct stat sbuf
, dbuf
, destbuf
;
2176 FileProgressStatus return_status
;
2177 gboolean move_over
= FALSE
;
2179 vfs_path_t
*src_vpath
, *dst_vpath
, *destdir_vpath
;
2181 src_vpath
= vfs_path_from_str (s
);
2182 dst_vpath
= vfs_path_from_str (d
);
2184 file_progress_show_source (ctx
, src_vpath
);
2185 file_progress_show_target (ctx
, dst_vpath
);
2187 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2189 return_status
= FILE_ABORT
;
2195 mc_stat (src_vpath
, &sbuf
);
2197 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2198 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2200 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2205 destdir
= g_strdup (d
); /* destination doesn't exist */
2206 else if (!ctx
->dive_into_subdirs
)
2208 destdir
= g_strdup (d
);
2212 destdir
= mc_build_filename (d
, x_basename (s
), NULL
);
2214 destdir_vpath
= vfs_path_from_str (destdir
);
2216 /* Check if the user inputted an existing dir */
2218 if (mc_stat (destdir_vpath
, &destbuf
) == 0)
2222 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
2224 if (return_status
!= FILE_CONT
)
2228 else if (ctx
->skip_all
)
2229 return_status
= FILE_SKIPALL
;
2232 if (S_ISDIR (destbuf
.st_mode
))
2233 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir
);
2235 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir
);
2236 if (return_status
== FILE_SKIPALL
)
2237 ctx
->skip_all
= TRUE
;
2238 if (return_status
== FILE_RETRY
)
2239 goto retry_dst_stat
;
2243 vfs_path_free (destdir_vpath
);
2248 if (mc_rename (src_vpath
, destdir_vpath
) == 0)
2250 return_status
= FILE_CONT
;
2258 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2259 if (return_status
== FILE_SKIPALL
)
2260 ctx
->skip_all
= TRUE
;
2261 if (return_status
== FILE_RETRY
)
2266 /* Failed because of filesystem boundary -> copy dir instead */
2267 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
2269 if (return_status
!= FILE_CONT
)
2272 file_progress_show_source (ctx
, NULL
);
2273 file_progress_show (ctx
, 0, 0, "", FALSE
);
2275 return_status
= check_progress_buttons (ctx
);
2276 if (return_status
!= FILE_CONT
)
2280 if (ctx
->erase_at_end
)
2282 for (; erase_list
!= NULL
&& return_status
!= FILE_ABORT
;)
2284 lp
= (struct link
*) erase_list
->data
;
2286 if (S_ISDIR (lp
->st_mode
))
2290 src_path
= vfs_path_to_str (lp
->src_vpath
);
2291 return_status
= erase_dir_iff_empty (ctx
, src_path
);
2295 return_status
= erase_file (tctx
, ctx
, lp
->src_vpath
);
2297 erase_list
= g_slist_remove (erase_list
, lp
);
2301 erase_dir_iff_empty (ctx
, s
);
2305 vfs_path_free (destdir_vpath
);
2306 erase_list
= free_linklist (erase_list
);
2308 vfs_path_free (src_vpath
);
2309 vfs_path_free (dst_vpath
);
2310 return return_status
;
2315 /* --------------------------------------------------------------------------------------------- */
2316 /* {{{ Erase routines */
2319 erase_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const vfs_path_t
* s_vpath
)
2321 FileProgressStatus error
;
2324 s
= vfs_path_to_str (s_vpath
);
2327 if (strcmp (s, "..") == 0)
2330 if (strcmp (s, ".") == 0)
2334 file_progress_show_deleting (ctx
, s
);
2335 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2342 /* The old way to detect a non empty directory was:
2343 error = my_rmdir (s);
2344 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2345 For the linux user space nfs server (nfs-server-2.2beta29-2)
2346 we would have to check also for EIO. I hope the new way is
2347 fool proof. (Norbert)
2349 error
= check_dir_is_empty (s_vpath
);
2352 error
= query_recursive (ctx
, s
);
2353 if (error
== FILE_CONT
)
2354 error
= recursive_erase (tctx
, ctx
, s
);
2359 while (my_rmdir (s
) == -1 && !ctx
->skip_all
)
2361 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
2362 if (error
!= FILE_RETRY
)
2375 /* --------------------------------------------------------------------------------------------- */
2376 /* {{{ Panel operate routines */
2379 compute_dir_size_create_ui (void)
2381 ComputeDirSizeUI
*ui
;
2383 const char *b_name
= N_("&Abort");
2389 ui
= g_new (ComputeDirSizeUI
, 1);
2391 ui
->dlg
= create_dlg (TRUE
, 0, 0, 8, COLS
/ 2, dialog_colors
, NULL
,
2392 NULL
, _("Directory scanning"), DLG_CENTER
);
2393 ui
->dirname
= label_new (3, 3, "");
2394 add_widget (ui
->dlg
, ui
->dirname
);
2396 add_widget (ui
->dlg
,
2397 button_new (5, (ui
->dlg
->cols
- strlen (b_name
)) / 2,
2398 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
2400 /* We will manage the dialog without any help,
2401 that's why we have to call init_dlg */
2407 /* --------------------------------------------------------------------------------------------- */
2410 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
2414 /* schedule to update passive panel */
2415 other_panel
->dirty
= 1;
2417 /* close and destroy dialog */
2418 dlg_run_done (ui
->dlg
);
2419 destroy_dlg (ui
->dlg
);
2424 /* --------------------------------------------------------------------------------------------- */
2427 compute_dir_size_update_ui (const void *ui
, const vfs_path_t
* dirname_vpath
)
2429 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
2437 dirname
= vfs_path_to_str (dirname_vpath
);
2438 label_set_text (this->dirname
, str_trunc (dirname
, this->dlg
->cols
- 6));
2441 event
.x
= -1; /* Don't show the GPM cursor */
2442 c
= tty_get_event (&event
, FALSE
, FALSE
);
2446 /* Reinitialize to avoid old values after events other than
2447 selecting a button */
2448 this->dlg
->ret_value
= FILE_CONT
;
2450 dlg_process_event (this->dlg
, c
, &event
);
2452 switch (this->dlg
->ret_value
)
2462 /* --------------------------------------------------------------------------------------------- */
2466 * Computes the number of bytes used by the files in a directory
2470 compute_dir_size (const vfs_path_t
* dirname_vpath
, const void *ui
,
2471 compute_dir_size_callback cback
,
2472 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
2477 struct dirent
*dirent
;
2478 FileProgressStatus ret
= FILE_CONT
;
2480 if (!compute_symlinks
)
2482 res
= mc_lstat (dirname_vpath
, &s
);
2486 /* don't scan symlink to directory */
2487 if (S_ISLNK (s
.st_mode
))
2490 *ret_total
+= (uintmax_t) s
.st_size
;
2495 dir
= mc_opendir (dirname_vpath
);
2500 while ((dirent
= mc_readdir (dir
)) != NULL
)
2502 vfs_path_t
*tmp_vpath
;
2504 ret
= (cback
!= NULL
) ? cback (ui
, dirname_vpath
) : FILE_CONT
;
2506 if (ret
!= FILE_CONT
)
2509 if (strcmp (dirent
->d_name
, ".") == 0)
2511 if (strcmp (dirent
->d_name
, "..") == 0)
2514 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, NULL
);
2515 res
= mc_lstat (tmp_vpath
, &s
);
2518 if (S_ISDIR (s
.st_mode
))
2520 size_t subdir_count
= 0;
2521 uintmax_t subdir_bytes
= 0;
2524 compute_dir_size (tmp_vpath
, ui
, cback
, &subdir_count
, &subdir_bytes
,
2527 if (ret
!= FILE_CONT
)
2529 vfs_path_free (tmp_vpath
);
2533 *ret_marked
+= subdir_count
;
2534 *ret_total
+= subdir_bytes
;
2539 *ret_total
+= (uintmax_t) s
.st_size
;
2542 vfs_path_free (tmp_vpath
);
2549 /* --------------------------------------------------------------------------------------------- */
2553 * Performs one of the operations on the selection on the source_panel
2554 * (copy, delete, move).
2556 * Returns TRUE if did change the directory
2557 * structure, Returns FALSE if user aborted
2559 * force_single forces operation on the current entry and affects
2560 * default destination. Current filename is used as default.
2564 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2566 WPanel
*panel
= (WPanel
*) source_panel
;
2567 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2568 || (get_current_type () == view_tree
);
2570 char *source
= NULL
;
2571 #ifdef WITH_FULL_PATHS
2572 vfs_path_t
*source_with_vpath
= NULL
;
2573 char *source_with_path_str
= NULL
;
2575 #define source_with_path source
2576 #endif /* !WITH_FULL_PATHS */
2578 vfs_path_t
*dest_vpath
= NULL
;
2580 char *save_cwd
= NULL
, *save_dest
= NULL
;
2581 struct stat src_stat
;
2582 gboolean ret_val
= TRUE
;
2584 FileProgressStatus value
;
2586 FileOpTotalContext
*tctx
;
2587 vfs_path_t
*tmp_vpath
;
2589 gboolean do_bg
= FALSE
; /* do background operation? */
2591 static gboolean i18n_flag
= FALSE
;
2594 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
2595 op_names
[i
] = Q_ (op_names
[i
]);
2599 linklist
= free_linklist (linklist
);
2600 dest_dirs
= free_linklist (dest_dirs
);
2604 vfs_path_t
*source_vpath
;
2607 source
= g_strdup (selection (panel
)->fname
);
2609 source
= panel_get_file (panel
);
2611 if (strcmp (source
, "..") == 0)
2614 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2618 source_vpath
= vfs_path_from_str (source
);
2619 /* Update stat to get actual info */
2620 if (mc_stat (source_vpath
, &src_stat
) != 0)
2622 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2623 path_trunc (source
, 30), unix_error_string (errno
));
2625 /* Directory was changed outside MC. Reload it forced */
2626 if (!panel
->is_panelized
)
2628 panel_update_flags_t flags
= UP_RELOAD
;
2630 /* don't update panelized panel */
2631 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2632 flags
|= UP_ONLY_CURRENT
;
2634 update_panels (flags
, UP_KEEPSEL
);
2636 vfs_path_free (source_vpath
);
2639 vfs_path_free (source_vpath
);
2642 ctx
= file_op_context_new (operation
);
2644 /* Show confirmation dialog */
2645 if (operation
!= OP_DELETE
)
2647 char *tmp_dest_dir
, *dest_dir
;
2650 /* Forced single operations default to the original name */
2652 tmp_dest_dir
= g_strdup (source
);
2653 else if (get_other_type () == view_listing
)
2654 tmp_dest_dir
= vfs_path_to_str (other_panel
->cwd_vpath
);
2656 tmp_dest_dir
= vfs_path_to_str (panel
->cwd_vpath
);
2658 * Add trailing backslash only when do non-local ops.
2659 * It saves user from occasional file renames (when destination
2662 if (!force_single
&& tmp_dest_dir
[0] != '\0'
2663 && tmp_dest_dir
[strlen (tmp_dest_dir
) - 1] != PATH_SEP
)
2665 /* add trailing separator */
2666 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2667 g_free (tmp_dest_dir
);
2672 dest_dir
= tmp_dest_dir
;
2674 if (dest_dir
== NULL
)
2680 /* Generate confirmation prompt */
2681 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2683 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2684 source
!= NULL
? (void *) source
2685 : (void *) &panel
->marked
, dest_dir
, &do_bg
);
2690 if (dest
== NULL
|| dest
[0] == '\0')
2696 dest_vpath
= vfs_path_from_str (dest
);
2698 else if (confirm_delete
)
2701 char fmd_buf
[BUF_MEDIUM
];
2703 /* Generate confirmation prompt */
2704 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2707 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2710 const int fmd_xlen
= 64;
2711 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2712 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2720 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2729 tctx
= file_op_total_context_new ();
2730 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2733 filegui_dialog_type_t dialog_type
;
2735 if (operation
== OP_DELETE
)
2736 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2739 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2740 ? FILEGUI_DIALOG_MULTI_ITEM
: FILEGUI_DIALOG_ONE_ITEM
;
2742 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2743 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2746 /* Background also need ctx->ui, but not full */
2748 file_op_context_create_ui_without_init (ctx
, TRUE
, dialog_type
);
2750 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
2753 #ifdef WITH_BACKGROUND
2754 /* Did the user select to do a background operation? */
2760 cwd_str
= vfs_path_to_str (panel
->cwd_vpath
);
2761 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", cwd_str
, (char *) NULL
));
2764 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2766 /* If we are the parent */
2769 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2771 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2772 vfs_path_free (dest_vpath
);
2774 /* file_op_context_destroy (ctx); */
2778 #endif /* WITH_BACKGROUND */
2780 /* Initialize things */
2781 /* We do not want to trash cache every time file is
2782 created/touched. However, this will make our cache contain
2784 if ((dest
!= NULL
) && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2785 save_dest
= g_strdup (dest
);
2787 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2788 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2789 save_cwd
= vfs_path_to_str (panel
->cwd_vpath
);
2791 /* Now, let's do the job */
2793 /* This code is only called by the tree and panel code */
2796 /* We now have ETA in all cases */
2798 /* One file: FIXME mc_chdir will take user out of any vfs */
2799 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2804 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2805 chdir_retcode
= mc_chdir (vpath
);
2806 vfs_path_free (vpath
);
2807 if (chdir_retcode
< 0)
2814 /* The source and src_stat variables have been initialized before */
2815 #ifdef WITH_FULL_PATHS
2816 if (g_path_is_absolute (source
))
2817 source_with_vpath
= vfs_path_from_str (source
);
2819 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2820 source_with_path_str
= vfs_path_to_str (source_with_vpath
);
2821 #endif /* WITH_FULL_PATHS */
2822 if (panel_operate_init_totals (operation
, panel
, source_with_path_str
, ctx
) == FILE_CONT
)
2824 if (operation
== OP_DELETE
)
2826 if (S_ISDIR (src_stat
.st_mode
))
2827 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2829 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2833 temp
= transform_source (ctx
, source_with_path_str
);
2835 value
= transform_error
;
2838 char *repl_dest
, *temp2
;
2840 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2841 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2845 vfs_path_free (dest_vpath
);
2847 dest_vpath
= vfs_path_from_str (dest
);
2852 /* we use file_mask_op_follow_links only with OP_COPY */
2853 ctx
->stat_func (source_with_vpath
, &src_stat
);
2855 if (S_ISDIR (src_stat
.st_mode
))
2856 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, dest
,
2857 TRUE
, FALSE
, FALSE
, NULL
);
2859 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, dest
);
2863 if (S_ISDIR (src_stat
.st_mode
))
2864 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, dest
);
2866 value
= move_file_file (tctx
, ctx
, source_with_path_str
, dest
);
2870 /* Unknown file operation */
2874 } /* Copy or move operation */
2876 if ((value
== FILE_CONT
) && !force_single
)
2877 unmark_files (panel
);
2884 /* Check destination for copy or move operation */
2885 while (operation
!= OP_DELETE
)
2888 struct stat dst_stat
;
2890 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2892 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2896 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2900 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
)
2902 /* Loop for every file, perform the actual copy operation */
2903 for (i
= 0; i
< panel
->count
; i
++)
2905 const char *source2
;
2907 if (!panel
->dir
.list
[i
].f
.marked
)
2908 continue; /* Skip the unmarked ones */
2910 source2
= panel
->dir
.list
[i
].fname
;
2911 src_stat
= panel
->dir
.list
[i
].st
;
2913 #ifdef WITH_FULL_PATHS
2914 g_free (source_with_path_str
);
2915 vfs_path_free (source_with_vpath
);
2916 if (g_path_is_absolute (source2
))
2917 source_with_vpath
= vfs_path_from_str (source2
);
2920 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2921 source_with_path_str
= vfs_path_to_str (source_with_vpath
);
2922 #endif /* WITH_FULL_PATHS */
2924 if (operation
== OP_DELETE
)
2926 if (S_ISDIR (src_stat
.st_mode
))
2927 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2929 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2933 temp
= transform_source (ctx
, source_with_path_str
);
2936 value
= transform_error
;
2939 char *temp2
, *temp3
, *repl_dest
;
2941 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2942 temp2
= mc_build_filename (repl_dest
, temp
, NULL
);
2945 temp3
= source_with_path_str
;
2946 source_with_path_str
= strutils_shell_unescape (source_with_path_str
);
2949 temp2
= strutils_shell_unescape (temp2
);
2955 /* we use file_mask_op_follow_links only with OP_COPY */
2959 vpath
= vfs_path_from_str (source_with_path_str
);
2960 ctx
->stat_func (vpath
, &src_stat
);
2961 vfs_path_free (vpath
);
2963 if (S_ISDIR (src_stat
.st_mode
))
2964 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
,
2965 TRUE
, FALSE
, FALSE
, NULL
);
2967 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
2968 dest_dirs
= free_linklist (dest_dirs
);
2972 if (S_ISDIR (src_stat
.st_mode
))
2973 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp2
);
2975 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp2
);
2979 /* Unknown file operation */
2985 } /* Copy or move operation */
2987 if (value
== FILE_ABORT
)
2990 if (value
== FILE_CONT
)
2991 do_file_mark (panel
, i
, 0);
2993 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
2995 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2996 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
2999 if (operation
!= OP_DELETE
)
3000 file_progress_show (ctx
, 0, 0, "", FALSE
);
3002 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3006 } /* Loop for every file */
3008 } /* Many entries */
3012 if (save_cwd
!= NULL
)
3014 tmp_vpath
= vfs_path_from_str (save_cwd
);
3015 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3016 vfs_path_free (tmp_vpath
);
3020 if (save_dest
!= NULL
)
3022 tmp_vpath
= vfs_path_from_str (save_dest
);
3023 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3024 vfs_path_free (tmp_vpath
);
3028 linklist
= free_linklist (linklist
);
3029 dest_dirs
= free_linklist (dest_dirs
);
3030 #ifdef WITH_FULL_PATHS
3031 g_free (source_with_path_str
);
3032 vfs_path_free (source_with_vpath
);
3033 #endif /* WITH_FULL_PATHS */
3035 vfs_path_free (dest_vpath
);
3036 g_free (ctx
->dest_mask
);
3037 ctx
->dest_mask
= NULL
;
3039 #ifdef WITH_BACKGROUND
3040 /* Let our parent know we are saying bye bye */
3041 if (mc_global
.we_are_background
)
3043 int cur_pid
= getpid ();
3044 /* Send pid to parent with child context, it is fork and
3045 don't modify real parent ctx */
3047 parent_call ((void *) end_bg_process
, ctx
, 0);
3052 #endif /* WITH_BACKGROUND */
3054 file_op_total_context_destroy (tctx
);
3056 file_op_context_destroy (ctx
);
3064 /* --------------------------------------------------------------------------------------------- */
3065 /* {{{ Query/status report routines */
3066 /** Report error with one file */
3068 file_error (const char *format
, const char *file
)
3070 char buf
[BUF_MEDIUM
];
3072 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3074 return do_file_error (buf
);
3077 /* --------------------------------------------------------------------------------------------- */
3080 Cause emacs to enter folding mode for this file: