4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011
6 The Free Software Foundation, Inc.
9 Janne Kukonlehto, 1994, 1995
10 Fred Leeflang, 1994, 1995
11 Miguel de Icaza, 1994, 1995, 1996
12 Jakub Jelinek, 1995, 1996
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
47 * \brief Source: file management
50 /* {{{ Include files */
59 #include <sys/types.h>
64 #include "lib/global.h"
65 #include "lib/tty/tty.h"
66 #include "lib/tty/key.h"
67 #include "lib/search.h"
68 #include "lib/strescape.h"
69 #include "lib/strutil.h"
71 #include "lib/vfs/vfs.h"
72 #include "lib/widget.h"
74 #include "src/setup.h"
75 #include "src/background.h"
77 #include "layout.h" /* rotate_dash() */
79 /* Needed for current_panel, other_panel and WTree */
83 #include "midnight.h" /* current_panel */
89 /*** global variables ****************************************************************************/
91 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
92 const char *op_names
[3] = {
93 N_("DialogTitle|Copy"),
94 N_("DialogTitle|Move"),
95 N_("DialogTitle|Delete")
98 /*** file scope macro definitions ****************************************************************/
100 /* Hack: the vfs code should not rely on this */
101 #define WITH_FULL_PATHS 1
103 #define FILEOP_UPDATE_INTERVAL 2
104 #define FILEOP_STALLING_INTERVAL 4
106 /*** file scope type declarations ****************************************************************/
108 /* This is a hard link cache */
112 struct vfs_class
*vfs
;
120 /* Status of the destination file */
123 DEST_NONE
= 0, /* Not created */
124 DEST_SHORT
= 1, /* Created, not fully copied */
125 DEST_FULL
= 2 /* Created, fully copied */
129 * This array introduced to avoid translation problems. The former (op_names)
130 * is assumed to be nouns, suitable in dialog box titles; this one should
131 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
132 * (I don't use spaces around the words, because someday they could be
133 * dropped, when widgets get smarter)
136 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
137 static const char *op_names1
[] = {
138 N_("FileOperation|Copy"),
139 N_("FileOperation|Move"),
140 N_("FileOperation|Delete")
144 * These are formats for building a prompt. Parts encoded as follows:
145 * %o - operation from op_names1
146 * %f - file/files or files/directories, as appropriate
147 * %m - "with source mask" or question mark for delete
148 * %s - source name (truncated)
149 * %d - number of marked files
150 * %e - "to:" or question mark for delete
152 * xgettext:no-c-format */
153 static const char *one_format
= N_("%o %f \"%s\"%m");
154 /* xgettext:no-c-format */
155 static const char *many_format
= N_("%o %d %f%m");
157 static const char *prompt_parts
[] = {
162 N_("files/directories"),
163 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
164 N_(" with source mask:"),
168 static const char *question_format
= N_("%s?");
170 /*** file scope variables ************************************************************************/
172 /* the hard link cache */
173 static struct link
*linklist
= NULL
;
175 /* the files-to-be-erased list */
176 static struct link
*erase_list
;
179 * In copy_dir_dir we use two additional single linked lists: The first -
180 * variable name `parent_dirs' - holds information about already copied
181 * directories and is used to detect cyclic symbolic links.
182 * The second (`dest_dirs' below) holds information about just created
183 * target directories and is used to detect when an directory is copied
184 * into itself (we don't want to copy infinitly).
185 * Both lists don't use the linkcount and name structure members of struct
188 static struct link
*dest_dirs
= NULL
;
190 static FileProgressStatus transform_error
= FILE_CONT
;
192 /*** file scope functions ************************************************************************/
193 /* --------------------------------------------------------------------------------------------- */
196 transform_source (FileOpContext
* ctx
, const char *source
)
201 s
= g_strdup (source
);
203 /* We remove \n from the filename since regex routines would use \n as an anchor */
204 /* this is just to be allowed to maniupulate file names with \n on it */
205 for (q
= s
; *q
!= '\0'; q
++)
209 fnsource
= (char *) x_basename (s
);
211 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
212 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
216 transform_error
= FILE_SKIP
;
223 /* --------------------------------------------------------------------------------------------- */
226 free_linklist (struct link
**lc_linklist
)
228 struct link
*lp
, *lp2
;
230 for (lp
= *lc_linklist
; lp
!= NULL
; lp
= lp2
)
238 /* --------------------------------------------------------------------------------------------- */
241 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
244 vfs_path_element_t
*vpath_element
;
245 ino_t ino
= sb
->st_ino
;
246 dev_t dev
= sb
->st_dev
;
248 vpath
= vfs_path_from_str (path
);
249 vpath_element
= vfs_path_get_by_index (vpath
, -1);
253 if (lp
->vfs
== vpath_element
->class)
254 if (lp
->ino
== ino
&& lp
->dev
== dev
)
258 vfs_path_free (vpath
);
262 /* --------------------------------------------------------------------------------------------- */
264 * Check and made hardlink
266 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
267 * and a hardlink was succesfully made
271 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
276 struct vfs_class
*my_vfs
;
277 ino_t ino
= pstat
->st_ino
;
278 dev_t dev
= pstat
->st_dev
;
279 struct stat link_stat
;
282 vpath
= vfs_path_from_str (src_name
);
284 if ((vfs_file_class_flags (vpath
) & VFSF_NOLINKS
) != 0)
286 vfs_path_free (vpath
);
289 my_vfs
= vfs_path_get_by_index (vpath
, -1)->class;
290 vfs_path_free (vpath
);
292 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
293 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
)
295 struct vfs_class
*lp_name_class
;
297 vpath
= vfs_path_from_str (lp
->name
);
298 lp_name_class
= vfs_path_get_by_index (vpath
, -1)->class;
299 vfs_path_free (vpath
);
301 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
302 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
304 struct vfs_class
*p_class
, *dst_name_class
;
306 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
309 vpath
= vfs_path_from_str (p
);
310 p_class
= vfs_path_get_by_index (vpath
, -1)->class;
311 vfs_path_free (vpath
);
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 if (dst_name_class
== p_class
)
319 if (!mc_stat (p
, &link_stat
))
321 if (!mc_link (p
, dst_name
))
326 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
329 lp
= (struct link
*) g_try_malloc (sizeof (struct link
) + strlen (src_name
)
330 + strlen (dst_name
) + 1);
337 strcpy (lp
->name
, src_name
);
338 lpdstname
= lp
->name
+ strlen (lp
->name
) + 1;
339 strcpy (lpdstname
, dst_name
);
346 /* --------------------------------------------------------------------------------------------- */
348 * Duplicate the contents of the symbolic link src_path in dst_path.
349 * Try to make a stable symlink if the option "stable symlink" was
350 * set in the file mask dialog.
351 * If dst_path is an existing symlink it will be deleted silently
352 * (upper levels take already care of existing files at dst_path).
355 static FileProgressStatus
356 make_symlink (FileOpContext
* ctx
, const char *src_path
, const char *dst_path
)
358 char link_target
[MC_MAXPATHLEN
];
360 FileProgressStatus return_status
;
362 gboolean dst_is_symlink
;
364 dst_is_symlink
= (mc_lstat (dst_path
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
367 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
371 return_status
= FILE_SKIPALL
;
374 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
375 if (return_status
== FILE_SKIPALL
)
376 ctx
->skip_all
= TRUE
;
377 if (return_status
== FILE_RETRY
)
378 goto retry_src_readlink
;
380 return return_status
;
382 link_target
[len
] = 0;
384 if (ctx
->stable_symlinks
)
386 vfs_path_t
*vpath1
= vfs_path_from_str (src_path
);
387 vfs_path_t
*vpath2
= vfs_path_from_str (dst_path
);
389 if (!vfs_file_is_local (vpath1
) || !vfs_file_is_local (vpath2
))
391 message (D_ERROR
, MSG_ERROR
,
392 _("Cannot make stable symlinks across"
393 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
394 ctx
->stable_symlinks
= FALSE
;
396 vfs_path_free (vpath1
);
397 vfs_path_free (vpath2
);
400 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
404 const char *r
= strrchr (src_path
, PATH_SEP
);
408 p
= g_strndup (src_path
, r
- src_path
+ 1);
409 if (g_path_is_absolute (dst_path
))
410 q
= g_strdup (dst_path
);
412 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
413 s
= strrchr (q
, PATH_SEP
);
417 s
= g_strconcat (p
, link_target
, (char *) NULL
);
419 g_strlcpy (link_target
, s
, sizeof (link_target
));
421 s
= diff_two_paths (q
, link_target
);
424 g_strlcpy (link_target
, s
, sizeof (link_target
));
434 if (mc_symlink (link_target
, dst_path
) == 0)
438 * if dst_exists, it is obvious that this had failed.
439 * We can delete the old symlink and try again...
443 if (!mc_unlink (dst_path
))
444 if (mc_symlink (link_target
, dst_path
) == 0)
449 return_status
= FILE_SKIPALL
;
452 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
453 if (return_status
== FILE_SKIPALL
)
454 ctx
->skip_all
= TRUE
;
455 if (return_status
== FILE_RETRY
)
456 goto retry_dst_symlink
;
458 return return_status
;
461 /* --------------------------------------------------------------------------------------------- */
463 static FileProgressStatus
464 progress_update_one (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, off_t add
)
466 struct timeval tv_current
;
467 static struct timeval tv_start
= { };
469 tctx
->progress_count
++;
470 tctx
->progress_bytes
+= (uintmax_t) add
;
472 if (tv_start
.tv_sec
== 0)
474 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
476 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
477 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
479 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
481 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
482 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
484 tv_start
.tv_sec
= tv_current
.tv_sec
;
487 return check_progress_buttons (ctx
);
490 /* --------------------------------------------------------------------------------------------- */
492 static FileProgressStatus
493 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
497 const char *head_msg
;
499 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
501 msg
= g_strdup_printf (fmt
, a
, b
);
502 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
506 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
509 /* --------------------------------------------------------------------------------------------- */
511 static FileProgressStatus
512 warn_same_file (const char *fmt
, const char *a
, const char *b
)
514 #ifdef WITH_BACKGROUND
519 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
520 const char *a
, const char *b
);
524 pntr
.f
= real_warn_same_file
;
526 if (mc_global
.we_are_background
)
527 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
529 return real_warn_same_file (Foreground
, fmt
, a
, b
);
532 /* --------------------------------------------------------------------------------------------- */
533 /* {{{ Query/status report routines */
535 static FileProgressStatus
536 real_do_file_error (enum OperationMode mode
, const char *error
)
541 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
543 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
565 /* --------------------------------------------------------------------------------------------- */
567 static FileProgressStatus
568 real_query_recursive (FileOpContext
* ctx
, enum OperationMode mode
, const char *s
)
572 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
574 const char *msg
= mode
== Foreground
575 ? _("\nDirectory not empty.\nDelete it recursively?")
576 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
577 text
= g_strconcat (_("Delete:"), " ", path_trunc (s
, 30), (char *) NULL
);
582 ctx
->recursive_result
=
583 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
584 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
586 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
591 switch (ctx
->recursive_result
)
594 case RECURSIVE_ALWAYS
:
598 case RECURSIVE_NEVER
:
601 case RECURSIVE_ABORT
:
607 /* --------------------------------------------------------------------------------------------- */
609 #ifdef WITH_BACKGROUND
610 static FileProgressStatus
611 do_file_error (const char *str
)
616 FileProgressStatus (*f
) (enum OperationMode
, const char *);
618 pntr
.f
= real_do_file_error
;
620 if (mc_global
.we_are_background
)
621 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
623 return real_do_file_error (Foreground
, str
);
626 /* --------------------------------------------------------------------------------------------- */
628 static FileProgressStatus
629 query_recursive (FileOpContext
* ctx
, const char *s
)
634 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
636 pntr
.f
= real_query_recursive
;
638 if (mc_global
.we_are_background
)
639 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
641 return real_query_recursive (ctx
, Foreground
, s
);
644 /* --------------------------------------------------------------------------------------------- */
646 static FileProgressStatus
647 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
648 struct stat
*_d_stat
)
653 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
654 struct stat
*, struct stat
*);
656 pntr
.f
= file_progress_real_query_replace
;
658 if (mc_global
.we_are_background
)
659 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
660 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
662 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
666 /* --------------------------------------------------------------------------------------------- */
668 static FileProgressStatus
669 do_file_error (const char *str
)
671 return real_do_file_error (Foreground
, str
);
674 /* --------------------------------------------------------------------------------------------- */
676 static FileProgressStatus
677 query_recursive (FileOpContext
* ctx
, const char *s
)
679 return real_query_recursive (ctx
, Foreground
, s
);
682 /* --------------------------------------------------------------------------------------------- */
684 static FileProgressStatus
685 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
686 struct stat
*_d_stat
)
688 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
691 #endif /* !WITH_BACKGROUND */
693 /* --------------------------------------------------------------------------------------------- */
694 /** Report error with two files */
696 static FileProgressStatus
697 files_error (const char *format
, const char *file1
, const char *file2
)
699 char buf
[BUF_MEDIUM
];
700 char *nfile1
= g_strdup (path_trunc (file1
, 15));
701 char *nfile2
= g_strdup (path_trunc (file2
, 15));
703 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
708 return do_file_error (buf
);
713 /* --------------------------------------------------------------------------------------------- */
716 copy_file_file_display_progress (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
717 struct timeval tv_current
, struct timeval tv_transfer_start
,
718 off_t file_size
, off_t n_read_total
)
722 /* 1. Update rotating dash after some time */
726 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
728 if (n_read_total
== 0)
732 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
733 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
736 /* 4. Compute BPS rate */
737 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
738 if (ctx
->bps_time
< 1)
740 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
742 /* 5. Compute total ETA and BPS */
743 if (ctx
->progress_bytes
!= 0)
745 uintmax_t remain_bytes
;
747 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
750 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
755 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
756 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
759 /* broken on lot of little files */
761 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
762 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
767 /* --------------------------------------------------------------------------------------------- */
769 /* {{{ Move routines */
770 static FileProgressStatus
771 move_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
773 struct stat src_stats
, dst_stats
;
774 FileProgressStatus return_status
= FILE_CONT
;
775 gboolean copy_done
= FALSE
;
776 gboolean old_ask_overwrite
;
778 file_progress_show_source (ctx
, s
);
779 file_progress_show_target (ctx
, d
);
780 if (check_progress_buttons (ctx
) == FILE_ABORT
)
785 while (mc_lstat (s
, &src_stats
) != 0)
787 /* Source doesn't exist */
789 return_status
= FILE_SKIPALL
;
792 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
793 if (return_status
== FILE_SKIPALL
)
794 ctx
->skip_all
= TRUE
;
796 if (return_status
!= FILE_RETRY
)
797 return return_status
;
800 if (mc_lstat (d
, &dst_stats
) == 0)
802 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
803 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
805 if (S_ISDIR (dst_stats
.st_mode
))
807 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
812 if (confirm_overwrite
)
814 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
815 if (return_status
!= FILE_CONT
)
816 return return_status
;
818 /* Ok to overwrite */
823 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
825 return_status
= make_symlink (ctx
, s
, d
);
826 if (return_status
== FILE_CONT
)
827 goto retry_src_remove
;
829 return return_status
;
832 if (mc_rename (s
, d
) == 0)
833 return progress_update_one (tctx
, ctx
, src_stats
.st_size
);
836 /* Comparison to EXDEV seems not to work in nfs if you're moving from
837 one nfs to the same, but on the server it is on two different
838 filesystems. Then nfs returns EIO instead of EXDEV.
839 Hope it will not hurt if we always in case of error try to copy/delete. */
841 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
846 return_status
= FILE_SKIPALL
;
849 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
850 if (return_status
== FILE_SKIPALL
)
851 ctx
->skip_all
= TRUE
;
852 if (return_status
== FILE_RETRY
)
855 return return_status
;
859 /* Failed because filesystem boundary -> copy the file instead */
860 old_ask_overwrite
= tctx
->ask_overwrite
;
861 tctx
->ask_overwrite
= FALSE
;
862 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
863 tctx
->ask_overwrite
= old_ask_overwrite
;
864 if (return_status
!= FILE_CONT
)
865 return return_status
;
869 file_progress_show_source (ctx
, NULL
);
870 file_progress_show (ctx
, 0, 0, "", FALSE
);
872 return_status
= check_progress_buttons (ctx
);
873 if (return_status
!= FILE_CONT
)
874 return return_status
;
879 if (mc_unlink (s
) != 0 && !ctx
->skip_all
)
881 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
882 if (return_status
== FILE_RETRY
)
883 goto retry_src_remove
;
884 if (return_status
== FILE_SKIPALL
)
885 ctx
->skip_all
= TRUE
;
886 return return_status
;
890 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
892 return return_status
;
897 /* --------------------------------------------------------------------------------------------- */
898 /* {{{ Erase routines */
899 /** Don't update progress status if progress_count==NULL */
901 static FileProgressStatus
902 erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
907 file_progress_show_deleting (ctx
, s
);
908 if (check_progress_buttons (ctx
) == FILE_ABORT
)
912 if (tctx
->progress_count
!= 0 && mc_lstat (s
, &buf
) != 0)
914 /* ignore, most likely the mc_unlink fails, too */
918 while (mc_unlink (s
) != 0 && !ctx
->skip_all
)
920 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), s
);
921 if (return_status
== FILE_ABORT
)
922 return return_status
;
923 if (return_status
== FILE_RETRY
)
925 if (return_status
== FILE_SKIPALL
)
926 ctx
->skip_all
= TRUE
;
930 if (tctx
->progress_count
== 0)
932 return progress_update_one (tctx
, ctx
, buf
.st_size
);
935 /* --------------------------------------------------------------------------------------------- */
938 Recursive remove of files
940 skip ->warn every level, gets default
941 skipall->remove as much as possible
943 static FileProgressStatus
944 recursive_erase (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
950 FileProgressStatus return_status
= FILE_CONT
;
952 if (!strcmp (s
, ".."))
955 reading
= mc_opendir (s
);
960 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
962 if (!strcmp (next
->d_name
, "."))
964 if (!strcmp (next
->d_name
, ".."))
966 path
= concat_dir_and_file (s
, next
->d_name
);
967 if (mc_lstat (path
, &buf
))
970 mc_closedir (reading
);
973 if (S_ISDIR (buf
.st_mode
))
974 return_status
= recursive_erase (tctx
, ctx
, path
);
976 return_status
= erase_file (tctx
, ctx
, path
);
979 mc_closedir (reading
);
980 if (return_status
== FILE_ABORT
)
981 return return_status
;
982 file_progress_show_deleting (ctx
, s
);
983 if (check_progress_buttons (ctx
) == FILE_ABORT
)
987 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
989 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
990 if (return_status
== FILE_RETRY
)
992 if (return_status
== FILE_ABORT
)
993 return return_status
;
994 if (return_status
== FILE_SKIPALL
)
995 ctx
->skip_all
= TRUE
;
1002 /* --------------------------------------------------------------------------------------------- */
1003 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1004 in the directory path points to, 0 else. */
1007 check_dir_is_empty (const char *path
)
1013 dir
= mc_opendir (path
);
1017 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
))
1019 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1020 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1021 continue; /* "." or ".." */
1030 /* --------------------------------------------------------------------------------------------- */
1032 static FileProgressStatus
1033 erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
)
1035 FileProgressStatus error
;
1037 if (strcmp (s
, "..") == 0)
1040 if (strcmp (s
, ".") == 0)
1043 file_progress_show_deleting (ctx
, s
);
1044 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1048 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1051 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1053 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1054 if (error
== FILE_SKIPALL
)
1055 ctx
->skip_all
= TRUE
;
1056 if (error
!= FILE_RETRY
)
1065 /* --------------------------------------------------------------------------------------------- */
1066 /* {{{ Panel operate routines */
1069 * Return currently selected entry name or the name of the first marked
1070 * entry if there is one.
1074 panel_get_file (WPanel
* panel
)
1076 if (get_current_type () == view_tree
)
1080 tree
= (WTree
*) get_panel_widget (get_current_index ());
1081 return tree_selected_name (tree
);
1084 if (panel
->marked
!= 0)
1088 for (i
= 0; i
< panel
->count
; i
++)
1089 if (panel
->dir
.list
[i
].f
.marked
)
1090 return panel
->dir
.list
[i
].fname
;
1093 return panel
->dir
.list
[panel
->selected
].fname
;
1096 /* --------------------------------------------------------------------------------------------- */
1098 * panel_compute_totals:
1100 * compute the number of files and the number of bytes
1101 * used up by the whole selection, recursing directories
1102 * as required. In addition, it checks to see if it will
1103 * overwrite any files by doing the copy.
1106 static FileProgressStatus
1107 panel_compute_totals (const WPanel
* panel
, const void *ui
,
1108 compute_dir_size_callback cback
,
1109 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
1116 for (i
= 0; i
< panel
->count
; i
++)
1120 if (!panel
->dir
.list
[i
].f
.marked
)
1123 s
= &panel
->dir
.list
[i
].st
;
1125 if (S_ISDIR (s
->st_mode
))
1128 size_t subdir_count
= 0;
1129 uintmax_t subdir_bytes
= 0;
1130 FileProgressStatus status
;
1132 dir_name
= concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1134 status
= compute_dir_size (dir_name
, ui
, cback
,
1135 &subdir_count
, &subdir_bytes
, compute_symlinks
);
1138 if (status
!= FILE_CONT
)
1141 *ret_marked
+= subdir_count
;
1142 *ret_total
+= subdir_bytes
;
1147 *ret_total
+= (uintmax_t) s
->st_size
;
1154 /* --------------------------------------------------------------------------------------------- */
1156 /** Initialize variables for progress bars */
1157 static FileProgressStatus
1158 panel_operate_init_totals (FileOperation operation
,
1159 const WPanel
* panel
, const char *source
, FileOpContext
* ctx
)
1161 FileProgressStatus status
;
1163 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
)
1165 ComputeDirSizeUI
*ui
;
1167 ui
= compute_dir_size_create_ui ();
1170 status
= compute_dir_size (source
, ui
, compute_dir_size_update_ui
,
1171 &ctx
->progress_count
, &ctx
->progress_bytes
,
1174 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1175 &ctx
->progress_count
, &ctx
->progress_bytes
,
1178 compute_dir_size_destroy_ui (ui
);
1180 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1185 ctx
->progress_count
= panel
->marked
;
1186 ctx
->progress_bytes
= panel
->total
;
1187 ctx
->progress_totals_computed
= FALSE
;
1193 /* --------------------------------------------------------------------------------------------- */
1195 * Generate user prompt for panel operation.
1196 * single_source is the name if the source entry or NULL for multiple
1198 * src_stat is only used when single_source is not NULL.
1202 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1203 gboolean single_source
, const struct stat
*src_stat
)
1205 const char *sp
, *cp
;
1206 char format_string
[BUF_MEDIUM
];
1207 char *dp
= format_string
;
1208 gboolean build_question
= FALSE
;
1210 static gboolean i18n_flag
= FALSE
;
1215 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1216 op_names1
[i
] = Q_ (op_names1
[i
]);
1219 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1220 prompt_parts
[i
] = _(prompt_parts
[i
]);
1222 one_format
= _(one_format
);
1223 many_format
= _(many_format
);
1224 question_format
= _(question_format
);
1225 #endif /* ENABLE_NLS */
1229 sp
= single_source
? one_format
: many_format
;
1240 cp
= op_names1
[operation
];
1243 if (operation
== OP_DELETE
)
1246 build_question
= TRUE
;
1249 cp
= prompt_parts
[5];
1252 if (operation
== OP_DELETE
)
1255 build_question
= TRUE
;
1258 cp
= prompt_parts
[6];
1262 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1264 cp
= (panel
->marked
== panel
->dirs_marked
)
1266 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1287 char tmp
[BUF_MEDIUM
];
1289 memmove (tmp
, format_string
, sizeof (tmp
));
1290 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1293 return g_strdup (format_string
);
1296 /* --------------------------------------------------------------------------------------------- */
1298 #ifdef WITH_BACKGROUND
1300 end_bg_process (FileOpContext
* ctx
, enum OperationMode mode
)
1307 unregister_task_with_pid (pid
);
1308 /* file_op_context_destroy(ctx); */
1314 /* --------------------------------------------------------------------------------------------- */
1315 /*** public functions ****************************************************************************/
1316 /* --------------------------------------------------------------------------------------------- */
1319 copy_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
1320 const char *src_path
, const char *dst_path
)
1322 uid_t src_uid
= (uid_t
) - 1;
1323 gid_t src_gid
= (gid_t
) - 1;
1325 int src_desc
, dest_desc
= -1;
1326 int n_read
, n_written
;
1327 mode_t src_mode
= 0; /* The mode of the source file */
1328 struct stat sb
, sb2
;
1330 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1331 off_t file_size
= -1;
1332 FileProgressStatus return_status
, temp_status
;
1333 struct timeval tv_transfer_start
;
1334 dest_status_t dst_status
= DEST_NONE
;
1336 gboolean is_first_time
= TRUE
;
1338 /* FIXME: We should not be using global variables! */
1340 return_status
= FILE_RETRY
;
1342 file_progress_show_source (ctx
, src_path
);
1343 file_progress_show_target (ctx
, dst_path
);
1344 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1349 while (mc_stat (dst_path
, &sb2
) == 0)
1351 if (S_ISDIR (sb2
.st_mode
))
1354 return_status
= FILE_SKIPALL
;
1357 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1358 if (return_status
== FILE_SKIPALL
)
1359 ctx
->skip_all
= TRUE
;
1360 if (return_status
== FILE_RETRY
)
1363 return return_status
;
1369 while ((*ctx
->stat_func
) (src_path
, &sb
) != 0)
1372 return_status
= FILE_SKIPALL
;
1375 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1376 if (return_status
== FILE_SKIPALL
)
1377 ctx
->skip_all
= TRUE
;
1379 if (return_status
!= FILE_RETRY
)
1380 return return_status
;
1385 /* Destination already exists */
1386 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1387 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), src_path
, dst_path
);
1388 /* Should we replace destination? */
1389 if (tctx
->ask_overwrite
)
1392 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1393 if (return_status
!= FILE_CONT
)
1394 return return_status
;
1398 if (!ctx
->do_append
)
1400 /* Check the hardlinks */
1401 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_path
, dst_path
, &sb
))
1403 /* We have made a hardlink - no more processing is necessary */
1407 if (S_ISLNK (sb
.st_mode
))
1408 return make_symlink (ctx
, src_path
, dst_path
);
1410 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1411 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1413 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0
1416 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1417 if (return_status
== FILE_RETRY
)
1419 if (return_status
== FILE_SKIPALL
)
1420 ctx
->skip_all
= TRUE
;
1421 return return_status
;
1425 while (ctx
->preserve_uidgid
&& mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
) != 0
1428 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1429 if (temp_status
== FILE_SKIP
)
1431 if (temp_status
== FILE_SKIPALL
)
1432 ctx
->skip_all
= TRUE
;
1433 if (temp_status
!= FILE_RETRY
)
1437 while (ctx
->preserve
&& mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
) != 0
1440 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1441 if (temp_status
== FILE_SKIP
)
1443 if (temp_status
== FILE_SKIPALL
)
1444 ctx
->skip_all
= TRUE
;
1445 if (temp_status
!= FILE_RETRY
)
1453 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1455 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1457 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1458 if (return_status
== FILE_RETRY
)
1460 if (return_status
== FILE_SKIPALL
)
1461 ctx
->skip_all
= TRUE
;
1462 if (return_status
== FILE_SKIP
)
1465 return return_status
;
1468 if (ctx
->do_reget
!= 0)
1470 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1472 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1474 ctx
->do_append
= FALSE
;
1478 while (mc_fstat (src_desc
, &sb
) != 0)
1481 return_status
= FILE_SKIPALL
;
1484 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1485 if (return_status
== FILE_RETRY
)
1487 if (return_status
== FILE_SKIPALL
)
1488 ctx
->skip_all
= TRUE
;
1489 ctx
->do_append
= FALSE
;
1493 src_mode
= sb
.st_mode
;
1494 src_uid
= sb
.st_uid
;
1495 src_gid
= sb
.st_gid
;
1496 utb
.actime
= sb
.st_atime
;
1497 utb
.modtime
= sb
.st_mtime
;
1498 file_size
= sb
.st_size
;
1500 open_flags
= O_WRONLY
;
1503 if (ctx
->do_append
!= 0)
1504 open_flags
|= O_APPEND
;
1506 open_flags
|= O_CREAT
| O_TRUNC
;
1510 open_flags
|= O_CREAT
| O_EXCL
;
1513 while ((dest_desc
= mc_open (dst_path
, open_flags
, src_mode
)) < 0)
1515 if (errno
!= EEXIST
)
1518 return_status
= FILE_SKIPALL
;
1521 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1522 if (return_status
== FILE_RETRY
)
1524 if (return_status
== FILE_SKIPALL
)
1525 ctx
->skip_all
= TRUE
;
1526 ctx
->do_append
= FALSE
;
1531 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1533 appending
= ctx
->do_append
;
1534 ctx
->do_append
= FALSE
;
1536 /* Find out the optimal buffer size. */
1537 while (mc_fstat (dest_desc
, &sb
) != 0)
1540 return_status
= FILE_SKIPALL
;
1543 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1544 if (return_status
== FILE_RETRY
)
1546 if (return_status
== FILE_SKIPALL
)
1547 ctx
->skip_all
= TRUE
;
1554 errno
= vfs_preallocate (dest_desc
, file_size
, (ctx
->do_append
!= 0) ? sb
.st_size
: 0);
1559 return_status
= FILE_SKIPALL
;
1563 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1564 if (return_status
== FILE_RETRY
)
1566 if (return_status
== FILE_SKIPALL
)
1567 ctx
->skip_all
= TRUE
;
1569 mc_close (dest_desc
);
1571 mc_unlink (dst_path
);
1572 dst_status
= DEST_NONE
;
1576 ctx
->eta_secs
= 0.0;
1579 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1580 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1582 file_progress_show (ctx
, 1, 1, "", TRUE
);
1583 return_status
= check_progress_buttons (ctx
);
1586 if (return_status
!= FILE_CONT
)
1590 off_t n_read_total
= 0;
1591 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1592 int secs
, update_secs
;
1593 const char *stalled_msg
= "";
1595 tv_last_update
= tv_transfer_start
;
1602 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1605 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0 && !ctx
->skip_all
)
1607 return_status
= file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
1608 if (return_status
== FILE_RETRY
)
1610 if (return_status
== FILE_SKIPALL
)
1611 ctx
->skip_all
= TRUE
;
1617 gettimeofday (&tv_current
, NULL
);
1622 n_read_total
+= n_read
;
1624 /* Windows NT ftp servers report that files have no
1625 * permissions: -------, so if we happen to have actually
1626 * read something, we should fix the permissions.
1628 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1629 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1630 gettimeofday (&tv_last_input
, NULL
);
1633 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
&& !ctx
->skip_all
)
1637 n_read
-= n_written
;
1641 return_status
= file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1642 if (return_status
== FILE_SKIP
)
1644 if (return_status
== FILE_SKIPALL
)
1645 ctx
->skip_all
= TRUE
;
1646 if (return_status
!= FILE_RETRY
)
1651 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1653 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1654 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1656 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1658 copy_file_file_display_progress (tctx
, ctx
,
1660 tv_transfer_start
, file_size
, n_read_total
);
1661 tv_last_update
= tv_current
;
1663 is_first_time
= FALSE
;
1665 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1667 stalled_msg
= _("(stalled)");
1671 gboolean force_update
;
1674 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1676 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1678 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1679 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1682 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1687 return_status
= check_progress_buttons (ctx
);
1689 if (return_status
!= FILE_CONT
)
1697 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1700 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1702 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1703 if (temp_status
== FILE_RETRY
)
1705 if (temp_status
== FILE_ABORT
)
1706 return_status
= temp_status
;
1707 if (temp_status
== FILE_SKIPALL
)
1708 ctx
->skip_all
= TRUE
;
1712 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1714 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1715 if (temp_status
== FILE_RETRY
)
1717 if (temp_status
== FILE_SKIPALL
)
1718 ctx
->skip_all
= TRUE
;
1719 return_status
= temp_status
;
1723 if (dst_status
== DEST_SHORT
)
1725 /* Remove short file */
1727 result
= query_dialog (Q_ ("DialogTitle|Copy"),
1728 _("Incomplete file was retrieved. Keep it?"),
1729 D_ERROR
, 2, _("&Delete"), _("&Keep"));
1731 mc_unlink (dst_path
);
1733 else if (dst_status
== DEST_FULL
)
1735 /* Copy has succeeded */
1736 if (!appending
&& ctx
->preserve_uidgid
)
1738 while (mc_chown (dst_path
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1740 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1741 if (temp_status
== FILE_RETRY
)
1743 if (temp_status
== FILE_SKIPALL
)
1745 ctx
->skip_all
= TRUE
;
1746 return_status
= FILE_CONT
;
1748 if (temp_status
== FILE_SKIP
)
1749 return_status
= FILE_CONT
;
1758 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1760 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1761 if (temp_status
== FILE_RETRY
)
1763 if (temp_status
== FILE_SKIPALL
)
1765 ctx
->skip_all
= TRUE
;
1766 return_status
= FILE_CONT
;
1768 if (temp_status
== FILE_SKIP
)
1769 return_status
= FILE_CONT
;
1775 src_mode
= umask (-1);
1777 src_mode
= 0100666 & ~src_mode
;
1778 mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
));
1780 mc_utime (dst_path
, &utb
);
1784 if (return_status
== FILE_CONT
)
1785 return_status
= progress_update_one (tctx
, ctx
, file_size
);
1787 return return_status
;
1790 /* --------------------------------------------------------------------------------------------- */
1792 * I think these copy_*_* functions should have a return type.
1793 * anyway, this function *must* have two directories as arguments.
1795 /* FIXME: This function needs to check the return values of the
1799 copy_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *_d
,
1800 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, struct link
* parent_dirs
)
1802 struct dirent
*next
;
1803 struct stat buf
, cbuf
;
1805 char *dest_dir
= NULL
;
1806 FileProgressStatus return_status
= FILE_CONT
;
1813 /* First get the mode of the source dir */
1815 if ((*ctx
->stat_func
) (s
, &cbuf
) != 0)
1818 return_status
= FILE_SKIPALL
;
1821 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
1822 if (return_status
== FILE_RETRY
)
1823 goto retry_src_stat
;
1824 if (return_status
== FILE_SKIPALL
)
1825 ctx
->skip_all
= TRUE
;
1830 if (is_in_linklist (dest_dirs
, s
, &cbuf
))
1832 /* Don't copy a directory we created before (we don't want to copy
1833 infinitely if a directory is copied into itself) */
1834 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1835 return_status
= FILE_CONT
;
1839 /* Hmm, hardlink to directory??? - Norbert */
1840 /* FIXME: In this step we should do something
1841 in case the destination already exist */
1842 /* Check the hardlinks */
1843 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (s
, d
, &cbuf
))
1845 /* We have made a hardlink - no more processing is necessary */
1849 if (!S_ISDIR (cbuf
.st_mode
))
1852 return_status
= FILE_SKIPALL
;
1855 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
1856 if (return_status
== FILE_RETRY
)
1857 goto retry_src_stat
;
1858 if (return_status
== FILE_SKIPALL
)
1859 ctx
->skip_all
= TRUE
;
1864 if (is_in_linklist (parent_dirs
, s
, &cbuf
))
1866 /* we found a cyclic symbolic link */
1867 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
1868 return_status
= FILE_SKIP
;
1872 lp
= g_new (struct link
, 1);
1874 vfs_path_t
*vpath
= vfs_path_from_str (s
);
1875 lp
->vfs
= vfs_path_get_by_index (vpath
, -1)->class;
1876 vfs_path_free (vpath
);
1878 lp
->ino
= cbuf
.st_ino
;
1879 lp
->dev
= cbuf
.st_dev
;
1880 lp
->next
= parent_dirs
;
1884 /* Now, check if the dest dir exists, if not, create it. */
1885 if (mc_stat (d
, &buf
))
1887 /* Here the dir doesn't exist : make it ! */
1890 if (mc_rename (s
, d
) == 0)
1892 return_status
= FILE_CONT
;
1902 * If the destination directory exists, we want to copy the whole
1903 * directory, but we only want this to happen once.
1905 * Escape sequences added to the * to compiler warnings.
1906 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1907 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
1909 if (!S_ISDIR (buf
.st_mode
))
1912 return_status
= FILE_SKIPALL
;
1915 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
1916 if (return_status
== FILE_SKIPALL
)
1917 ctx
->skip_all
= TRUE
;
1918 if (return_status
== FILE_RETRY
)
1919 goto retry_dst_stat
;
1923 /* Dive into subdir if exists */
1924 if (toplevel
&& ctx
->dive_into_subdirs
)
1926 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
1935 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
))
1938 return_status
= FILE_SKIPALL
;
1941 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir
);
1942 if (return_status
== FILE_SKIPALL
)
1943 ctx
->skip_all
= TRUE
;
1945 if (return_status
!= FILE_RETRY
)
1949 lp
= g_new (struct link
, 1);
1950 mc_stat (dest_dir
, &buf
);
1952 vfs_path_t
*vpath
= vfs_path_from_str (dest_dir
);
1953 lp
->vfs
= vfs_path_get_by_index (vpath
, -1)->class;
1954 vfs_path_free (vpath
);
1956 lp
->ino
= buf
.st_ino
;
1957 lp
->dev
= buf
.st_dev
;
1958 lp
->next
= dest_dirs
;
1961 if (ctx
->preserve_uidgid
)
1963 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
1966 return_status
= FILE_SKIPALL
;
1970 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir
);
1971 if (return_status
== FILE_SKIPALL
)
1972 ctx
->skip_all
= TRUE
;
1974 if (return_status
!= FILE_RETRY
)
1980 /* open the source dir for reading */
1981 reading
= mc_opendir (s
);
1982 if (reading
== NULL
)
1985 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1989 * Now, we don't want '.' and '..' to be created / copied at any time
1991 if (!strcmp (next
->d_name
, "."))
1993 if (!strcmp (next
->d_name
, ".."))
1996 /* get the filename and add it to the src directory */
1997 path
= concat_dir_and_file (s
, next
->d_name
);
1999 (*ctx
->stat_func
) (path
, &buf
);
2000 if (S_ISDIR (buf
.st_mode
))
2004 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
2006 * From here, we just intend to recursively copy subdirs, not
2007 * the double functionality of copying different when the target
2008 * dir already exists. So, we give the recursive call the flag 0
2009 * meaning no toplevel.
2012 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2019 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
2020 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2023 if (do_delete
&& return_status
== FILE_CONT
)
2025 if (ctx
->erase_at_end
)
2027 static struct link
*tail
;
2028 size_t len
= strlen (path
);
2029 lp
= g_malloc (sizeof (struct link
) + len
);
2030 strncpy (lp
->name
, path
, len
+ 1);
2031 lp
->st_mode
= buf
.st_mode
;
2033 if (erase_list
!= NULL
)
2039 erase_list
= tail
= lp
;
2043 if (S_ISDIR (buf
.st_mode
))
2045 return_status
= erase_dir_iff_empty (ctx
, path
);
2048 return_status
= erase_file (tctx
, ctx
, path
);
2053 mc_closedir (reading
);
2057 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
2058 utb
.actime
= cbuf
.st_atime
;
2059 utb
.modtime
= cbuf
.st_mtime
;
2060 mc_utime (dest_dir
, &utb
);
2064 cbuf
.st_mode
= umask (-1);
2065 umask (cbuf
.st_mode
);
2066 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2067 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
2072 g_free (parent_dirs
);
2075 return return_status
;
2080 /* --------------------------------------------------------------------------------------------- */
2081 /* {{{ Move routines */
2084 move_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
2086 struct stat sbuf
, dbuf
, destbuf
;
2089 FileProgressStatus return_status
;
2090 gboolean move_over
= FALSE
;
2093 file_progress_show_source (ctx
, s
);
2094 file_progress_show_target (ctx
, d
);
2095 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2101 dstat_ok
= (mc_stat (d
, &dbuf
) == 0);
2103 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2104 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2107 destdir
= g_strdup (d
); /* destination doesn't exist */
2108 else if (!ctx
->dive_into_subdirs
)
2110 destdir
= g_strdup (d
);
2114 destdir
= concat_dir_and_file (d
, x_basename (s
));
2116 /* Check if the user inputted an existing dir */
2118 if (mc_stat (destdir
, &destbuf
) == 0)
2122 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
2124 if (return_status
!= FILE_CONT
)
2128 else if (ctx
->skip_all
)
2129 return_status
= FILE_SKIPALL
;
2132 if (S_ISDIR (destbuf
.st_mode
))
2133 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir
);
2135 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir
);
2136 if (return_status
== FILE_SKIPALL
)
2137 ctx
->skip_all
= TRUE
;
2138 if (return_status
== FILE_RETRY
)
2139 goto retry_dst_stat
;
2142 return return_status
;
2146 if (mc_rename (s
, destdir
) == 0)
2148 return_status
= FILE_CONT
;
2156 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2157 if (return_status
== FILE_SKIPALL
)
2158 ctx
->skip_all
= TRUE
;
2159 if (return_status
== FILE_RETRY
)
2164 /* Failed because of filesystem boundary -> copy dir instead */
2165 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
2167 if (return_status
!= FILE_CONT
)
2170 file_progress_show_source (ctx
, NULL
);
2171 file_progress_show (ctx
, 0, 0, "", FALSE
);
2173 return_status
= check_progress_buttons (ctx
);
2174 if (return_status
!= FILE_CONT
)
2178 if (ctx
->erase_at_end
)
2180 for (; erase_list
&& return_status
!= FILE_ABORT
;)
2182 if (S_ISDIR (erase_list
->st_mode
))
2184 return_status
= erase_dir_iff_empty (ctx
, erase_list
->name
);
2187 return_status
= erase_file (tctx
, ctx
, erase_list
->name
);
2189 erase_list
= erase_list
->next
;
2193 erase_dir_iff_empty (ctx
, s
);
2200 erase_list
= erase_list
->next
;
2203 return return_status
;
2208 /* --------------------------------------------------------------------------------------------- */
2209 /* {{{ Erase routines */
2212 erase_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
2214 FileProgressStatus error
;
2216 if (strcmp (s
, "..") == 0)
2219 if (strcmp (s
, ".") == 0)
2222 file_progress_show_deleting (ctx
, s
);
2223 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2227 /* The old way to detect a non empty directory was:
2228 error = my_rmdir (s);
2229 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2230 For the linux user space nfs server (nfs-server-2.2beta29-2)
2231 we would have to check also for EIO. I hope the new way is
2232 fool proof. (Norbert)
2234 error
= check_dir_is_empty (s
);
2237 error
= query_recursive (ctx
, s
);
2238 if (error
== FILE_CONT
)
2239 return recursive_erase (tctx
, ctx
, s
);
2244 while (my_rmdir (s
) == -1 && !ctx
->skip_all
)
2246 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
2247 if (error
!= FILE_RETRY
)
2256 /* --------------------------------------------------------------------------------------------- */
2257 /* {{{ Panel operate routines */
2260 compute_dir_size_create_ui (void)
2262 ComputeDirSizeUI
*ui
;
2264 const char *b_name
= N_("&Abort");
2270 ui
= g_new (ComputeDirSizeUI
, 1);
2272 ui
->dlg
= create_dlg (TRUE
, 0, 0, 8, COLS
/ 2, dialog_colors
, NULL
,
2273 NULL
, _("Directory scanning"), DLG_CENTER
);
2274 ui
->dirname
= label_new (3, 3, "");
2275 add_widget (ui
->dlg
, ui
->dirname
);
2277 add_widget (ui
->dlg
,
2278 button_new (5, (ui
->dlg
->cols
- strlen (b_name
)) / 2,
2279 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
2281 /* We will manage the dialog without any help,
2282 that's why we have to call init_dlg */
2288 /* --------------------------------------------------------------------------------------------- */
2291 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
2295 /* schedule to update passive panel */
2296 other_panel
->dirty
= 1;
2298 /* close and destroy dialog */
2299 dlg_run_done (ui
->dlg
);
2300 destroy_dlg (ui
->dlg
);
2305 /* --------------------------------------------------------------------------------------------- */
2308 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
2310 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
2317 label_set_text (this->dirname
, str_trunc (dirname
, this->dlg
->cols
- 6));
2319 event
.x
= -1; /* Don't show the GPM cursor */
2320 c
= tty_get_event (&event
, FALSE
, FALSE
);
2324 /* Reinitialize to avoid old values after events other than
2325 selecting a button */
2326 this->dlg
->ret_value
= FILE_CONT
;
2328 dlg_process_event (this->dlg
, c
, &event
);
2330 switch (this->dlg
->ret_value
)
2340 /* --------------------------------------------------------------------------------------------- */
2344 * Computes the number of bytes used by the files in a directory
2348 compute_dir_size (const char *dirname
, const void *ui
,
2349 compute_dir_size_callback cback
,
2350 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
2355 struct dirent
*dirent
;
2356 FileProgressStatus ret
= FILE_CONT
;
2358 if (!compute_symlinks
)
2360 res
= mc_lstat (dirname
, &s
);
2364 /* don't scan symlink to directory */
2365 if (S_ISLNK (s
.st_mode
))
2368 *ret_total
+= (uintmax_t) s
.st_size
;
2373 dir
= mc_opendir (dirname
);
2378 while ((dirent
= mc_readdir (dir
)) != NULL
)
2382 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
2384 if (ret
!= FILE_CONT
)
2387 if (strcmp (dirent
->d_name
, ".") == 0)
2389 if (strcmp (dirent
->d_name
, "..") == 0)
2392 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
2393 res
= mc_lstat (fullname
, &s
);
2401 if (S_ISDIR (s
.st_mode
))
2403 size_t subdir_count
= 0;
2404 uintmax_t subdir_bytes
= 0;
2407 compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
,
2410 if (ret
!= FILE_CONT
)
2416 *ret_marked
+= subdir_count
;
2417 *ret_total
+= subdir_bytes
;
2422 *ret_total
+= (uintmax_t) s
.st_size
;
2433 /* --------------------------------------------------------------------------------------------- */
2437 * Performs one of the operations on the selection on the source_panel
2438 * (copy, delete, move).
2440 * Returns TRUE if did change the directory
2441 * structure, Returns FALSE if user aborted
2443 * force_single forces operation on the current entry and affects
2444 * default destination. Current filename is used as default.
2448 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2450 WPanel
*panel
= (WPanel
*) source_panel
;
2451 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2452 || (get_current_type () == view_tree
);
2454 char *source
= NULL
;
2455 #ifdef WITH_FULL_PATHS
2456 char *source_with_path
= NULL
;
2458 #define source_with_path source
2459 #endif /* !WITH_FULL_PATHS */
2462 char *save_cwd
= NULL
, *save_dest
= NULL
;
2463 struct stat src_stat
;
2464 gboolean ret_val
= TRUE
;
2466 FileProgressStatus value
;
2468 FileOpTotalContext
*tctx
;
2470 gboolean do_bg
= FALSE
; /* do background operation? */
2472 static gboolean i18n_flag
= FALSE
;
2475 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
2476 op_names
[i
] = Q_ (op_names
[i
]);
2480 free_linklist (&linklist
);
2481 free_linklist (&dest_dirs
);
2486 source
= selection (panel
)->fname
;
2488 source
= panel_get_file (panel
);
2490 if (strcmp (source
, "..") == 0)
2492 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2496 /* Update stat to get actual info */
2497 if (mc_stat (source
, &src_stat
) != 0)
2499 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2500 path_trunc (source
, 30), unix_error_string (errno
));
2502 /* Directory was changed outside MC. Reload it forced */
2503 if (!panel
->is_panelized
)
2505 panel_update_flags_t flags
= UP_RELOAD
;
2507 /* don't update panelized panel */
2508 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2509 flags
|= UP_ONLY_CURRENT
;
2511 update_panels (flags
, UP_KEEPSEL
);
2518 ctx
= file_op_context_new (operation
);
2520 /* Show confirmation dialog */
2521 if (operation
!= OP_DELETE
)
2527 /* Forced single operations default to the original name */
2530 else if (get_other_type () == view_listing
)
2531 dest_dir
= other_panel
->cwd
;
2533 dest_dir
= panel
->cwd
;
2535 * Add trailing backslash only when do non-local ops.
2536 * It saves user from occasional file renames (when destination
2539 if (!force_single
&& dest_dir
[0] != '\0' && dest_dir
[strlen (dest_dir
) - 1] != PATH_SEP
)
2541 /* add trailing separator */
2542 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2547 dest_dir_
= g_strdup (dest_dir
);
2550 if (dest_dir_
== NULL
)
2556 /* Generate confirmation prompt */
2557 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2559 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2560 source
!= NULL
? (void *) source
2561 : (void *) &panel
->marked
, dest_dir_
, &do_bg
);
2566 if (dest
== NULL
|| dest
[0] == '\0')
2573 else if (confirm_delete
)
2576 char fmd_buf
[BUF_MEDIUM
];
2578 /* Generate confirmation prompt */
2579 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2582 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2585 const int fmd_xlen
= 64;
2586 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2587 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2595 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2604 tctx
= file_op_total_context_new ();
2605 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2608 filegui_dialog_type_t dialog_type
;
2610 if (operation
== OP_DELETE
)
2611 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2614 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2615 ? FILEGUI_DIALOG_MULTI_ITEM
: FILEGUI_DIALOG_ONE_ITEM
;
2617 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2618 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2621 /* Background also need ctx->ui, but not full */
2623 file_op_context_create_ui_without_init (ctx
, TRUE
, dialog_type
);
2625 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
2628 #ifdef WITH_BACKGROUND
2629 /* Did the user select to do a background operation? */
2634 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", panel
->cwd
, (char *) NULL
));
2636 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2638 /* If we are the parent */
2641 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
2642 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
2643 /* file_op_context_destroy (ctx); */
2647 #endif /* WITH_BACKGROUND */
2649 /* Initialize things */
2650 /* We do not want to trash cache every time file is
2651 created/touched. However, this will make our cache contain
2653 if ((dest
!= NULL
) && (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2654 save_dest
= g_strdup (dest
);
2656 if ((panel
->cwd
[0] != '\0') && (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2657 save_cwd
= g_strdup (panel
->cwd
);
2659 /* Now, let's do the job */
2661 /* This code is only called by the tree and panel code */
2664 /* We now have ETA in all cases */
2666 /* One file: FIXME mc_chdir will take user out of any vfs */
2667 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2672 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2673 chdir_retcode
= mc_chdir (vpath
);
2674 vfs_path_free (vpath
);
2675 if (chdir_retcode
< 0)
2682 /* The source and src_stat variables have been initialized before */
2683 #ifdef WITH_FULL_PATHS
2684 if (g_path_is_absolute (source
))
2685 source_with_path
= g_strdup (source
);
2687 source_with_path
= mc_build_filename (panel
->cwd
, source
, (char *) NULL
);
2688 #endif /* WITH_FULL_PATHS */
2690 if (panel_operate_init_totals (operation
, panel
, source_with_path
, ctx
) == FILE_CONT
)
2692 if (operation
== OP_DELETE
)
2694 if (S_ISDIR (src_stat
.st_mode
))
2695 value
= erase_dir (tctx
, ctx
, source_with_path
);
2697 value
= erase_file (tctx
, ctx
, source_with_path
);
2701 temp
= transform_source (ctx
, source_with_path
);
2703 value
= transform_error
;
2706 char *repl_dest
, *temp2
;
2708 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2709 temp2
= concat_dir_and_file (repl_dest
, temp
);
2718 /* we use file_mask_op_follow_links only with OP_COPY */
2719 ctx
->stat_func (source_with_path
, &src_stat
);
2721 if (S_ISDIR (src_stat
.st_mode
))
2722 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, dest
,
2723 TRUE
, FALSE
, FALSE
, NULL
);
2725 value
= copy_file_file (tctx
, ctx
, source_with_path
, dest
);
2729 if (S_ISDIR (src_stat
.st_mode
))
2730 value
= move_dir_dir (tctx
, ctx
, source_with_path
, dest
);
2732 value
= move_file_file (tctx
, ctx
, source_with_path
, dest
);
2736 /* Unknown file operation */
2740 } /* Copy or move operation */
2742 if ((value
== FILE_CONT
) && !force_single
)
2743 unmark_files (panel
);
2750 /* Check destination for copy or move operation */
2751 while (operation
!= OP_DELETE
)
2754 struct stat dst_stat
;
2756 dst_result
= mc_stat (dest
, &dst_stat
);
2758 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2762 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2766 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
)
2768 /* Loop for every file, perform the actual copy operation */
2769 for (i
= 0; i
< panel
->count
; i
++)
2771 if (!panel
->dir
.list
[i
].f
.marked
)
2772 continue; /* Skip the unmarked ones */
2774 source
= panel
->dir
.list
[i
].fname
;
2775 src_stat
= panel
->dir
.list
[i
].st
;
2777 #ifdef WITH_FULL_PATHS
2778 g_free (source_with_path
);
2779 if (g_path_is_absolute (source
))
2780 source_with_path
= g_strdup (source
);
2782 source_with_path
= mc_build_filename (panel
->cwd
, source
, (char *) NULL
);
2783 #endif /* WITH_FULL_PATHS */
2785 if (operation
== OP_DELETE
)
2787 if (S_ISDIR (src_stat
.st_mode
))
2788 value
= erase_dir (tctx
, ctx
, source_with_path
);
2790 value
= erase_file (tctx
, ctx
, source_with_path
);
2794 temp
= transform_source (ctx
, source_with_path
);
2797 value
= transform_error
;
2800 char *temp2
, *temp3
, *repl_dest
;
2802 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2803 temp2
= concat_dir_and_file (repl_dest
, temp
);
2806 temp3
= source_with_path
;
2807 source_with_path
= strutils_shell_unescape (source_with_path
);
2810 temp2
= strutils_shell_unescape (temp2
);
2816 /* we use file_mask_op_follow_links only with OP_COPY */
2817 ctx
->stat_func (source_with_path
, &src_stat
);
2818 if (S_ISDIR (src_stat
.st_mode
))
2819 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, temp2
,
2820 TRUE
, FALSE
, FALSE
, NULL
);
2822 value
= copy_file_file (tctx
, ctx
, source_with_path
, temp2
);
2823 free_linklist (&dest_dirs
);
2827 if (S_ISDIR (src_stat
.st_mode
))
2828 value
= move_dir_dir (tctx
, ctx
, source_with_path
, temp2
);
2830 value
= move_file_file (tctx
, ctx
, source_with_path
, temp2
);
2834 /* Unknown file operation */
2840 } /* Copy or move operation */
2842 if (value
== FILE_ABORT
)
2845 if (value
== FILE_CONT
)
2846 do_file_mark (panel
, i
, 0);
2848 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
2850 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2851 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
2854 if (operation
!= OP_DELETE
)
2855 file_progress_show (ctx
, 0, 0, "", FALSE
);
2857 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2861 } /* Loop for every file */
2863 } /* Many entries */
2867 if (save_cwd
!= NULL
)
2869 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2873 if (save_dest
!= NULL
)
2875 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2879 free_linklist (&linklist
);
2880 free_linklist (&dest_dirs
);
2881 #ifdef WITH_FULL_PATHS
2882 g_free (source_with_path
);
2883 #endif /* WITH_FULL_PATHS */
2885 g_free (ctx
->dest_mask
);
2886 ctx
->dest_mask
= NULL
;
2888 #ifdef WITH_BACKGROUND
2889 /* Let our parent know we are saying bye bye */
2890 if (mc_global
.we_are_background
)
2892 int cur_pid
= getpid ();
2893 /* Send pid to parent with child context, it is fork and
2894 don't modify real parent ctx */
2896 parent_call ((void *) end_bg_process
, ctx
, 0);
2901 #endif /* WITH_BACKGROUND */
2903 file_op_total_context_destroy (tctx
);
2905 file_op_context_destroy (ctx
);
2912 /* --------------------------------------------------------------------------------------------- */
2913 /* {{{ Query/status report routines */
2914 /** Report error with one file */
2916 file_error (const char *format
, const char *file
)
2918 char buf
[BUF_MEDIUM
];
2920 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
2922 return do_file_error (buf
);
2925 /* --------------------------------------------------------------------------------------------- */
2928 Cause emacs to enter folding mode for this file: