4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011
6 The Free Software Foundation, Inc.
9 Janne Kukonlehto, 1994, 1995
10 Fred Leeflang, 1994, 1995
11 Miguel de Icaza, 1994, 1995, 1996
12 Jakub Jelinek, 1995, 1996
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
47 * \brief Source: file management
50 /* {{{ Include files */
59 #include <sys/types.h>
64 #include "lib/global.h"
65 #include "lib/tty/tty.h"
66 #include "lib/tty/key.h"
67 #include "lib/search.h"
68 #include "lib/strescape.h"
69 #include "lib/strutil.h"
71 #include "lib/vfs/vfs.h"
72 #include "lib/widget.h"
74 #include "src/setup.h"
75 #ifdef ENABLE_BACKGROUND
76 #include "src/background.h" /* do_background() */
79 #include "layout.h" /* rotate_dash() */
81 /* Needed for current_panel, other_panel and WTree */
85 #include "midnight.h" /* current_panel */
91 /*** global variables ****************************************************************************/
93 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
94 const char *op_names
[3] = {
95 N_("DialogTitle|Copy"),
96 N_("DialogTitle|Move"),
97 N_("DialogTitle|Delete")
100 /*** file scope macro definitions ****************************************************************/
102 /* Hack: the vfs code should not rely on this */
103 #define WITH_FULL_PATHS 1
105 #define FILEOP_UPDATE_INTERVAL 2
106 #define FILEOP_STALLING_INTERVAL 4
108 /*** file scope type declarations ****************************************************************/
110 /* This is a hard link cache */
114 struct vfs_class
*vfs
;
122 /* Status of the destination file */
125 DEST_NONE
= 0, /* Not created */
126 DEST_SHORT
= 1, /* Created, not fully copied */
127 DEST_FULL
= 2 /* Created, fully copied */
131 * This array introduced to avoid translation problems. The former (op_names)
132 * is assumed to be nouns, suitable in dialog box titles; this one should
133 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
134 * (I don't use spaces around the words, because someday they could be
135 * dropped, when widgets get smarter)
138 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
139 static const char *op_names1
[] = {
140 N_("FileOperation|Copy"),
141 N_("FileOperation|Move"),
142 N_("FileOperation|Delete")
146 * These are formats for building a prompt. Parts encoded as follows:
147 * %o - operation from op_names1
148 * %f - file/files or files/directories, as appropriate
149 * %m - "with source mask" or question mark for delete
150 * %s - source name (truncated)
151 * %d - number of marked files
152 * %e - "to:" or question mark for delete
154 * xgettext:no-c-format */
155 static const char *one_format
= N_("%o %f \"%s\"%m");
156 /* xgettext:no-c-format */
157 static const char *many_format
= N_("%o %d %f%m");
159 static const char *prompt_parts
[] = {
164 N_("files/directories"),
165 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
166 N_(" with source mask:"),
170 static const char *question_format
= N_("%s?");
172 /*** file scope variables ************************************************************************/
174 /* the hard link cache */
175 static struct link
*linklist
= NULL
;
177 /* the files-to-be-erased list */
178 static struct link
*erase_list
;
181 * In copy_dir_dir we use two additional single linked lists: The first -
182 * variable name `parent_dirs' - holds information about already copied
183 * directories and is used to detect cyclic symbolic links.
184 * The second (`dest_dirs' below) holds information about just created
185 * target directories and is used to detect when an directory is copied
186 * into itself (we don't want to copy infinitly).
187 * Both lists don't use the linkcount and name structure members of struct
190 static struct link
*dest_dirs
= NULL
;
192 static FileProgressStatus transform_error
= FILE_CONT
;
194 /*** file scope functions ************************************************************************/
195 /* --------------------------------------------------------------------------------------------- */
198 transform_source (FileOpContext
* ctx
, const char *source
)
203 s
= g_strdup (source
);
205 /* We remove \n from the filename since regex routines would use \n as an anchor */
206 /* this is just to be allowed to maniupulate file names with \n on it */
207 for (q
= s
; *q
!= '\0'; q
++)
211 fnsource
= (char *) x_basename (s
);
213 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
214 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
218 transform_error
= FILE_SKIP
;
225 /* --------------------------------------------------------------------------------------------- */
228 free_linklist (struct link
**lc_linklist
)
230 struct link
*lp
, *lp2
;
232 for (lp
= *lc_linklist
; lp
!= NULL
; lp
= lp2
)
240 /* --------------------------------------------------------------------------------------------- */
243 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
246 vfs_path_element_t
*vpath_element
;
247 ino_t ino
= sb
->st_ino
;
248 dev_t dev
= sb
->st_dev
;
250 vpath
= vfs_path_from_str (path
);
251 vpath_element
= vfs_path_get_by_index (vpath
, -1);
255 if (lp
->vfs
== vpath_element
->class)
256 if (lp
->ino
== ino
&& lp
->dev
== dev
)
260 vfs_path_free (vpath
);
264 /* --------------------------------------------------------------------------------------------- */
266 * Check and made hardlink
268 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
269 * and a hardlink was succesfully made
273 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
278 struct vfs_class
*my_vfs
;
279 ino_t ino
= pstat
->st_ino
;
280 dev_t dev
= pstat
->st_dev
;
281 struct stat link_stat
;
284 vpath
= vfs_path_from_str (src_name
);
286 if ((vfs_file_class_flags (vpath
) & VFSF_NOLINKS
) != 0)
288 vfs_path_free (vpath
);
291 my_vfs
= vfs_path_get_by_index (vpath
, -1)->class;
292 vfs_path_free (vpath
);
294 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
295 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
)
297 struct vfs_class
*lp_name_class
;
300 vpath
= vfs_path_from_str (lp
->name
);
301 lp_name_class
= vfs_path_get_by_index (vpath
, -1)->class;
302 stat_result
= mc_stat (vpath
, &link_stat
);
303 vfs_path_free (vpath
);
305 if (!stat_result
&& link_stat
.st_ino
== ino
306 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
308 struct vfs_class
*p_class
, *dst_name_class
;
310 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
313 vpath
= vfs_path_from_str (dst_name
);
314 dst_name_class
= vfs_path_get_by_index (vpath
, -1)->class;
315 vfs_path_free (vpath
);
317 vpath
= vfs_path_from_str (p
);
318 p_class
= vfs_path_get_by_index (vpath
, -1)->class;
320 if (dst_name_class
== p_class
)
322 if (!mc_stat (vpath
, &link_stat
))
324 if (!mc_link (p
, dst_name
))
326 vfs_path_free (vpath
);
331 vfs_path_free (vpath
);
334 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
337 lp
= (struct link
*) g_try_malloc (sizeof (struct link
) + strlen (src_name
)
338 + strlen (dst_name
) + 1);
345 strcpy (lp
->name
, src_name
);
346 lpdstname
= lp
->name
+ strlen (lp
->name
) + 1;
347 strcpy (lpdstname
, dst_name
);
354 /* --------------------------------------------------------------------------------------------- */
356 * Duplicate the contents of the symbolic link src_path in dst_path.
357 * Try to make a stable symlink if the option "stable symlink" was
358 * set in the file mask dialog.
359 * If dst_path is an existing symlink it will be deleted silently
360 * (upper levels take already care of existing files at dst_path).
363 static FileProgressStatus
364 make_symlink (FileOpContext
* ctx
, const char *src_path
, const char *dst_path
)
366 char link_target
[MC_MAXPATHLEN
];
368 FileProgressStatus return_status
;
370 gboolean dst_is_symlink
;
372 vfs_path_t
*src_vpath
= vfs_path_from_str (src_path
);
373 vfs_path_t
*dst_vpath
= vfs_path_from_str (dst_path
);
375 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
378 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
382 return_status
= FILE_SKIPALL
;
385 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
386 if (return_status
== FILE_SKIPALL
)
387 ctx
->skip_all
= TRUE
;
388 if (return_status
== FILE_RETRY
)
389 goto retry_src_readlink
;
391 return return_status
;
393 link_target
[len
] = 0;
395 if (ctx
->stable_symlinks
)
398 if (!vfs_file_is_local (src_vpath
) || !vfs_file_is_local (dst_vpath
))
400 message (D_ERROR
, MSG_ERROR
,
401 _("Cannot make stable symlinks across"
402 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
403 ctx
->stable_symlinks
= FALSE
;
407 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
411 const char *r
= strrchr (src_path
, PATH_SEP
);
415 p
= g_strndup (src_path
, r
- src_path
+ 1);
416 if (g_path_is_absolute (dst_path
))
417 q
= g_strdup (dst_path
);
419 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
420 s
= strrchr (q
, PATH_SEP
);
424 s
= g_strconcat (p
, link_target
, (char *) NULL
);
426 g_strlcpy (link_target
, s
, sizeof (link_target
));
428 s
= diff_two_paths (q
, link_target
);
431 g_strlcpy (link_target
, s
, sizeof (link_target
));
441 if (mc_symlink (link_target
, dst_path
) == 0)
444 vfs_path_free (src_vpath
);
445 vfs_path_free (dst_vpath
);
449 * if dst_exists, it is obvious that this had failed.
450 * We can delete the old symlink and try again...
454 if (!mc_unlink (dst_path
))
455 if (mc_symlink (link_target
, dst_path
) == 0)
458 vfs_path_free (src_vpath
);
459 vfs_path_free (dst_vpath
);
465 return_status
= FILE_SKIPALL
;
468 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
469 if (return_status
== FILE_SKIPALL
)
470 ctx
->skip_all
= TRUE
;
471 if (return_status
== FILE_RETRY
)
472 goto retry_dst_symlink
;
474 vfs_path_free (src_vpath
);
475 vfs_path_free (dst_vpath
);
476 return return_status
;
479 /* --------------------------------------------------------------------------------------------- */
481 static FileProgressStatus
482 progress_update_one (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, off_t add
)
484 struct timeval tv_current
;
485 static struct timeval tv_start
= { };
487 tctx
->progress_count
++;
488 tctx
->progress_bytes
+= (uintmax_t) add
;
490 if (tv_start
.tv_sec
== 0)
492 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
494 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
495 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
497 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
499 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
500 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
502 tv_start
.tv_sec
= tv_current
.tv_sec
;
505 return check_progress_buttons (ctx
);
508 /* --------------------------------------------------------------------------------------------- */
510 static FileProgressStatus
511 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
515 const char *head_msg
;
517 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
519 msg
= g_strdup_printf (fmt
, a
, b
);
520 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
524 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
527 /* --------------------------------------------------------------------------------------------- */
529 static FileProgressStatus
530 warn_same_file (const char *fmt
, const char *a
, const char *b
)
532 #ifdef ENABLE_BACKGROUND
537 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
538 const char *a
, const char *b
);
542 pntr
.f
= real_warn_same_file
;
544 if (mc_global
.we_are_background
)
545 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
547 return real_warn_same_file (Foreground
, fmt
, a
, b
);
550 /* --------------------------------------------------------------------------------------------- */
551 /* {{{ Query/status report routines */
553 static FileProgressStatus
554 real_do_file_error (enum OperationMode mode
, const char *error
)
559 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
561 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
583 /* --------------------------------------------------------------------------------------------- */
585 static FileProgressStatus
586 real_query_recursive (FileOpContext
* ctx
, enum OperationMode mode
, const char *s
)
590 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
592 const char *msg
= mode
== Foreground
593 ? _("\nDirectory not empty.\nDelete it recursively?")
594 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
595 text
= g_strconcat (_("Delete:"), " ", path_trunc (s
, 30), (char *) NULL
);
600 ctx
->recursive_result
=
601 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
602 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
604 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
609 switch (ctx
->recursive_result
)
612 case RECURSIVE_ALWAYS
:
616 case RECURSIVE_NEVER
:
619 case RECURSIVE_ABORT
:
625 /* --------------------------------------------------------------------------------------------- */
627 #ifdef ENABLE_BACKGROUND
628 static FileProgressStatus
629 do_file_error (const char *str
)
634 FileProgressStatus (*f
) (enum OperationMode
, const char *);
636 pntr
.f
= real_do_file_error
;
638 if (mc_global
.we_are_background
)
639 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
641 return real_do_file_error (Foreground
, str
);
644 /* --------------------------------------------------------------------------------------------- */
646 static FileProgressStatus
647 query_recursive (FileOpContext
* ctx
, const char *s
)
652 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
654 pntr
.f
= real_query_recursive
;
656 if (mc_global
.we_are_background
)
657 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
659 return real_query_recursive (ctx
, Foreground
, s
);
662 /* --------------------------------------------------------------------------------------------- */
664 static FileProgressStatus
665 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
666 struct stat
*_d_stat
)
671 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
672 struct stat
*, struct stat
*);
674 pntr
.f
= file_progress_real_query_replace
;
676 if (mc_global
.we_are_background
)
677 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
678 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
680 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
684 /* --------------------------------------------------------------------------------------------- */
686 static FileProgressStatus
687 do_file_error (const char *str
)
689 return real_do_file_error (Foreground
, str
);
692 /* --------------------------------------------------------------------------------------------- */
694 static FileProgressStatus
695 query_recursive (FileOpContext
* ctx
, const char *s
)
697 return real_query_recursive (ctx
, Foreground
, s
);
700 /* --------------------------------------------------------------------------------------------- */
702 static FileProgressStatus
703 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
704 struct stat
*_d_stat
)
706 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
709 #endif /* !ENABLE_BACKGROUND */
711 /* --------------------------------------------------------------------------------------------- */
712 /** Report error with two files */
714 static FileProgressStatus
715 files_error (const char *format
, const char *file1
, const char *file2
)
717 char buf
[BUF_MEDIUM
];
718 char *nfile1
= g_strdup (path_trunc (file1
, 15));
719 char *nfile2
= g_strdup (path_trunc (file2
, 15));
721 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
726 return do_file_error (buf
);
731 /* --------------------------------------------------------------------------------------------- */
734 copy_file_file_display_progress (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
735 struct timeval tv_current
, struct timeval tv_transfer_start
,
736 off_t file_size
, off_t n_read_total
)
740 /* 1. Update rotating dash after some time */
744 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
746 if (n_read_total
== 0)
750 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
751 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
754 /* 4. Compute BPS rate */
755 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
756 if (ctx
->bps_time
< 1)
758 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
760 /* 5. Compute total ETA and BPS */
761 if (ctx
->progress_bytes
!= 0)
763 uintmax_t remain_bytes
;
765 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
768 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
773 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
774 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
777 /* broken on lot of little files */
779 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
780 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
785 /* --------------------------------------------------------------------------------------------- */
787 /* {{{ Move routines */
788 static FileProgressStatus
789 move_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
791 struct stat src_stats
, dst_stats
;
792 FileProgressStatus return_status
= FILE_CONT
;
793 gboolean copy_done
= FALSE
;
794 gboolean old_ask_overwrite
;
795 vfs_path_t
*src_vpath
, *dst_vpath
;
797 file_progress_show_source (ctx
, s
);
798 file_progress_show_target (ctx
, d
);
799 if (check_progress_buttons (ctx
) == FILE_ABORT
)
803 src_vpath
= vfs_path_from_str (s
);
804 dst_vpath
= vfs_path_from_str (d
);
806 while (mc_lstat (src_vpath
, &src_stats
) != 0)
808 /* Source doesn't exist */
810 return_status
= FILE_SKIPALL
;
813 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
814 if (return_status
== FILE_SKIPALL
)
815 ctx
->skip_all
= TRUE
;
817 if (return_status
!= FILE_RETRY
)
819 vfs_path_free (src_vpath
);
820 vfs_path_free (dst_vpath
);
821 return return_status
;
825 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
827 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
828 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
830 if (S_ISDIR (dst_stats
.st_mode
))
832 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
834 vfs_path_free (src_vpath
);
835 vfs_path_free (dst_vpath
);
839 if (confirm_overwrite
)
841 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
842 if (return_status
!= FILE_CONT
)
844 vfs_path_free (src_vpath
);
845 vfs_path_free (dst_vpath
);
846 return return_status
;
849 /* Ok to overwrite */
854 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
856 return_status
= make_symlink (ctx
, s
, d
);
857 if (return_status
== FILE_CONT
)
858 goto retry_src_remove
;
861 vfs_path_free (src_vpath
);
862 vfs_path_free (dst_vpath
);
863 return return_status
;
867 if (mc_rename (s
, d
) == 0)
869 vfs_path_free (src_vpath
);
870 vfs_path_free (dst_vpath
);
871 return progress_update_one (tctx
, ctx
, src_stats
.st_size
);
875 /* Comparison to EXDEV seems not to work in nfs if you're moving from
876 one nfs to the same, but on the server it is on two different
877 filesystems. Then nfs returns EIO instead of EXDEV.
878 Hope it will not hurt if we always in case of error try to copy/delete. */
880 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
885 return_status
= FILE_SKIPALL
;
888 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
889 if (return_status
== FILE_SKIPALL
)
890 ctx
->skip_all
= TRUE
;
891 if (return_status
== FILE_RETRY
)
894 vfs_path_free (src_vpath
);
895 vfs_path_free (dst_vpath
);
897 return return_status
;
901 /* Failed because filesystem boundary -> copy the file instead */
902 old_ask_overwrite
= tctx
->ask_overwrite
;
903 tctx
->ask_overwrite
= FALSE
;
904 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
905 tctx
->ask_overwrite
= old_ask_overwrite
;
906 if (return_status
!= FILE_CONT
)
908 vfs_path_free (src_vpath
);
909 vfs_path_free (dst_vpath
);
910 return return_status
;
915 file_progress_show_source (ctx
, NULL
);
916 file_progress_show (ctx
, 0, 0, "", FALSE
);
918 return_status
= check_progress_buttons (ctx
);
919 if (return_status
!= FILE_CONT
)
921 vfs_path_free (src_vpath
);
922 vfs_path_free (dst_vpath
);
923 return return_status
;
929 if (mc_unlink (s
) != 0 && !ctx
->skip_all
)
931 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
932 if (return_status
== FILE_RETRY
)
933 goto retry_src_remove
;
934 if (return_status
== FILE_SKIPALL
)
935 ctx
->skip_all
= TRUE
;
937 vfs_path_free (src_vpath
);
938 vfs_path_free (dst_vpath
);
939 return return_status
;
943 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
945 vfs_path_free (src_vpath
);
946 vfs_path_free (dst_vpath
);
948 return return_status
;
953 /* --------------------------------------------------------------------------------------------- */
954 /* {{{ Erase routines */
955 /** Don't update progress status if progress_count==NULL */
957 static FileProgressStatus
958 erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
962 vfs_path_t
*vpath
= vfs_path_from_str (s
);
964 file_progress_show_deleting (ctx
, s
);
965 if (check_progress_buttons (ctx
) == FILE_ABORT
)
969 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
971 /* ignore, most likely the mc_unlink fails, too */
975 while (mc_unlink (s
) != 0 && !ctx
->skip_all
)
977 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), s
);
978 if (return_status
== FILE_ABORT
)
980 vfs_path_free (vpath
);
981 return return_status
;
983 if (return_status
== FILE_RETRY
)
985 if (return_status
== FILE_SKIPALL
)
986 ctx
->skip_all
= TRUE
;
990 vfs_path_free (vpath
);
991 if (tctx
->progress_count
== 0)
993 return progress_update_one (tctx
, ctx
, buf
.st_size
);
996 /* --------------------------------------------------------------------------------------------- */
999 Recursive remove of files
1001 skip ->warn every level, gets default
1002 skipall->remove as much as possible
1004 static FileProgressStatus
1005 recursive_erase (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
1007 struct dirent
*next
;
1011 FileProgressStatus return_status
= FILE_CONT
;
1014 if (strcmp (s
, "..") == 0)
1017 vpath
= vfs_path_from_str (s
);
1018 reading
= mc_opendir (vpath
);
1020 if (reading
== NULL
)
1022 return_status
= FILE_RETRY
;
1026 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1028 vfs_path_t
*tmp_vpath
;
1030 if (!strcmp (next
->d_name
, "."))
1032 if (!strcmp (next
->d_name
, ".."))
1034 path
= concat_dir_and_file (s
, next
->d_name
);
1035 tmp_vpath
= vfs_path_from_str (path
);
1036 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1039 mc_closedir (reading
);
1040 vfs_path_free (tmp_vpath
);
1041 return_status
= FILE_RETRY
;
1044 if (S_ISDIR (buf
.st_mode
))
1045 return_status
= recursive_erase (tctx
, ctx
, path
);
1047 return_status
= erase_file (tctx
, ctx
, path
);
1048 vfs_path_free (tmp_vpath
);
1051 mc_closedir (reading
);
1052 if (return_status
== FILE_ABORT
)
1055 file_progress_show_deleting (ctx
, s
);
1056 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1058 return_status
= FILE_ABORT
;
1063 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1065 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1066 if (return_status
== FILE_RETRY
)
1068 if (return_status
== FILE_ABORT
)
1070 if (return_status
== FILE_SKIPALL
)
1071 ctx
->skip_all
= TRUE
;
1076 vfs_path_free (vpath
);
1077 return return_status
;
1080 /* --------------------------------------------------------------------------------------------- */
1081 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1082 in the directory path points to, 0 else. */
1085 check_dir_is_empty (const char *path
)
1090 vfs_path_t
*vpath
= vfs_path_from_str (path
);
1092 dir
= mc_opendir (vpath
);
1095 vfs_path_free (vpath
);
1099 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
))
1101 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1102 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1103 continue; /* "." or ".." */
1109 vfs_path_free (vpath
);
1113 /* --------------------------------------------------------------------------------------------- */
1115 static FileProgressStatus
1116 erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
)
1118 FileProgressStatus error
;
1120 if (strcmp (s
, "..") == 0)
1123 if (strcmp (s
, ".") == 0)
1126 file_progress_show_deleting (ctx
, s
);
1127 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1131 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1134 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1136 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1137 if (error
== FILE_SKIPALL
)
1138 ctx
->skip_all
= TRUE
;
1139 if (error
!= FILE_RETRY
)
1148 /* --------------------------------------------------------------------------------------------- */
1149 /* {{{ Panel operate routines */
1152 * Return currently selected entry name or the name of the first marked
1153 * entry if there is one.
1157 panel_get_file (WPanel
* panel
)
1159 if (get_current_type () == view_tree
)
1163 tree
= (WTree
*) get_panel_widget (get_current_index ());
1164 return tree_selected_name (tree
);
1167 if (panel
->marked
!= 0)
1171 for (i
= 0; i
< panel
->count
; i
++)
1172 if (panel
->dir
.list
[i
].f
.marked
)
1173 return panel
->dir
.list
[i
].fname
;
1176 return panel
->dir
.list
[panel
->selected
].fname
;
1179 /* --------------------------------------------------------------------------------------------- */
1181 * panel_compute_totals:
1183 * compute the number of files and the number of bytes
1184 * used up by the whole selection, recursing directories
1185 * as required. In addition, it checks to see if it will
1186 * overwrite any files by doing the copy.
1189 static FileProgressStatus
1190 panel_compute_totals (const WPanel
* panel
, const void *ui
,
1191 compute_dir_size_callback cback
,
1192 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
1199 for (i
= 0; i
< panel
->count
; i
++)
1203 if (!panel
->dir
.list
[i
].f
.marked
)
1206 s
= &panel
->dir
.list
[i
].st
;
1208 if (S_ISDIR (s
->st_mode
))
1211 size_t subdir_count
= 0;
1212 uintmax_t subdir_bytes
= 0;
1213 FileProgressStatus status
;
1215 dir_name
= concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1217 status
= compute_dir_size (dir_name
, ui
, cback
,
1218 &subdir_count
, &subdir_bytes
, compute_symlinks
);
1221 if (status
!= FILE_CONT
)
1224 *ret_marked
+= subdir_count
;
1225 *ret_total
+= subdir_bytes
;
1230 *ret_total
+= (uintmax_t) s
->st_size
;
1237 /* --------------------------------------------------------------------------------------------- */
1239 /** Initialize variables for progress bars */
1240 static FileProgressStatus
1241 panel_operate_init_totals (FileOperation operation
,
1242 const WPanel
* panel
, const char *source
, FileOpContext
* ctx
)
1244 FileProgressStatus status
;
1246 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
)
1248 ComputeDirSizeUI
*ui
;
1250 ui
= compute_dir_size_create_ui ();
1253 status
= compute_dir_size (source
, ui
, compute_dir_size_update_ui
,
1254 &ctx
->progress_count
, &ctx
->progress_bytes
,
1257 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1258 &ctx
->progress_count
, &ctx
->progress_bytes
,
1261 compute_dir_size_destroy_ui (ui
);
1263 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1268 ctx
->progress_count
= panel
->marked
;
1269 ctx
->progress_bytes
= panel
->total
;
1270 ctx
->progress_totals_computed
= FALSE
;
1276 /* --------------------------------------------------------------------------------------------- */
1278 * Generate user prompt for panel operation.
1279 * single_source is the name if the source entry or NULL for multiple
1281 * src_stat is only used when single_source is not NULL.
1285 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1286 gboolean single_source
, const struct stat
*src_stat
)
1288 const char *sp
, *cp
;
1289 char format_string
[BUF_MEDIUM
];
1290 char *dp
= format_string
;
1291 gboolean build_question
= FALSE
;
1293 static gboolean i18n_flag
= FALSE
;
1298 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1299 op_names1
[i
] = Q_ (op_names1
[i
]);
1302 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1303 prompt_parts
[i
] = _(prompt_parts
[i
]);
1305 one_format
= _(one_format
);
1306 many_format
= _(many_format
);
1307 question_format
= _(question_format
);
1308 #endif /* ENABLE_NLS */
1312 sp
= single_source
? one_format
: many_format
;
1323 cp
= op_names1
[operation
];
1326 if (operation
== OP_DELETE
)
1329 build_question
= TRUE
;
1332 cp
= prompt_parts
[5];
1335 if (operation
== OP_DELETE
)
1338 build_question
= TRUE
;
1341 cp
= prompt_parts
[6];
1345 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1347 cp
= (panel
->marked
== panel
->dirs_marked
)
1349 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1370 char tmp
[BUF_MEDIUM
];
1372 memmove (tmp
, format_string
, sizeof (tmp
));
1373 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1376 return g_strdup (format_string
);
1379 /* --------------------------------------------------------------------------------------------- */
1381 #ifdef ENABLE_BACKGROUND
1383 end_bg_process (FileOpContext
* ctx
, enum OperationMode mode
)
1390 unregister_task_with_pid (pid
);
1391 /* file_op_context_destroy(ctx); */
1397 /* --------------------------------------------------------------------------------------------- */
1398 /*** public functions ****************************************************************************/
1399 /* --------------------------------------------------------------------------------------------- */
1402 copy_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
1403 const char *src_path
, const char *dst_path
)
1405 uid_t src_uid
= (uid_t
) - 1;
1406 gid_t src_gid
= (gid_t
) - 1;
1408 int src_desc
, dest_desc
= -1;
1409 int n_read
, n_written
;
1410 mode_t src_mode
= 0; /* The mode of the source file */
1411 struct stat sb
, sb2
;
1413 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1414 off_t file_size
= -1;
1415 FileProgressStatus return_status
, temp_status
;
1416 struct timeval tv_transfer_start
;
1417 dest_status_t dst_status
= DEST_NONE
;
1419 gboolean is_first_time
= TRUE
;
1420 vfs_path_t
*src_vpath
, *dst_vpath
;
1422 /* FIXME: We should not be using global variables! */
1424 return_status
= FILE_RETRY
;
1426 file_progress_show_source (ctx
, src_path
);
1427 file_progress_show_target (ctx
, dst_path
);
1428 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1433 dst_vpath
= vfs_path_from_str (dst_path
);
1434 while (mc_stat (dst_vpath
, &sb2
) == 0)
1436 if (S_ISDIR (sb2
.st_mode
))
1439 return_status
= FILE_SKIPALL
;
1442 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1443 if (return_status
== FILE_SKIPALL
)
1444 ctx
->skip_all
= TRUE
;
1445 if (return_status
== FILE_RETRY
)
1448 return return_status
;
1453 vfs_path_free (dst_vpath
);
1455 src_vpath
= vfs_path_from_str (src_path
);
1456 while ((*ctx
->stat_func
) (src_vpath
, &sb
) != 0)
1459 return_status
= FILE_SKIPALL
;
1462 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1463 if (return_status
== FILE_SKIPALL
)
1464 ctx
->skip_all
= TRUE
;
1466 if (return_status
!= FILE_RETRY
)
1468 vfs_path_free (src_vpath
);
1469 return return_status
;
1472 vfs_path_free (src_vpath
);
1476 /* Destination already exists */
1477 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1478 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), src_path
, dst_path
);
1479 /* Should we replace destination? */
1480 if (tctx
->ask_overwrite
)
1483 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1484 if (return_status
!= FILE_CONT
)
1485 return return_status
;
1489 if (!ctx
->do_append
)
1491 /* Check the hardlinks */
1492 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_path
, dst_path
, &sb
))
1494 /* We have made a hardlink - no more processing is necessary */
1498 if (S_ISLNK (sb
.st_mode
))
1499 return make_symlink (ctx
, src_path
, dst_path
);
1501 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1502 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1504 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0
1507 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1508 if (return_status
== FILE_RETRY
)
1510 if (return_status
== FILE_SKIPALL
)
1511 ctx
->skip_all
= TRUE
;
1512 return return_status
;
1516 while (ctx
->preserve_uidgid
&& mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
) != 0
1519 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1520 if (temp_status
== FILE_SKIP
)
1522 if (temp_status
== FILE_SKIPALL
)
1523 ctx
->skip_all
= TRUE
;
1524 if (temp_status
!= FILE_RETRY
)
1528 while (ctx
->preserve
&& mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
) != 0
1531 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1532 if (temp_status
== FILE_SKIP
)
1534 if (temp_status
== FILE_SKIPALL
)
1535 ctx
->skip_all
= TRUE
;
1536 if (temp_status
!= FILE_RETRY
)
1544 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1546 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1548 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1549 if (return_status
== FILE_RETRY
)
1551 if (return_status
== FILE_SKIPALL
)
1552 ctx
->skip_all
= TRUE
;
1553 if (return_status
== FILE_SKIP
)
1556 return return_status
;
1559 if (ctx
->do_reget
!= 0)
1561 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1563 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1565 ctx
->do_append
= FALSE
;
1569 while (mc_fstat (src_desc
, &sb
) != 0)
1572 return_status
= FILE_SKIPALL
;
1575 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1576 if (return_status
== FILE_RETRY
)
1578 if (return_status
== FILE_SKIPALL
)
1579 ctx
->skip_all
= TRUE
;
1580 ctx
->do_append
= FALSE
;
1584 src_mode
= sb
.st_mode
;
1585 src_uid
= sb
.st_uid
;
1586 src_gid
= sb
.st_gid
;
1587 utb
.actime
= sb
.st_atime
;
1588 utb
.modtime
= sb
.st_mtime
;
1589 file_size
= sb
.st_size
;
1591 open_flags
= O_WRONLY
;
1594 if (ctx
->do_append
!= 0)
1595 open_flags
|= O_APPEND
;
1597 open_flags
|= O_CREAT
| O_TRUNC
;
1601 open_flags
|= O_CREAT
| O_EXCL
;
1604 while ((dest_desc
= mc_open (dst_path
, open_flags
, src_mode
)) < 0)
1606 if (errno
!= EEXIST
)
1609 return_status
= FILE_SKIPALL
;
1612 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1613 if (return_status
== FILE_RETRY
)
1615 if (return_status
== FILE_SKIPALL
)
1616 ctx
->skip_all
= TRUE
;
1617 ctx
->do_append
= FALSE
;
1622 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1624 appending
= ctx
->do_append
;
1625 ctx
->do_append
= FALSE
;
1627 /* Find out the optimal buffer size. */
1628 while (mc_fstat (dest_desc
, &sb
) != 0)
1631 return_status
= FILE_SKIPALL
;
1634 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1635 if (return_status
== FILE_RETRY
)
1637 if (return_status
== FILE_SKIPALL
)
1638 ctx
->skip_all
= TRUE
;
1645 errno
= vfs_preallocate (dest_desc
, file_size
, (ctx
->do_append
!= 0) ? sb
.st_size
: 0);
1650 return_status
= FILE_SKIPALL
;
1654 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1655 if (return_status
== FILE_RETRY
)
1657 if (return_status
== FILE_SKIPALL
)
1658 ctx
->skip_all
= TRUE
;
1660 mc_close (dest_desc
);
1662 mc_unlink (dst_path
);
1663 dst_status
= DEST_NONE
;
1667 ctx
->eta_secs
= 0.0;
1670 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1671 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1673 file_progress_show (ctx
, 1, 1, "", TRUE
);
1674 return_status
= check_progress_buttons (ctx
);
1677 if (return_status
!= FILE_CONT
)
1681 off_t n_read_total
= 0;
1682 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1683 int secs
, update_secs
;
1684 const char *stalled_msg
= "";
1686 tv_last_update
= tv_transfer_start
;
1693 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1696 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0 && !ctx
->skip_all
)
1698 return_status
= file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
1699 if (return_status
== FILE_RETRY
)
1701 if (return_status
== FILE_SKIPALL
)
1702 ctx
->skip_all
= TRUE
;
1708 gettimeofday (&tv_current
, NULL
);
1713 n_read_total
+= n_read
;
1715 /* Windows NT ftp servers report that files have no
1716 * permissions: -------, so if we happen to have actually
1717 * read something, we should fix the permissions.
1719 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1720 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1721 gettimeofday (&tv_last_input
, NULL
);
1724 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
&& !ctx
->skip_all
)
1728 n_read
-= n_written
;
1732 return_status
= file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1733 if (return_status
== FILE_SKIP
)
1735 if (return_status
== FILE_SKIPALL
)
1736 ctx
->skip_all
= TRUE
;
1737 if (return_status
!= FILE_RETRY
)
1742 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1744 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1745 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1747 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1749 copy_file_file_display_progress (tctx
, ctx
,
1751 tv_transfer_start
, file_size
, n_read_total
);
1752 tv_last_update
= tv_current
;
1754 is_first_time
= FALSE
;
1756 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1758 stalled_msg
= _("(stalled)");
1762 gboolean force_update
;
1765 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1767 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1769 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1770 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1773 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1778 return_status
= check_progress_buttons (ctx
);
1780 if (return_status
!= FILE_CONT
)
1788 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1791 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1793 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1794 if (temp_status
== FILE_RETRY
)
1796 if (temp_status
== FILE_ABORT
)
1797 return_status
= temp_status
;
1798 if (temp_status
== FILE_SKIPALL
)
1799 ctx
->skip_all
= TRUE
;
1803 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1805 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1806 if (temp_status
== FILE_RETRY
)
1808 if (temp_status
== FILE_SKIPALL
)
1809 ctx
->skip_all
= TRUE
;
1810 return_status
= temp_status
;
1814 if (dst_status
== DEST_SHORT
)
1816 /* Remove short file */
1818 result
= query_dialog (Q_ ("DialogTitle|Copy"),
1819 _("Incomplete file was retrieved. Keep it?"),
1820 D_ERROR
, 2, _("&Delete"), _("&Keep"));
1822 mc_unlink (dst_path
);
1824 else if (dst_status
== DEST_FULL
)
1826 /* Copy has succeeded */
1827 if (!appending
&& ctx
->preserve_uidgid
)
1829 while (mc_chown (dst_path
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1831 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1832 if (temp_status
== FILE_RETRY
)
1834 if (temp_status
== FILE_SKIPALL
)
1836 ctx
->skip_all
= TRUE
;
1837 return_status
= FILE_CONT
;
1839 if (temp_status
== FILE_SKIP
)
1840 return_status
= FILE_CONT
;
1849 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1851 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1852 if (temp_status
== FILE_RETRY
)
1854 if (temp_status
== FILE_SKIPALL
)
1856 ctx
->skip_all
= TRUE
;
1857 return_status
= FILE_CONT
;
1859 if (temp_status
== FILE_SKIP
)
1860 return_status
= FILE_CONT
;
1864 else if (!dst_exists
)
1866 src_mode
= umask (-1);
1868 src_mode
= 0100666 & ~src_mode
;
1869 mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
));
1871 mc_utime (dst_path
, &utb
);
1875 if (return_status
== FILE_CONT
)
1876 return_status
= progress_update_one (tctx
, ctx
, file_size
);
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
;
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
, s
, &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 (s
, d
, &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
, s
, &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_new (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 (s
, d
) == 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
= concat_dir_and_file (d
, x_basename (s
));
2027 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
))
2030 return_status
= FILE_SKIPALL
;
2033 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir
);
2034 if (return_status
== FILE_SKIPALL
)
2035 ctx
->skip_all
= TRUE
;
2037 if (return_status
!= FILE_RETRY
)
2041 lp
= g_new (struct link
, 1);
2043 vfs_path_t
*tmp_vpath
= vfs_path_from_str (dest_dir
);
2044 mc_stat (tmp_vpath
, &buf
);
2046 lp
->vfs
= vfs_path_get_by_index (tmp_vpath
, -1)->class;
2047 vfs_path_free (tmp_vpath
);
2049 lp
->ino
= buf
.st_ino
;
2050 lp
->dev
= buf
.st_dev
;
2051 lp
->next
= dest_dirs
;
2054 if (ctx
->preserve_uidgid
)
2056 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2059 return_status
= FILE_SKIPALL
;
2063 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir
);
2064 if (return_status
== FILE_SKIPALL
)
2065 ctx
->skip_all
= TRUE
;
2067 if (return_status
!= FILE_RETRY
)
2073 /* open the source dir for reading */
2074 reading
= mc_opendir (src_vpath
);
2075 if (reading
== NULL
)
2078 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2081 vfs_path_t
*tmp_vpath
;
2083 * Now, we don't want '.' and '..' to be created / copied at any time
2085 if (!strcmp (next
->d_name
, "."))
2087 if (!strcmp (next
->d_name
, ".."))
2090 /* get the filename and add it to the src directory */
2091 path
= concat_dir_and_file (s
, next
->d_name
);
2092 tmp_vpath
= vfs_path_from_str (path
);
2094 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2095 if (S_ISDIR (buf
.st_mode
))
2099 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
2101 * From here, we just intend to recursively copy subdirs, not
2102 * the double functionality of copying different when the target
2103 * dir already exists. So, we give the recursive call the flag 0
2104 * meaning no toplevel.
2107 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2114 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
2115 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2118 if (do_delete
&& return_status
== FILE_CONT
)
2120 if (ctx
->erase_at_end
)
2122 static struct link
*tail
;
2123 size_t len
= strlen (path
);
2124 lp
= g_malloc (sizeof (struct link
) + len
);
2125 strncpy (lp
->name
, path
, len
+ 1);
2126 lp
->st_mode
= buf
.st_mode
;
2128 if (erase_list
!= NULL
)
2134 erase_list
= tail
= lp
;
2138 if (S_ISDIR (buf
.st_mode
))
2140 return_status
= erase_dir_iff_empty (ctx
, path
);
2143 return_status
= erase_file (tctx
, ctx
, path
);
2147 vfs_path_free (tmp_vpath
);
2149 mc_closedir (reading
);
2153 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
2154 utb
.actime
= cbuf
.st_atime
;
2155 utb
.modtime
= cbuf
.st_mtime
;
2156 mc_utime (dest_dir
, &utb
);
2160 cbuf
.st_mode
= umask (-1);
2161 umask (cbuf
.st_mode
);
2162 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2163 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
2168 g_free (parent_dirs
);
2171 vfs_path_free (src_vpath
);
2172 vfs_path_free (dst_vpath
);
2173 return return_status
;
2178 /* --------------------------------------------------------------------------------------------- */
2179 /* {{{ Move routines */
2182 move_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
2184 struct stat sbuf
, dbuf
, destbuf
;
2187 FileProgressStatus return_status
;
2188 gboolean move_over
= FALSE
;
2190 vfs_path_t
*src_vpath
, *dst_vpath
, *destdir_vpath
;
2192 src_vpath
= vfs_path_from_str (s
);
2193 dst_vpath
= vfs_path_from_str (d
);
2195 file_progress_show_source (ctx
, s
);
2196 file_progress_show_target (ctx
, d
);
2197 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2202 mc_stat (src_vpath
, &sbuf
);
2204 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2206 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2207 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2210 destdir
= g_strdup (d
); /* destination doesn't exist */
2211 else if (!ctx
->dive_into_subdirs
)
2213 destdir
= g_strdup (d
);
2217 destdir
= concat_dir_and_file (d
, x_basename (s
));
2219 destdir_vpath
= vfs_path_from_str (destdir
);
2221 /* Check if the user inputted an existing dir */
2223 if (mc_stat (destdir_vpath
, &destbuf
) == 0)
2227 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
2229 if (return_status
!= FILE_CONT
)
2233 else if (ctx
->skip_all
)
2234 return_status
= FILE_SKIPALL
;
2237 if (S_ISDIR (destbuf
.st_mode
))
2238 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir
);
2240 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir
);
2241 if (return_status
== FILE_SKIPALL
)
2242 ctx
->skip_all
= TRUE
;
2243 if (return_status
== FILE_RETRY
)
2244 goto retry_dst_stat
;
2247 vfs_path_free (destdir_vpath
);
2248 vfs_path_free (src_vpath
);
2249 vfs_path_free (dst_vpath
);
2250 return return_status
;
2254 if (mc_rename (s
, destdir
) == 0)
2256 return_status
= FILE_CONT
;
2264 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2265 if (return_status
== FILE_SKIPALL
)
2266 ctx
->skip_all
= TRUE
;
2267 if (return_status
== FILE_RETRY
)
2272 /* Failed because of filesystem boundary -> copy dir instead */
2273 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
2275 if (return_status
!= FILE_CONT
)
2278 file_progress_show_source (ctx
, NULL
);
2279 file_progress_show (ctx
, 0, 0, "", FALSE
);
2281 return_status
= check_progress_buttons (ctx
);
2282 if (return_status
!= FILE_CONT
)
2286 if (ctx
->erase_at_end
)
2288 for (; erase_list
&& return_status
!= FILE_ABORT
;)
2290 if (S_ISDIR (erase_list
->st_mode
))
2292 return_status
= erase_dir_iff_empty (ctx
, erase_list
->name
);
2295 return_status
= erase_file (tctx
, ctx
, erase_list
->name
);
2297 erase_list
= erase_list
->next
;
2301 erase_dir_iff_empty (ctx
, s
);
2305 vfs_path_free (destdir_vpath
);
2309 erase_list
= erase_list
->next
;
2312 vfs_path_free (src_vpath
);
2313 vfs_path_free (dst_vpath
);
2314 return return_status
;
2319 /* --------------------------------------------------------------------------------------------- */
2320 /* {{{ Erase routines */
2323 erase_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
2325 FileProgressStatus error
;
2327 if (strcmp (s
, "..") == 0)
2330 if (strcmp (s
, ".") == 0)
2333 file_progress_show_deleting (ctx
, s
);
2334 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2338 /* The old way to detect a non empty directory was:
2339 error = my_rmdir (s);
2340 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2341 For the linux user space nfs server (nfs-server-2.2beta29-2)
2342 we would have to check also for EIO. I hope the new way is
2343 fool proof. (Norbert)
2345 error
= check_dir_is_empty (s
);
2348 error
= query_recursive (ctx
, s
);
2349 if (error
== FILE_CONT
)
2350 return recursive_erase (tctx
, ctx
, s
);
2355 while (my_rmdir (s
) == -1 && !ctx
->skip_all
)
2357 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
2358 if (error
!= FILE_RETRY
)
2367 /* --------------------------------------------------------------------------------------------- */
2368 /* {{{ Panel operate routines */
2371 compute_dir_size_create_ui (void)
2373 ComputeDirSizeUI
*ui
;
2375 const char *b_name
= N_("&Abort");
2381 ui
= g_new (ComputeDirSizeUI
, 1);
2383 ui
->dlg
= create_dlg (TRUE
, 0, 0, 8, COLS
/ 2, dialog_colors
, NULL
,
2384 NULL
, _("Directory scanning"), DLG_CENTER
);
2385 ui
->dirname
= label_new (3, 3, "");
2386 add_widget (ui
->dlg
, ui
->dirname
);
2388 add_widget (ui
->dlg
,
2389 button_new (5, (ui
->dlg
->cols
- strlen (b_name
)) / 2,
2390 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
2392 /* We will manage the dialog without any help,
2393 that's why we have to call init_dlg */
2399 /* --------------------------------------------------------------------------------------------- */
2402 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
2406 /* schedule to update passive panel */
2407 other_panel
->dirty
= 1;
2409 /* close and destroy dialog */
2410 dlg_run_done (ui
->dlg
);
2411 destroy_dlg (ui
->dlg
);
2416 /* --------------------------------------------------------------------------------------------- */
2419 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
2421 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
2428 label_set_text (this->dirname
, str_trunc (dirname
, this->dlg
->cols
- 6));
2430 event
.x
= -1; /* Don't show the GPM cursor */
2431 c
= tty_get_event (&event
, FALSE
, FALSE
);
2435 /* Reinitialize to avoid old values after events other than
2436 selecting a button */
2437 this->dlg
->ret_value
= FILE_CONT
;
2439 dlg_process_event (this->dlg
, c
, &event
);
2441 switch (this->dlg
->ret_value
)
2451 /* --------------------------------------------------------------------------------------------- */
2455 * Computes the number of bytes used by the files in a directory
2459 compute_dir_size (const char *dirname
, const void *ui
,
2460 compute_dir_size_callback cback
,
2461 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
2466 struct dirent
*dirent
;
2467 FileProgressStatus ret
= FILE_CONT
;
2470 vpath
= vfs_path_from_str (dirname
);
2472 if (!compute_symlinks
)
2474 res
= mc_lstat (vpath
, &s
);
2478 /* don't scan symlink to directory */
2479 if (S_ISLNK (s
.st_mode
))
2482 *ret_total
+= (uintmax_t) s
.st_size
;
2487 dir
= mc_opendir (vpath
);
2492 while ((dirent
= mc_readdir (dir
)) != NULL
)
2495 vfs_path_t
*tmp_vpath
;
2497 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
2499 if (ret
!= FILE_CONT
)
2502 if (strcmp (dirent
->d_name
, ".") == 0)
2504 if (strcmp (dirent
->d_name
, "..") == 0)
2507 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
2508 tmp_vpath
= vfs_path_from_str (fullname
);
2509 res
= mc_lstat (tmp_vpath
, &s
);
2514 vfs_path_free (tmp_vpath
);
2518 if (S_ISDIR (s
.st_mode
))
2520 size_t subdir_count
= 0;
2521 uintmax_t subdir_bytes
= 0;
2524 compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
,
2527 if (ret
!= FILE_CONT
)
2530 vfs_path_free (tmp_vpath
);
2534 *ret_marked
+= subdir_count
;
2535 *ret_total
+= subdir_bytes
;
2540 *ret_total
+= (uintmax_t) s
.st_size
;
2544 vfs_path_free (tmp_vpath
);
2549 vfs_path_free (vpath
);
2553 /* --------------------------------------------------------------------------------------------- */
2557 * Performs one of the operations on the selection on the source_panel
2558 * (copy, delete, move).
2560 * Returns TRUE if did change the directory
2561 * structure, Returns FALSE if user aborted
2563 * force_single forces operation on the current entry and affects
2564 * default destination. Current filename is used as default.
2568 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2570 WPanel
*panel
= (WPanel
*) source_panel
;
2571 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2572 || (get_current_type () == view_tree
);
2574 char *source
= NULL
;
2575 #ifdef WITH_FULL_PATHS
2576 char *source_with_path
= NULL
;
2578 #define source_with_path source
2579 #endif /* !WITH_FULL_PATHS */
2582 char *save_cwd
= NULL
, *save_dest
= NULL
;
2583 struct stat src_stat
;
2584 gboolean ret_val
= TRUE
;
2586 FileProgressStatus value
;
2588 FileOpTotalContext
*tctx
;
2590 gboolean do_bg
= FALSE
; /* do background operation? */
2592 static gboolean i18n_flag
= FALSE
;
2595 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
2596 op_names
[i
] = Q_ (op_names
[i
]);
2600 free_linklist (&linklist
);
2601 free_linklist (&dest_dirs
);
2605 vfs_path_t
*source_vpath
;
2608 source
= selection (panel
)->fname
;
2610 source
= panel_get_file (panel
);
2612 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_lstat (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
)
2651 /* Forced single operations default to the original name */
2654 else if (get_other_type () == view_listing
)
2655 dest_dir
= other_panel
->cwd
;
2657 dest_dir
= panel
->cwd
;
2659 * Add trailing backslash only when do non-local ops.
2660 * It saves user from occasional file renames (when destination
2663 if (!force_single
&& dest_dir
[0] != '\0' && dest_dir
[strlen (dest_dir
) - 1] != PATH_SEP
)
2665 /* add trailing separator */
2666 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2671 dest_dir_
= g_strdup (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')
2697 else if (confirm_delete
)
2700 char fmd_buf
[BUF_MEDIUM
];
2702 /* Generate confirmation prompt */
2703 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2706 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2709 const int fmd_xlen
= 64;
2710 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2711 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2719 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2728 tctx
= file_op_total_context_new ();
2729 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2732 filegui_dialog_type_t dialog_type
;
2734 if (operation
== OP_DELETE
)
2735 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2738 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2739 ? FILEGUI_DIALOG_MULTI_ITEM
: FILEGUI_DIALOG_ONE_ITEM
;
2741 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2742 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2745 /* Background also need ctx->ui, but not full */
2747 file_op_context_create_ui_without_init (ctx
, TRUE
, dialog_type
);
2749 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
2752 #ifdef ENABLE_BACKGROUND
2753 /* Did the user select to do a background operation? */
2758 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", panel
->cwd
, (char *) NULL
));
2760 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2762 /* If we are the parent */
2765 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
2766 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
2767 /* file_op_context_destroy (ctx); */
2771 #endif /* ENABLE_BACKGROUND */
2773 /* Initialize things */
2774 /* We do not want to trash cache every time file is
2775 created/touched. However, this will make our cache contain
2777 if ((dest
!= NULL
) && (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2778 save_dest
= g_strdup (dest
);
2780 if ((panel
->cwd
[0] != '\0') && (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2781 save_cwd
= g_strdup (panel
->cwd
);
2783 /* Now, let's do the job */
2785 /* This code is only called by the tree and panel code */
2788 /* We now have ETA in all cases */
2790 /* One file: FIXME mc_chdir will take user out of any vfs */
2791 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2796 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2797 chdir_retcode
= mc_chdir (vpath
);
2798 vfs_path_free (vpath
);
2799 if (chdir_retcode
< 0)
2806 /* The source and src_stat variables have been initialized before */
2807 #ifdef WITH_FULL_PATHS
2808 if (g_path_is_absolute (source
))
2809 source_with_path
= g_strdup (source
);
2811 source_with_path
= mc_build_filename (panel
->cwd
, source
, (char *) NULL
);
2812 #endif /* WITH_FULL_PATHS */
2814 if (panel_operate_init_totals (operation
, panel
, source_with_path
, ctx
) == FILE_CONT
)
2816 if (operation
== OP_DELETE
)
2818 if (S_ISDIR (src_stat
.st_mode
))
2819 value
= erase_dir (tctx
, ctx
, source_with_path
);
2821 value
= erase_file (tctx
, ctx
, source_with_path
);
2825 temp
= transform_source (ctx
, source_with_path
);
2827 value
= transform_error
;
2830 char *repl_dest
, *temp2
;
2832 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2833 temp2
= concat_dir_and_file (repl_dest
, temp
);
2842 /* we use file_mask_op_follow_links only with OP_COPY */
2844 vfs_path_t
*vpath
= vfs_path_from_str (source_with_path
);
2845 ctx
->stat_func (vpath
, &src_stat
);
2846 vfs_path_free (vpath
);
2849 if (S_ISDIR (src_stat
.st_mode
))
2850 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, dest
,
2851 TRUE
, FALSE
, FALSE
, NULL
);
2853 value
= copy_file_file (tctx
, ctx
, source_with_path
, dest
);
2857 if (S_ISDIR (src_stat
.st_mode
))
2858 value
= move_dir_dir (tctx
, ctx
, source_with_path
, dest
);
2860 value
= move_file_file (tctx
, ctx
, source_with_path
, dest
);
2864 /* Unknown file operation */
2868 } /* Copy or move operation */
2870 if ((value
== FILE_CONT
) && !force_single
)
2871 unmark_files (panel
);
2878 /* Check destination for copy or move operation */
2879 while (operation
!= OP_DELETE
)
2882 struct stat dst_stat
;
2883 vfs_path_t
*vpath
= vfs_path_from_str (dest
);
2885 dst_result
= mc_stat (vpath
, &dst_stat
);
2886 vfs_path_free (vpath
);
2888 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2892 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2896 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
)
2898 /* Loop for every file, perform the actual copy operation */
2899 for (i
= 0; i
< panel
->count
; i
++)
2901 if (!panel
->dir
.list
[i
].f
.marked
)
2902 continue; /* Skip the unmarked ones */
2904 source
= panel
->dir
.list
[i
].fname
;
2905 src_stat
= panel
->dir
.list
[i
].st
;
2907 #ifdef WITH_FULL_PATHS
2908 g_free (source_with_path
);
2909 if (g_path_is_absolute (source
))
2910 source_with_path
= g_strdup (source
);
2912 source_with_path
= mc_build_filename (panel
->cwd
, source
, (char *) NULL
);
2913 #endif /* WITH_FULL_PATHS */
2915 if (operation
== OP_DELETE
)
2917 if (S_ISDIR (src_stat
.st_mode
))
2918 value
= erase_dir (tctx
, ctx
, source_with_path
);
2920 value
= erase_file (tctx
, ctx
, source_with_path
);
2924 temp
= transform_source (ctx
, source_with_path
);
2927 value
= transform_error
;
2930 char *temp2
, *temp3
, *repl_dest
;
2932 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2933 temp2
= concat_dir_and_file (repl_dest
, temp
);
2936 temp3
= source_with_path
;
2937 source_with_path
= strutils_shell_unescape (source_with_path
);
2940 temp2
= strutils_shell_unescape (temp2
);
2946 /* we use file_mask_op_follow_links only with OP_COPY */
2950 vpath
= vfs_path_from_str (source_with_path
);
2951 ctx
->stat_func (vpath
, &src_stat
);
2952 vfs_path_free (vpath
);
2954 if (S_ISDIR (src_stat
.st_mode
))
2955 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, temp2
,
2956 TRUE
, FALSE
, FALSE
, NULL
);
2958 value
= copy_file_file (tctx
, ctx
, source_with_path
, temp2
);
2959 free_linklist (&dest_dirs
);
2963 if (S_ISDIR (src_stat
.st_mode
))
2964 value
= move_dir_dir (tctx
, ctx
, source_with_path
, temp2
);
2966 value
= move_file_file (tctx
, ctx
, source_with_path
, temp2
);
2970 /* Unknown file operation */
2976 } /* Copy or move operation */
2978 if (value
== FILE_ABORT
)
2981 if (value
== FILE_CONT
)
2982 do_file_mark (panel
, i
, 0);
2984 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
2986 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2987 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
2990 if (operation
!= OP_DELETE
)
2991 file_progress_show (ctx
, 0, 0, "", FALSE
);
2993 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2997 } /* Loop for every file */
2999 } /* Many entries */
3003 if (save_cwd
!= NULL
)
3005 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
3009 if (save_dest
!= NULL
)
3011 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
3015 free_linklist (&linklist
);
3016 free_linklist (&dest_dirs
);
3017 #ifdef WITH_FULL_PATHS
3018 g_free (source_with_path
);
3019 #endif /* WITH_FULL_PATHS */
3021 g_free (ctx
->dest_mask
);
3022 ctx
->dest_mask
= NULL
;
3024 #ifdef ENABLE_BACKGROUND
3025 /* Let our parent know we are saying bye bye */
3026 if (mc_global
.we_are_background
)
3028 int cur_pid
= getpid ();
3029 /* Send pid to parent with child context, it is fork and
3030 don't modify real parent ctx */
3032 parent_call ((void *) end_bg_process
, ctx
, 0);
3037 #endif /* ENABLE_BACKGROUND */
3039 file_op_total_context_destroy (tctx
);
3041 file_op_context_destroy (ctx
);
3048 /* --------------------------------------------------------------------------------------------- */
3049 /* {{{ Query/status report routines */
3050 /** Report error with one file */
3052 file_error (const char *format
, const char *file
)
3054 char buf
[BUF_MEDIUM
];
3056 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3058 return do_file_error (buf
);
3061 /* --------------------------------------------------------------------------------------------- */
3064 Cause emacs to enter folding mode for this file: