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
;
299 vpath
= vfs_path_from_str (lp
->name
);
300 lp_name_class
= vfs_path_get_by_index (vpath
, -1)->class;
301 vfs_path_free (vpath
);
303 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
304 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
306 struct vfs_class
*p_class
, *dst_name_class
;
308 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
311 vpath
= vfs_path_from_str (p
);
312 p_class
= vfs_path_get_by_index (vpath
, -1)->class;
313 vfs_path_free (vpath
);
315 vpath
= vfs_path_from_str (dst_name
);
316 dst_name_class
= vfs_path_get_by_index (vpath
, -1)->class;
317 vfs_path_free (vpath
);
319 if (dst_name_class
== p_class
)
321 if (!mc_stat (p
, &link_stat
))
323 if (!mc_link (p
, dst_name
))
328 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
331 lp
= (struct link
*) g_try_malloc (sizeof (struct link
) + strlen (src_name
)
332 + strlen (dst_name
) + 1);
339 strcpy (lp
->name
, src_name
);
340 lpdstname
= lp
->name
+ strlen (lp
->name
) + 1;
341 strcpy (lpdstname
, dst_name
);
348 /* --------------------------------------------------------------------------------------------- */
350 * Duplicate the contents of the symbolic link src_path in dst_path.
351 * Try to make a stable symlink if the option "stable symlink" was
352 * set in the file mask dialog.
353 * If dst_path is an existing symlink it will be deleted silently
354 * (upper levels take already care of existing files at dst_path).
357 static FileProgressStatus
358 make_symlink (FileOpContext
* ctx
, const char *src_path
, const char *dst_path
)
360 char link_target
[MC_MAXPATHLEN
];
362 FileProgressStatus return_status
;
364 gboolean dst_is_symlink
;
366 dst_is_symlink
= (mc_lstat (dst_path
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
369 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
373 return_status
= FILE_SKIPALL
;
376 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
377 if (return_status
== FILE_SKIPALL
)
378 ctx
->skip_all
= TRUE
;
379 if (return_status
== FILE_RETRY
)
380 goto retry_src_readlink
;
382 return return_status
;
384 link_target
[len
] = 0;
386 if (ctx
->stable_symlinks
)
388 vfs_path_t
*vpath1
= vfs_path_from_str (src_path
);
389 vfs_path_t
*vpath2
= vfs_path_from_str (dst_path
);
391 if (!vfs_file_is_local (vpath1
) || !vfs_file_is_local (vpath2
))
393 message (D_ERROR
, MSG_ERROR
,
394 _("Cannot make stable symlinks across"
395 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
396 ctx
->stable_symlinks
= FALSE
;
398 vfs_path_free (vpath1
);
399 vfs_path_free (vpath2
);
402 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
406 const char *r
= strrchr (src_path
, PATH_SEP
);
410 p
= g_strndup (src_path
, r
- src_path
+ 1);
411 if (g_path_is_absolute (dst_path
))
412 q
= g_strdup (dst_path
);
414 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
415 s
= strrchr (q
, PATH_SEP
);
419 s
= g_strconcat (p
, link_target
, (char *) NULL
);
421 g_strlcpy (link_target
, s
, sizeof (link_target
));
423 s
= diff_two_paths (q
, link_target
);
426 g_strlcpy (link_target
, s
, sizeof (link_target
));
436 if (mc_symlink (link_target
, dst_path
) == 0)
440 * if dst_exists, it is obvious that this had failed.
441 * We can delete the old symlink and try again...
445 if (!mc_unlink (dst_path
))
446 if (mc_symlink (link_target
, dst_path
) == 0)
451 return_status
= FILE_SKIPALL
;
454 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
455 if (return_status
== FILE_SKIPALL
)
456 ctx
->skip_all
= TRUE
;
457 if (return_status
== FILE_RETRY
)
458 goto retry_dst_symlink
;
460 return return_status
;
463 /* --------------------------------------------------------------------------------------------- */
465 static FileProgressStatus
466 progress_update_one (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, off_t add
)
468 struct timeval tv_current
;
469 static struct timeval tv_start
= { };
471 tctx
->progress_count
++;
472 tctx
->progress_bytes
+= (uintmax_t) add
;
474 if (tv_start
.tv_sec
== 0)
476 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
478 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
479 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
481 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
483 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
484 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
486 tv_start
.tv_sec
= tv_current
.tv_sec
;
489 return check_progress_buttons (ctx
);
492 /* --------------------------------------------------------------------------------------------- */
494 static FileProgressStatus
495 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
499 const char *head_msg
;
501 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
503 msg
= g_strdup_printf (fmt
, a
, b
);
504 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
508 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
511 /* --------------------------------------------------------------------------------------------- */
513 static FileProgressStatus
514 warn_same_file (const char *fmt
, const char *a
, const char *b
)
516 #ifdef ENABLE_BACKGROUND
521 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
522 const char *a
, const char *b
);
526 pntr
.f
= real_warn_same_file
;
528 if (mc_global
.we_are_background
)
529 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
531 return real_warn_same_file (Foreground
, fmt
, a
, b
);
534 /* --------------------------------------------------------------------------------------------- */
535 /* {{{ Query/status report routines */
537 static FileProgressStatus
538 real_do_file_error (enum OperationMode mode
, const char *error
)
543 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
545 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
567 /* --------------------------------------------------------------------------------------------- */
569 static FileProgressStatus
570 real_query_recursive (FileOpContext
* ctx
, enum OperationMode mode
, const char *s
)
574 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
576 const char *msg
= mode
== Foreground
577 ? _("\nDirectory not empty.\nDelete it recursively?")
578 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
579 text
= g_strconcat (_("Delete:"), " ", path_trunc (s
, 30), (char *) NULL
);
584 ctx
->recursive_result
=
585 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
586 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
588 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
593 switch (ctx
->recursive_result
)
596 case RECURSIVE_ALWAYS
:
600 case RECURSIVE_NEVER
:
603 case RECURSIVE_ABORT
:
609 /* --------------------------------------------------------------------------------------------- */
611 #ifdef ENABLE_BACKGROUND
612 static FileProgressStatus
613 do_file_error (const char *str
)
618 FileProgressStatus (*f
) (enum OperationMode
, const char *);
620 pntr
.f
= real_do_file_error
;
622 if (mc_global
.we_are_background
)
623 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
625 return real_do_file_error (Foreground
, str
);
628 /* --------------------------------------------------------------------------------------------- */
630 static FileProgressStatus
631 query_recursive (FileOpContext
* ctx
, const char *s
)
636 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
638 pntr
.f
= real_query_recursive
;
640 if (mc_global
.we_are_background
)
641 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
643 return real_query_recursive (ctx
, Foreground
, s
);
646 /* --------------------------------------------------------------------------------------------- */
648 static FileProgressStatus
649 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
650 struct stat
*_d_stat
)
655 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
656 struct stat
*, struct stat
*);
658 pntr
.f
= file_progress_real_query_replace
;
660 if (mc_global
.we_are_background
)
661 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
662 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
664 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
668 /* --------------------------------------------------------------------------------------------- */
670 static FileProgressStatus
671 do_file_error (const char *str
)
673 return real_do_file_error (Foreground
, str
);
676 /* --------------------------------------------------------------------------------------------- */
678 static FileProgressStatus
679 query_recursive (FileOpContext
* ctx
, const char *s
)
681 return real_query_recursive (ctx
, Foreground
, s
);
684 /* --------------------------------------------------------------------------------------------- */
686 static FileProgressStatus
687 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
688 struct stat
*_d_stat
)
690 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
693 #endif /* !ENABLE_BACKGROUND */
695 /* --------------------------------------------------------------------------------------------- */
696 /** Report error with two files */
698 static FileProgressStatus
699 files_error (const char *format
, const char *file1
, const char *file2
)
701 char buf
[BUF_MEDIUM
];
702 char *nfile1
= g_strdup (path_trunc (file1
, 15));
703 char *nfile2
= g_strdup (path_trunc (file2
, 15));
705 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
710 return do_file_error (buf
);
715 /* --------------------------------------------------------------------------------------------- */
718 copy_file_file_display_progress (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
719 struct timeval tv_current
, struct timeval tv_transfer_start
,
720 off_t file_size
, off_t n_read_total
)
724 /* 1. Update rotating dash after some time */
728 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
730 if (n_read_total
== 0)
734 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
735 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
738 /* 4. Compute BPS rate */
739 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
740 if (ctx
->bps_time
< 1)
742 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
744 /* 5. Compute total ETA and BPS */
745 if (ctx
->progress_bytes
!= 0)
747 uintmax_t remain_bytes
;
749 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
752 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
757 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
758 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
761 /* broken on lot of little files */
763 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
764 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
769 /* --------------------------------------------------------------------------------------------- */
771 /* {{{ Move routines */
772 static FileProgressStatus
773 move_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
775 struct stat src_stats
, dst_stats
;
776 FileProgressStatus return_status
= FILE_CONT
;
777 gboolean copy_done
= FALSE
;
778 gboolean old_ask_overwrite
;
780 file_progress_show_source (ctx
, s
);
781 file_progress_show_target (ctx
, d
);
782 if (check_progress_buttons (ctx
) == FILE_ABORT
)
787 while (mc_lstat (s
, &src_stats
) != 0)
789 /* Source doesn't exist */
791 return_status
= FILE_SKIPALL
;
794 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
795 if (return_status
== FILE_SKIPALL
)
796 ctx
->skip_all
= TRUE
;
798 if (return_status
!= FILE_RETRY
)
799 return return_status
;
802 if (mc_lstat (d
, &dst_stats
) == 0)
804 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
805 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
807 if (S_ISDIR (dst_stats
.st_mode
))
809 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
814 if (confirm_overwrite
)
816 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
817 if (return_status
!= FILE_CONT
)
818 return return_status
;
820 /* Ok to overwrite */
825 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
827 return_status
= make_symlink (ctx
, s
, d
);
828 if (return_status
== FILE_CONT
)
829 goto retry_src_remove
;
831 return return_status
;
834 if (mc_rename (s
, d
) == 0)
835 return progress_update_one (tctx
, ctx
, src_stats
.st_size
);
838 /* Comparison to EXDEV seems not to work in nfs if you're moving from
839 one nfs to the same, but on the server it is on two different
840 filesystems. Then nfs returns EIO instead of EXDEV.
841 Hope it will not hurt if we always in case of error try to copy/delete. */
843 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
848 return_status
= FILE_SKIPALL
;
851 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
852 if (return_status
== FILE_SKIPALL
)
853 ctx
->skip_all
= TRUE
;
854 if (return_status
== FILE_RETRY
)
857 return return_status
;
861 /* Failed because filesystem boundary -> copy the file instead */
862 old_ask_overwrite
= tctx
->ask_overwrite
;
863 tctx
->ask_overwrite
= FALSE
;
864 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
865 tctx
->ask_overwrite
= old_ask_overwrite
;
866 if (return_status
!= FILE_CONT
)
867 return return_status
;
871 file_progress_show_source (ctx
, NULL
);
872 file_progress_show (ctx
, 0, 0, "", FALSE
);
874 return_status
= check_progress_buttons (ctx
);
875 if (return_status
!= FILE_CONT
)
876 return return_status
;
881 if (mc_unlink (s
) != 0 && !ctx
->skip_all
)
883 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
884 if (return_status
== FILE_RETRY
)
885 goto retry_src_remove
;
886 if (return_status
== FILE_SKIPALL
)
887 ctx
->skip_all
= TRUE
;
888 return return_status
;
892 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
894 return return_status
;
899 /* --------------------------------------------------------------------------------------------- */
900 /* {{{ Erase routines */
901 /** Don't update progress status if progress_count==NULL */
903 static FileProgressStatus
904 erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
909 file_progress_show_deleting (ctx
, s
);
910 if (check_progress_buttons (ctx
) == FILE_ABORT
)
914 if (tctx
->progress_count
!= 0 && mc_lstat (s
, &buf
) != 0)
916 /* ignore, most likely the mc_unlink fails, too */
920 while (mc_unlink (s
) != 0 && !ctx
->skip_all
)
922 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), s
);
923 if (return_status
== FILE_ABORT
)
924 return return_status
;
925 if (return_status
== FILE_RETRY
)
927 if (return_status
== FILE_SKIPALL
)
928 ctx
->skip_all
= TRUE
;
932 if (tctx
->progress_count
== 0)
934 return progress_update_one (tctx
, ctx
, buf
.st_size
);
937 /* --------------------------------------------------------------------------------------------- */
940 Recursive remove of files
942 skip ->warn every level, gets default
943 skipall->remove as much as possible
945 static FileProgressStatus
946 recursive_erase (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
952 FileProgressStatus return_status
= FILE_CONT
;
954 if (!strcmp (s
, ".."))
957 reading
= mc_opendir (s
);
962 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
964 if (!strcmp (next
->d_name
, "."))
966 if (!strcmp (next
->d_name
, ".."))
968 path
= concat_dir_and_file (s
, next
->d_name
);
969 if (mc_lstat (path
, &buf
))
972 mc_closedir (reading
);
975 if (S_ISDIR (buf
.st_mode
))
976 return_status
= recursive_erase (tctx
, ctx
, path
);
978 return_status
= erase_file (tctx
, ctx
, path
);
981 mc_closedir (reading
);
982 if (return_status
== FILE_ABORT
)
983 return return_status
;
984 file_progress_show_deleting (ctx
, s
);
985 if (check_progress_buttons (ctx
) == FILE_ABORT
)
989 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
991 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
992 if (return_status
== FILE_RETRY
)
994 if (return_status
== FILE_ABORT
)
995 return return_status
;
996 if (return_status
== FILE_SKIPALL
)
997 ctx
->skip_all
= TRUE
;
1004 /* --------------------------------------------------------------------------------------------- */
1005 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1006 in the directory path points to, 0 else. */
1009 check_dir_is_empty (const char *path
)
1015 dir
= mc_opendir (path
);
1019 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
))
1021 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1022 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1023 continue; /* "." or ".." */
1032 /* --------------------------------------------------------------------------------------------- */
1034 static FileProgressStatus
1035 erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
)
1037 FileProgressStatus error
;
1039 if (strcmp (s
, "..") == 0)
1042 if (strcmp (s
, ".") == 0)
1045 file_progress_show_deleting (ctx
, s
);
1046 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1050 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1053 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1055 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1056 if (error
== FILE_SKIPALL
)
1057 ctx
->skip_all
= TRUE
;
1058 if (error
!= FILE_RETRY
)
1067 /* --------------------------------------------------------------------------------------------- */
1068 /* {{{ Panel operate routines */
1071 * Return currently selected entry name or the name of the first marked
1072 * entry if there is one.
1076 panel_get_file (WPanel
* panel
)
1078 if (get_current_type () == view_tree
)
1082 tree
= (WTree
*) get_panel_widget (get_current_index ());
1083 return tree_selected_name (tree
);
1086 if (panel
->marked
!= 0)
1090 for (i
= 0; i
< panel
->count
; i
++)
1091 if (panel
->dir
.list
[i
].f
.marked
)
1092 return panel
->dir
.list
[i
].fname
;
1095 return panel
->dir
.list
[panel
->selected
].fname
;
1098 /* --------------------------------------------------------------------------------------------- */
1100 * panel_compute_totals:
1102 * compute the number of files and the number of bytes
1103 * used up by the whole selection, recursing directories
1104 * as required. In addition, it checks to see if it will
1105 * overwrite any files by doing the copy.
1108 static FileProgressStatus
1109 panel_compute_totals (const WPanel
* panel
, const void *ui
,
1110 compute_dir_size_callback cback
,
1111 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
1118 for (i
= 0; i
< panel
->count
; i
++)
1122 if (!panel
->dir
.list
[i
].f
.marked
)
1125 s
= &panel
->dir
.list
[i
].st
;
1127 if (S_ISDIR (s
->st_mode
))
1130 size_t subdir_count
= 0;
1131 uintmax_t subdir_bytes
= 0;
1132 FileProgressStatus status
;
1134 dir_name
= concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1136 status
= compute_dir_size (dir_name
, ui
, cback
,
1137 &subdir_count
, &subdir_bytes
, compute_symlinks
);
1140 if (status
!= FILE_CONT
)
1143 *ret_marked
+= subdir_count
;
1144 *ret_total
+= subdir_bytes
;
1149 *ret_total
+= (uintmax_t) s
->st_size
;
1156 /* --------------------------------------------------------------------------------------------- */
1158 /** Initialize variables for progress bars */
1159 static FileProgressStatus
1160 panel_operate_init_totals (FileOperation operation
,
1161 const WPanel
* panel
, const char *source
, FileOpContext
* ctx
)
1163 FileProgressStatus status
;
1165 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
)
1167 ComputeDirSizeUI
*ui
;
1169 ui
= compute_dir_size_create_ui ();
1172 status
= compute_dir_size (source
, ui
, compute_dir_size_update_ui
,
1173 &ctx
->progress_count
, &ctx
->progress_bytes
,
1176 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1177 &ctx
->progress_count
, &ctx
->progress_bytes
,
1180 compute_dir_size_destroy_ui (ui
);
1182 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1187 ctx
->progress_count
= panel
->marked
;
1188 ctx
->progress_bytes
= panel
->total
;
1189 ctx
->progress_totals_computed
= FALSE
;
1195 /* --------------------------------------------------------------------------------------------- */
1197 * Generate user prompt for panel operation.
1198 * single_source is the name if the source entry or NULL for multiple
1200 * src_stat is only used when single_source is not NULL.
1204 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1205 gboolean single_source
, const struct stat
*src_stat
)
1207 const char *sp
, *cp
;
1208 char format_string
[BUF_MEDIUM
];
1209 char *dp
= format_string
;
1210 gboolean build_question
= FALSE
;
1212 static gboolean i18n_flag
= FALSE
;
1217 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1218 op_names1
[i
] = Q_ (op_names1
[i
]);
1221 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1222 prompt_parts
[i
] = _(prompt_parts
[i
]);
1224 one_format
= _(one_format
);
1225 many_format
= _(many_format
);
1226 question_format
= _(question_format
);
1227 #endif /* ENABLE_NLS */
1231 sp
= single_source
? one_format
: many_format
;
1242 cp
= op_names1
[operation
];
1245 if (operation
== OP_DELETE
)
1248 build_question
= TRUE
;
1251 cp
= prompt_parts
[5];
1254 if (operation
== OP_DELETE
)
1257 build_question
= TRUE
;
1260 cp
= prompt_parts
[6];
1264 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1266 cp
= (panel
->marked
== panel
->dirs_marked
)
1268 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1289 char tmp
[BUF_MEDIUM
];
1291 memmove (tmp
, format_string
, sizeof (tmp
));
1292 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1295 return g_strdup (format_string
);
1298 /* --------------------------------------------------------------------------------------------- */
1300 #ifdef ENABLE_BACKGROUND
1302 end_bg_process (FileOpContext
* ctx
, enum OperationMode mode
)
1309 unregister_task_with_pid (pid
);
1310 /* file_op_context_destroy(ctx); */
1316 /* --------------------------------------------------------------------------------------------- */
1317 /*** public functions ****************************************************************************/
1318 /* --------------------------------------------------------------------------------------------- */
1321 copy_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
1322 const char *src_path
, const char *dst_path
)
1324 uid_t src_uid
= (uid_t
) - 1;
1325 gid_t src_gid
= (gid_t
) - 1;
1327 int src_desc
, dest_desc
= -1;
1328 int n_read
, n_written
;
1329 mode_t src_mode
= 0; /* The mode of the source file */
1330 struct stat sb
, sb2
;
1332 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1333 off_t file_size
= -1;
1334 FileProgressStatus return_status
, temp_status
;
1335 struct timeval tv_transfer_start
;
1336 dest_status_t dst_status
= DEST_NONE
;
1338 gboolean is_first_time
= TRUE
;
1340 /* FIXME: We should not be using global variables! */
1342 return_status
= FILE_RETRY
;
1344 file_progress_show_source (ctx
, src_path
);
1345 file_progress_show_target (ctx
, dst_path
);
1346 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1351 while (mc_stat (dst_path
, &sb2
) == 0)
1353 if (S_ISDIR (sb2
.st_mode
))
1356 return_status
= FILE_SKIPALL
;
1359 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1360 if (return_status
== FILE_SKIPALL
)
1361 ctx
->skip_all
= TRUE
;
1362 if (return_status
== FILE_RETRY
)
1365 return return_status
;
1371 while ((*ctx
->stat_func
) (src_path
, &sb
) != 0)
1374 return_status
= FILE_SKIPALL
;
1377 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1378 if (return_status
== FILE_SKIPALL
)
1379 ctx
->skip_all
= TRUE
;
1381 if (return_status
!= FILE_RETRY
)
1382 return return_status
;
1387 /* Destination already exists */
1388 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1389 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), src_path
, dst_path
);
1390 /* Should we replace destination? */
1391 if (tctx
->ask_overwrite
)
1394 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1395 if (return_status
!= FILE_CONT
)
1396 return return_status
;
1400 if (!ctx
->do_append
)
1402 /* Check the hardlinks */
1403 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_path
, dst_path
, &sb
))
1405 /* We have made a hardlink - no more processing is necessary */
1409 if (S_ISLNK (sb
.st_mode
))
1410 return make_symlink (ctx
, src_path
, dst_path
);
1412 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1413 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1415 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0
1418 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1419 if (return_status
== FILE_RETRY
)
1421 if (return_status
== FILE_SKIPALL
)
1422 ctx
->skip_all
= TRUE
;
1423 return return_status
;
1427 while (ctx
->preserve_uidgid
&& mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
) != 0
1430 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1431 if (temp_status
== FILE_SKIP
)
1433 if (temp_status
== FILE_SKIPALL
)
1434 ctx
->skip_all
= TRUE
;
1435 if (temp_status
!= FILE_RETRY
)
1439 while (ctx
->preserve
&& mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
) != 0
1442 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1443 if (temp_status
== FILE_SKIP
)
1445 if (temp_status
== FILE_SKIPALL
)
1446 ctx
->skip_all
= TRUE
;
1447 if (temp_status
!= FILE_RETRY
)
1455 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1457 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1459 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1460 if (return_status
== FILE_RETRY
)
1462 if (return_status
== FILE_SKIPALL
)
1463 ctx
->skip_all
= TRUE
;
1464 if (return_status
== FILE_SKIP
)
1467 return return_status
;
1470 if (ctx
->do_reget
!= 0)
1472 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1474 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1476 ctx
->do_append
= FALSE
;
1480 while (mc_fstat (src_desc
, &sb
) != 0)
1483 return_status
= FILE_SKIPALL
;
1486 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1487 if (return_status
== FILE_RETRY
)
1489 if (return_status
== FILE_SKIPALL
)
1490 ctx
->skip_all
= TRUE
;
1491 ctx
->do_append
= FALSE
;
1495 src_mode
= sb
.st_mode
;
1496 src_uid
= sb
.st_uid
;
1497 src_gid
= sb
.st_gid
;
1498 utb
.actime
= sb
.st_atime
;
1499 utb
.modtime
= sb
.st_mtime
;
1500 file_size
= sb
.st_size
;
1502 open_flags
= O_WRONLY
;
1505 if (ctx
->do_append
!= 0)
1506 open_flags
|= O_APPEND
;
1508 open_flags
|= O_CREAT
| O_TRUNC
;
1512 open_flags
|= O_CREAT
| O_EXCL
;
1515 while ((dest_desc
= mc_open (dst_path
, open_flags
, src_mode
)) < 0)
1517 if (errno
!= EEXIST
)
1520 return_status
= FILE_SKIPALL
;
1523 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1524 if (return_status
== FILE_RETRY
)
1526 if (return_status
== FILE_SKIPALL
)
1527 ctx
->skip_all
= TRUE
;
1528 ctx
->do_append
= FALSE
;
1533 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1535 appending
= ctx
->do_append
;
1536 ctx
->do_append
= FALSE
;
1538 /* Find out the optimal buffer size. */
1539 while (mc_fstat (dest_desc
, &sb
) != 0)
1542 return_status
= FILE_SKIPALL
;
1545 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1546 if (return_status
== FILE_RETRY
)
1548 if (return_status
== FILE_SKIPALL
)
1549 ctx
->skip_all
= TRUE
;
1556 errno
= vfs_preallocate (dest_desc
, file_size
, (ctx
->do_append
!= 0) ? sb
.st_size
: 0);
1561 return_status
= FILE_SKIPALL
;
1565 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1566 if (return_status
== FILE_RETRY
)
1568 if (return_status
== FILE_SKIPALL
)
1569 ctx
->skip_all
= TRUE
;
1571 mc_close (dest_desc
);
1573 mc_unlink (dst_path
);
1574 dst_status
= DEST_NONE
;
1578 ctx
->eta_secs
= 0.0;
1581 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1582 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1584 file_progress_show (ctx
, 1, 1, "", TRUE
);
1585 return_status
= check_progress_buttons (ctx
);
1588 if (return_status
!= FILE_CONT
)
1592 off_t n_read_total
= 0;
1593 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1594 int secs
, update_secs
;
1595 const char *stalled_msg
= "";
1597 tv_last_update
= tv_transfer_start
;
1604 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1607 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0 && !ctx
->skip_all
)
1609 return_status
= file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
1610 if (return_status
== FILE_RETRY
)
1612 if (return_status
== FILE_SKIPALL
)
1613 ctx
->skip_all
= TRUE
;
1619 gettimeofday (&tv_current
, NULL
);
1624 n_read_total
+= n_read
;
1626 /* Windows NT ftp servers report that files have no
1627 * permissions: -------, so if we happen to have actually
1628 * read something, we should fix the permissions.
1630 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1631 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1632 gettimeofday (&tv_last_input
, NULL
);
1635 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
&& !ctx
->skip_all
)
1639 n_read
-= n_written
;
1643 return_status
= file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1644 if (return_status
== FILE_SKIP
)
1646 if (return_status
== FILE_SKIPALL
)
1647 ctx
->skip_all
= TRUE
;
1648 if (return_status
!= FILE_RETRY
)
1653 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1655 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1656 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1658 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1660 copy_file_file_display_progress (tctx
, ctx
,
1662 tv_transfer_start
, file_size
, n_read_total
);
1663 tv_last_update
= tv_current
;
1665 is_first_time
= FALSE
;
1667 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1669 stalled_msg
= _("(stalled)");
1673 gboolean force_update
;
1676 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1678 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1680 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1681 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1684 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1689 return_status
= check_progress_buttons (ctx
);
1691 if (return_status
!= FILE_CONT
)
1699 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1702 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1704 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1705 if (temp_status
== FILE_RETRY
)
1707 if (temp_status
== FILE_ABORT
)
1708 return_status
= temp_status
;
1709 if (temp_status
== FILE_SKIPALL
)
1710 ctx
->skip_all
= TRUE
;
1714 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1716 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1717 if (temp_status
== FILE_RETRY
)
1719 if (temp_status
== FILE_SKIPALL
)
1720 ctx
->skip_all
= TRUE
;
1721 return_status
= temp_status
;
1725 if (dst_status
== DEST_SHORT
)
1727 /* Remove short file */
1729 result
= query_dialog (Q_ ("DialogTitle|Copy"),
1730 _("Incomplete file was retrieved. Keep it?"),
1731 D_ERROR
, 2, _("&Delete"), _("&Keep"));
1733 mc_unlink (dst_path
);
1735 else if (dst_status
== DEST_FULL
)
1737 /* Copy has succeeded */
1738 if (!appending
&& ctx
->preserve_uidgid
)
1740 while (mc_chown (dst_path
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1742 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1743 if (temp_status
== FILE_RETRY
)
1745 if (temp_status
== FILE_SKIPALL
)
1747 ctx
->skip_all
= TRUE
;
1748 return_status
= FILE_CONT
;
1750 if (temp_status
== FILE_SKIP
)
1751 return_status
= FILE_CONT
;
1760 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1762 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1763 if (temp_status
== FILE_RETRY
)
1765 if (temp_status
== FILE_SKIPALL
)
1767 ctx
->skip_all
= TRUE
;
1768 return_status
= FILE_CONT
;
1770 if (temp_status
== FILE_SKIP
)
1771 return_status
= FILE_CONT
;
1777 src_mode
= umask (-1);
1779 src_mode
= 0100666 & ~src_mode
;
1780 mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
));
1782 mc_utime (dst_path
, &utb
);
1786 if (return_status
== FILE_CONT
)
1787 return_status
= progress_update_one (tctx
, ctx
, file_size
);
1789 return return_status
;
1792 /* --------------------------------------------------------------------------------------------- */
1794 * I think these copy_*_* functions should have a return type.
1795 * anyway, this function *must* have two directories as arguments.
1797 /* FIXME: This function needs to check the return values of the
1801 copy_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *_d
,
1802 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, struct link
* parent_dirs
)
1804 struct dirent
*next
;
1805 struct stat buf
, cbuf
;
1807 char *dest_dir
= NULL
;
1808 FileProgressStatus return_status
= FILE_CONT
;
1815 /* First get the mode of the source dir */
1817 if ((*ctx
->stat_func
) (s
, &cbuf
) != 0)
1820 return_status
= FILE_SKIPALL
;
1823 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
1824 if (return_status
== FILE_RETRY
)
1825 goto retry_src_stat
;
1826 if (return_status
== FILE_SKIPALL
)
1827 ctx
->skip_all
= TRUE
;
1832 if (is_in_linklist (dest_dirs
, s
, &cbuf
))
1834 /* Don't copy a directory we created before (we don't want to copy
1835 infinitely if a directory is copied into itself) */
1836 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1837 return_status
= FILE_CONT
;
1841 /* Hmm, hardlink to directory??? - Norbert */
1842 /* FIXME: In this step we should do something
1843 in case the destination already exist */
1844 /* Check the hardlinks */
1845 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (s
, d
, &cbuf
))
1847 /* We have made a hardlink - no more processing is necessary */
1851 if (!S_ISDIR (cbuf
.st_mode
))
1854 return_status
= FILE_SKIPALL
;
1857 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
1858 if (return_status
== FILE_RETRY
)
1859 goto retry_src_stat
;
1860 if (return_status
== FILE_SKIPALL
)
1861 ctx
->skip_all
= TRUE
;
1866 if (is_in_linklist (parent_dirs
, s
, &cbuf
))
1868 /* we found a cyclic symbolic link */
1869 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
1870 return_status
= FILE_SKIP
;
1874 lp
= g_new (struct link
, 1);
1876 vfs_path_t
*vpath
= vfs_path_from_str (s
);
1877 lp
->vfs
= vfs_path_get_by_index (vpath
, -1)->class;
1878 vfs_path_free (vpath
);
1880 lp
->ino
= cbuf
.st_ino
;
1881 lp
->dev
= cbuf
.st_dev
;
1882 lp
->next
= parent_dirs
;
1886 /* Now, check if the dest dir exists, if not, create it. */
1887 if (mc_stat (d
, &buf
))
1889 /* Here the dir doesn't exist : make it ! */
1892 if (mc_rename (s
, d
) == 0)
1894 return_status
= FILE_CONT
;
1904 * If the destination directory exists, we want to copy the whole
1905 * directory, but we only want this to happen once.
1907 * Escape sequences added to the * to compiler warnings.
1908 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1909 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
1911 if (!S_ISDIR (buf
.st_mode
))
1914 return_status
= FILE_SKIPALL
;
1917 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
1918 if (return_status
== FILE_SKIPALL
)
1919 ctx
->skip_all
= TRUE
;
1920 if (return_status
== FILE_RETRY
)
1921 goto retry_dst_stat
;
1925 /* Dive into subdir if exists */
1926 if (toplevel
&& ctx
->dive_into_subdirs
)
1928 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
1937 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
))
1940 return_status
= FILE_SKIPALL
;
1943 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir
);
1944 if (return_status
== FILE_SKIPALL
)
1945 ctx
->skip_all
= TRUE
;
1947 if (return_status
!= FILE_RETRY
)
1951 lp
= g_new (struct link
, 1);
1952 mc_stat (dest_dir
, &buf
);
1954 vfs_path_t
*vpath
= vfs_path_from_str (dest_dir
);
1955 lp
->vfs
= vfs_path_get_by_index (vpath
, -1)->class;
1956 vfs_path_free (vpath
);
1958 lp
->ino
= buf
.st_ino
;
1959 lp
->dev
= buf
.st_dev
;
1960 lp
->next
= dest_dirs
;
1963 if (ctx
->preserve_uidgid
)
1965 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
1968 return_status
= FILE_SKIPALL
;
1972 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir
);
1973 if (return_status
== FILE_SKIPALL
)
1974 ctx
->skip_all
= TRUE
;
1976 if (return_status
!= FILE_RETRY
)
1982 /* open the source dir for reading */
1983 reading
= mc_opendir (s
);
1984 if (reading
== NULL
)
1987 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1991 * Now, we don't want '.' and '..' to be created / copied at any time
1993 if (!strcmp (next
->d_name
, "."))
1995 if (!strcmp (next
->d_name
, ".."))
1998 /* get the filename and add it to the src directory */
1999 path
= concat_dir_and_file (s
, next
->d_name
);
2001 (*ctx
->stat_func
) (path
, &buf
);
2002 if (S_ISDIR (buf
.st_mode
))
2006 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
2008 * From here, we just intend to recursively copy subdirs, not
2009 * the double functionality of copying different when the target
2010 * dir already exists. So, we give the recursive call the flag 0
2011 * meaning no toplevel.
2014 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2021 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
2022 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2025 if (do_delete
&& return_status
== FILE_CONT
)
2027 if (ctx
->erase_at_end
)
2029 static struct link
*tail
;
2030 size_t len
= strlen (path
);
2031 lp
= g_malloc (sizeof (struct link
) + len
);
2032 strncpy (lp
->name
, path
, len
+ 1);
2033 lp
->st_mode
= buf
.st_mode
;
2035 if (erase_list
!= NULL
)
2041 erase_list
= tail
= lp
;
2045 if (S_ISDIR (buf
.st_mode
))
2047 return_status
= erase_dir_iff_empty (ctx
, path
);
2050 return_status
= erase_file (tctx
, ctx
, path
);
2055 mc_closedir (reading
);
2059 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
2060 utb
.actime
= cbuf
.st_atime
;
2061 utb
.modtime
= cbuf
.st_mtime
;
2062 mc_utime (dest_dir
, &utb
);
2066 cbuf
.st_mode
= umask (-1);
2067 umask (cbuf
.st_mode
);
2068 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2069 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
2074 g_free (parent_dirs
);
2077 return return_status
;
2082 /* --------------------------------------------------------------------------------------------- */
2083 /* {{{ Move routines */
2086 move_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
2088 struct stat sbuf
, dbuf
, destbuf
;
2091 FileProgressStatus return_status
;
2092 gboolean move_over
= FALSE
;
2095 file_progress_show_source (ctx
, s
);
2096 file_progress_show_target (ctx
, d
);
2097 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2103 dstat_ok
= (mc_stat (d
, &dbuf
) == 0);
2105 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2106 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2109 destdir
= g_strdup (d
); /* destination doesn't exist */
2110 else if (!ctx
->dive_into_subdirs
)
2112 destdir
= g_strdup (d
);
2116 destdir
= concat_dir_and_file (d
, x_basename (s
));
2118 /* Check if the user inputted an existing dir */
2120 if (mc_stat (destdir
, &destbuf
) == 0)
2124 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
2126 if (return_status
!= FILE_CONT
)
2130 else if (ctx
->skip_all
)
2131 return_status
= FILE_SKIPALL
;
2134 if (S_ISDIR (destbuf
.st_mode
))
2135 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir
);
2137 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir
);
2138 if (return_status
== FILE_SKIPALL
)
2139 ctx
->skip_all
= TRUE
;
2140 if (return_status
== FILE_RETRY
)
2141 goto retry_dst_stat
;
2144 return return_status
;
2148 if (mc_rename (s
, destdir
) == 0)
2150 return_status
= FILE_CONT
;
2158 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2159 if (return_status
== FILE_SKIPALL
)
2160 ctx
->skip_all
= TRUE
;
2161 if (return_status
== FILE_RETRY
)
2166 /* Failed because of filesystem boundary -> copy dir instead */
2167 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
2169 if (return_status
!= FILE_CONT
)
2172 file_progress_show_source (ctx
, NULL
);
2173 file_progress_show (ctx
, 0, 0, "", FALSE
);
2175 return_status
= check_progress_buttons (ctx
);
2176 if (return_status
!= FILE_CONT
)
2180 if (ctx
->erase_at_end
)
2182 for (; erase_list
&& return_status
!= FILE_ABORT
;)
2184 if (S_ISDIR (erase_list
->st_mode
))
2186 return_status
= erase_dir_iff_empty (ctx
, erase_list
->name
);
2189 return_status
= erase_file (tctx
, ctx
, erase_list
->name
);
2191 erase_list
= erase_list
->next
;
2195 erase_dir_iff_empty (ctx
, s
);
2202 erase_list
= erase_list
->next
;
2205 return return_status
;
2210 /* --------------------------------------------------------------------------------------------- */
2211 /* {{{ Erase routines */
2214 erase_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
2216 FileProgressStatus error
;
2218 if (strcmp (s
, "..") == 0)
2221 if (strcmp (s
, ".") == 0)
2224 file_progress_show_deleting (ctx
, s
);
2225 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2229 /* The old way to detect a non empty directory was:
2230 error = my_rmdir (s);
2231 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2232 For the linux user space nfs server (nfs-server-2.2beta29-2)
2233 we would have to check also for EIO. I hope the new way is
2234 fool proof. (Norbert)
2236 error
= check_dir_is_empty (s
);
2239 error
= query_recursive (ctx
, s
);
2240 if (error
== FILE_CONT
)
2241 return recursive_erase (tctx
, ctx
, s
);
2246 while (my_rmdir (s
) == -1 && !ctx
->skip_all
)
2248 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
2249 if (error
!= FILE_RETRY
)
2258 /* --------------------------------------------------------------------------------------------- */
2259 /* {{{ Panel operate routines */
2262 compute_dir_size_create_ui (void)
2264 ComputeDirSizeUI
*ui
;
2266 const char *b_name
= N_("&Abort");
2272 ui
= g_new (ComputeDirSizeUI
, 1);
2274 ui
->dlg
= create_dlg (TRUE
, 0, 0, 8, COLS
/ 2, dialog_colors
, NULL
,
2275 NULL
, _("Directory scanning"), DLG_CENTER
);
2276 ui
->dirname
= label_new (3, 3, "");
2277 add_widget (ui
->dlg
, ui
->dirname
);
2279 add_widget (ui
->dlg
,
2280 button_new (5, (ui
->dlg
->cols
- strlen (b_name
)) / 2,
2281 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
2283 /* We will manage the dialog without any help,
2284 that's why we have to call init_dlg */
2290 /* --------------------------------------------------------------------------------------------- */
2293 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
2297 /* schedule to update passive panel */
2298 other_panel
->dirty
= 1;
2300 /* close and destroy dialog */
2301 dlg_run_done (ui
->dlg
);
2302 destroy_dlg (ui
->dlg
);
2307 /* --------------------------------------------------------------------------------------------- */
2310 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
2312 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
2319 label_set_text (this->dirname
, str_trunc (dirname
, this->dlg
->cols
- 6));
2321 event
.x
= -1; /* Don't show the GPM cursor */
2322 c
= tty_get_event (&event
, FALSE
, FALSE
);
2326 /* Reinitialize to avoid old values after events other than
2327 selecting a button */
2328 this->dlg
->ret_value
= FILE_CONT
;
2330 dlg_process_event (this->dlg
, c
, &event
);
2332 switch (this->dlg
->ret_value
)
2342 /* --------------------------------------------------------------------------------------------- */
2346 * Computes the number of bytes used by the files in a directory
2350 compute_dir_size (const char *dirname
, const void *ui
,
2351 compute_dir_size_callback cback
,
2352 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
2357 struct dirent
*dirent
;
2358 FileProgressStatus ret
= FILE_CONT
;
2360 if (!compute_symlinks
)
2362 res
= mc_lstat (dirname
, &s
);
2366 /* don't scan symlink to directory */
2367 if (S_ISLNK (s
.st_mode
))
2370 *ret_total
+= (uintmax_t) s
.st_size
;
2375 dir
= mc_opendir (dirname
);
2380 while ((dirent
= mc_readdir (dir
)) != NULL
)
2384 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
2386 if (ret
!= FILE_CONT
)
2389 if (strcmp (dirent
->d_name
, ".") == 0)
2391 if (strcmp (dirent
->d_name
, "..") == 0)
2394 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
2395 res
= mc_lstat (fullname
, &s
);
2403 if (S_ISDIR (s
.st_mode
))
2405 size_t subdir_count
= 0;
2406 uintmax_t subdir_bytes
= 0;
2409 compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
,
2412 if (ret
!= FILE_CONT
)
2418 *ret_marked
+= subdir_count
;
2419 *ret_total
+= subdir_bytes
;
2424 *ret_total
+= (uintmax_t) s
.st_size
;
2435 /* --------------------------------------------------------------------------------------------- */
2439 * Performs one of the operations on the selection on the source_panel
2440 * (copy, delete, move).
2442 * Returns TRUE if did change the directory
2443 * structure, Returns FALSE if user aborted
2445 * force_single forces operation on the current entry and affects
2446 * default destination. Current filename is used as default.
2450 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2452 WPanel
*panel
= (WPanel
*) source_panel
;
2453 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2454 || (get_current_type () == view_tree
);
2456 char *source
= NULL
;
2457 #ifdef WITH_FULL_PATHS
2458 char *source_with_path
= NULL
;
2460 #define source_with_path source
2461 #endif /* !WITH_FULL_PATHS */
2464 char *save_cwd
= NULL
, *save_dest
= NULL
;
2465 struct stat src_stat
;
2466 gboolean ret_val
= TRUE
;
2468 FileProgressStatus value
;
2470 FileOpTotalContext
*tctx
;
2472 gboolean do_bg
= FALSE
; /* do background operation? */
2474 static gboolean i18n_flag
= FALSE
;
2477 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
2478 op_names
[i
] = Q_ (op_names
[i
]);
2482 free_linklist (&linklist
);
2483 free_linklist (&dest_dirs
);
2488 source
= selection (panel
)->fname
;
2490 source
= panel_get_file (panel
);
2492 if (strcmp (source
, "..") == 0)
2494 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2498 /* Update stat to get actual info */
2499 if (mc_lstat (source
, &src_stat
) != 0)
2501 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2502 path_trunc (source
, 30), unix_error_string (errno
));
2504 /* Directory was changed outside MC. Reload it forced */
2505 if (!panel
->is_panelized
)
2507 panel_update_flags_t flags
= UP_RELOAD
;
2509 /* don't update panelized panel */
2510 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2511 flags
|= UP_ONLY_CURRENT
;
2513 update_panels (flags
, UP_KEEPSEL
);
2520 ctx
= file_op_context_new (operation
);
2522 /* Show confirmation dialog */
2523 if (operation
!= OP_DELETE
)
2529 /* Forced single operations default to the original name */
2532 else if (get_other_type () == view_listing
)
2533 dest_dir
= other_panel
->cwd
;
2535 dest_dir
= panel
->cwd
;
2537 * Add trailing backslash only when do non-local ops.
2538 * It saves user from occasional file renames (when destination
2541 if (!force_single
&& dest_dir
[0] != '\0' && dest_dir
[strlen (dest_dir
) - 1] != PATH_SEP
)
2543 /* add trailing separator */
2544 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2549 dest_dir_
= g_strdup (dest_dir
);
2552 if (dest_dir_
== NULL
)
2558 /* Generate confirmation prompt */
2559 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2561 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2562 source
!= NULL
? (void *) source
2563 : (void *) &panel
->marked
, dest_dir_
, &do_bg
);
2568 if (dest
== NULL
|| dest
[0] == '\0')
2575 else if (confirm_delete
)
2578 char fmd_buf
[BUF_MEDIUM
];
2580 /* Generate confirmation prompt */
2581 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2584 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2587 const int fmd_xlen
= 64;
2588 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2589 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2597 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2606 tctx
= file_op_total_context_new ();
2607 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2610 filegui_dialog_type_t dialog_type
;
2612 if (operation
== OP_DELETE
)
2613 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2616 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2617 ? FILEGUI_DIALOG_MULTI_ITEM
: FILEGUI_DIALOG_ONE_ITEM
;
2619 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2620 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2623 /* Background also need ctx->ui, but not full */
2625 file_op_context_create_ui_without_init (ctx
, TRUE
, dialog_type
);
2627 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
2630 #ifdef ENABLE_BACKGROUND
2631 /* Did the user select to do a background operation? */
2636 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", panel
->cwd
, (char *) NULL
));
2638 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2640 /* If we are the parent */
2643 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
2644 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
2645 /* file_op_context_destroy (ctx); */
2649 #endif /* ENABLE_BACKGROUND */
2651 /* Initialize things */
2652 /* We do not want to trash cache every time file is
2653 created/touched. However, this will make our cache contain
2655 if ((dest
!= NULL
) && (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2656 save_dest
= g_strdup (dest
);
2658 if ((panel
->cwd
[0] != '\0') && (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2659 save_cwd
= g_strdup (panel
->cwd
);
2661 /* Now, let's do the job */
2663 /* This code is only called by the tree and panel code */
2666 /* We now have ETA in all cases */
2668 /* One file: FIXME mc_chdir will take user out of any vfs */
2669 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
) &&
2670 (mc_chdir (PATH_SEP_STR
) < 0))
2676 /* The source and src_stat variables have been initialized before */
2677 #ifdef WITH_FULL_PATHS
2678 if (g_path_is_absolute (source
))
2679 source_with_path
= g_strdup (source
);
2681 source_with_path
= mc_build_filename (panel
->cwd
, source
, (char *) NULL
);
2682 #endif /* WITH_FULL_PATHS */
2684 if (panel_operate_init_totals (operation
, panel
, source_with_path
, ctx
) == FILE_CONT
)
2686 if (operation
== OP_DELETE
)
2688 if (S_ISDIR (src_stat
.st_mode
))
2689 value
= erase_dir (tctx
, ctx
, source_with_path
);
2691 value
= erase_file (tctx
, ctx
, source_with_path
);
2695 temp
= transform_source (ctx
, source_with_path
);
2697 value
= transform_error
;
2700 char *repl_dest
, *temp2
;
2702 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2703 temp2
= concat_dir_and_file (repl_dest
, temp
);
2712 /* we use file_mask_op_follow_links only with OP_COPY */
2713 ctx
->stat_func (source_with_path
, &src_stat
);
2715 if (S_ISDIR (src_stat
.st_mode
))
2716 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, dest
,
2717 TRUE
, FALSE
, FALSE
, NULL
);
2719 value
= copy_file_file (tctx
, ctx
, source_with_path
, dest
);
2723 if (S_ISDIR (src_stat
.st_mode
))
2724 value
= move_dir_dir (tctx
, ctx
, source_with_path
, dest
);
2726 value
= move_file_file (tctx
, ctx
, source_with_path
, dest
);
2730 /* Unknown file operation */
2734 } /* Copy or move operation */
2736 if ((value
== FILE_CONT
) && !force_single
)
2737 unmark_files (panel
);
2744 /* Check destination for copy or move operation */
2745 while (operation
!= OP_DELETE
)
2748 struct stat dst_stat
;
2750 dst_result
= mc_stat (dest
, &dst_stat
);
2752 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2756 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2760 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
)
2762 /* Loop for every file, perform the actual copy operation */
2763 for (i
= 0; i
< panel
->count
; i
++)
2765 if (!panel
->dir
.list
[i
].f
.marked
)
2766 continue; /* Skip the unmarked ones */
2768 source
= panel
->dir
.list
[i
].fname
;
2769 src_stat
= panel
->dir
.list
[i
].st
;
2771 #ifdef WITH_FULL_PATHS
2772 g_free (source_with_path
);
2773 if (g_path_is_absolute (source
))
2774 source_with_path
= g_strdup (source
);
2776 source_with_path
= mc_build_filename (panel
->cwd
, source
, (char *) NULL
);
2777 #endif /* WITH_FULL_PATHS */
2779 if (operation
== OP_DELETE
)
2781 if (S_ISDIR (src_stat
.st_mode
))
2782 value
= erase_dir (tctx
, ctx
, source_with_path
);
2784 value
= erase_file (tctx
, ctx
, source_with_path
);
2788 temp
= transform_source (ctx
, source_with_path
);
2791 value
= transform_error
;
2794 char *temp2
, *temp3
, *repl_dest
;
2796 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2797 temp2
= concat_dir_and_file (repl_dest
, temp
);
2800 temp3
= source_with_path
;
2801 source_with_path
= strutils_shell_unescape (source_with_path
);
2804 temp2
= strutils_shell_unescape (temp2
);
2810 /* we use file_mask_op_follow_links only with OP_COPY */
2811 ctx
->stat_func (source_with_path
, &src_stat
);
2812 if (S_ISDIR (src_stat
.st_mode
))
2813 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, temp2
,
2814 TRUE
, FALSE
, FALSE
, NULL
);
2816 value
= copy_file_file (tctx
, ctx
, source_with_path
, temp2
);
2817 free_linklist (&dest_dirs
);
2821 if (S_ISDIR (src_stat
.st_mode
))
2822 value
= move_dir_dir (tctx
, ctx
, source_with_path
, temp2
);
2824 value
= move_file_file (tctx
, ctx
, source_with_path
, temp2
);
2828 /* Unknown file operation */
2834 } /* Copy or move operation */
2836 if (value
== FILE_ABORT
)
2839 if (value
== FILE_CONT
)
2840 do_file_mark (panel
, i
, 0);
2842 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
2844 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2845 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
2848 if (operation
!= OP_DELETE
)
2849 file_progress_show (ctx
, 0, 0, "", FALSE
);
2851 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2855 } /* Loop for every file */
2857 } /* Many entries */
2861 if (save_cwd
!= NULL
)
2863 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2867 if (save_dest
!= NULL
)
2869 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2873 free_linklist (&linklist
);
2874 free_linklist (&dest_dirs
);
2875 #ifdef WITH_FULL_PATHS
2876 g_free (source_with_path
);
2877 #endif /* WITH_FULL_PATHS */
2879 g_free (ctx
->dest_mask
);
2880 ctx
->dest_mask
= NULL
;
2882 #ifdef ENABLE_BACKGROUND
2883 /* Let our parent know we are saying bye bye */
2884 if (mc_global
.we_are_background
)
2886 int cur_pid
= getpid ();
2887 /* Send pid to parent with child context, it is fork and
2888 don't modify real parent ctx */
2890 parent_call ((void *) end_bg_process
, ctx
, 0);
2895 #endif /* ENABLE_BACKGROUND */
2897 file_op_total_context_destroy (tctx
);
2899 file_op_context_destroy (ctx
);
2906 /* --------------------------------------------------------------------------------------------- */
2907 /* {{{ Query/status report routines */
2908 /** Report error with one file */
2910 file_error (const char *format
, const char *file
)
2912 char buf
[BUF_MEDIUM
];
2914 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
2916 return do_file_error (buf
);
2919 /* --------------------------------------------------------------------------------------------- */
2922 Cause emacs to enter folding mode for this file: