4 Copyright (C) 1994-2016
5 Free Software Foundation, Inc.
8 Janne Kukonlehto, 1994, 1995
9 Fred Leeflang, 1994, 1995
10 Miguel de Icaza, 1994, 1995, 1996
11 Jakub Jelinek, 1995, 1996
14 Andrew Borodin <aborodin@vmail.ru>, 2011-2014
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
46 /** \file src/filemanager/file.c
47 * \brief Source: file management
50 /* {{{ Include files */
59 #include <sys/types.h>
63 #include "lib/global.h"
64 #include "lib/tty/tty.h"
65 #include "lib/tty/key.h"
66 #include "lib/search.h"
67 #include "lib/strescape.h"
68 #include "lib/strutil.h"
70 #include "lib/vfs/vfs.h"
71 #include "lib/widget.h"
73 #include "src/setup.h"
74 #ifdef ENABLE_BACKGROUND
75 #include "src/background.h" /* do_background() */
78 /* Needed for current_panel, other_panel and WTree */
83 #include "midnight.h" /* current_panel */
84 #include "layout.h" /* rotate_dash() */
85 #include "ioblksize.h" /* io_blksize() */
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 */
113 const struct vfs_class
*vfs
;
118 vfs_path_t
*src_vpath
;
119 vfs_path_t
*dst_vpath
;
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 * %n - the '\n' symbol to form two-line prompt for delete or space for other operations
154 /* xgettext:no-c-format */
155 static const char *one_format
= N_("%o %f%n\"%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:")
169 /*** file scope variables ************************************************************************/
171 /* the hard link cache */
172 static GSList
*linklist
= NULL
;
174 /* the files-to-be-erased list */
175 static GSList
*erase_list
= NULL
;
178 * In copy_dir_dir we use two additional single linked lists: The first -
179 * variable name 'parent_dirs' - holds information about already copied
180 * directories and is used to detect cyclic symbolic links.
181 * The second ('dest_dirs' below) holds information about just created
182 * target directories and is used to detect when an directory is copied
183 * into itself (we don't want to copy infinitly).
184 * Both lists don't use the linkcount and name structure members of struct
187 static GSList
*dest_dirs
= NULL
;
189 static FileProgressStatus transform_error
= FILE_CONT
;
191 /* --------------------------------------------------------------------------------------------- */
192 /*** file scope functions ************************************************************************/
193 /* --------------------------------------------------------------------------------------------- */
196 dirsize_status_locate_buttons (dirsize_status_msg_t
* dsm
)
198 status_msg_t
*sm
= STATUS_MSG (dsm
);
199 Widget
*wd
= WIDGET (sm
->dlg
);
205 if (!dsm
->allow_skip
)
207 /* single button: "Abort" */
208 x
+= (wd
->cols
- dsm
->abort_button
->cols
) / 2;
209 widget_set_size (dsm
->abort_button
, y
, x
,
210 dsm
->abort_button
->lines
, dsm
->abort_button
->cols
);
214 /* two buttons: "Abort" and "Skip" */
217 cols
= dsm
->abort_button
->cols
+ dsm
->skip_button
->cols
+ 1;
218 x
+= (wd
->cols
- cols
) / 2;
219 widget_set_size (dsm
->abort_button
, y
, x
, dsm
->abort_button
->lines
,
220 dsm
->abort_button
->cols
);
221 x
+= dsm
->abort_button
->cols
+ 1;
222 widget_set_size (dsm
->skip_button
, y
, x
, dsm
->skip_button
->lines
, dsm
->skip_button
->cols
);
226 /* --------------------------------------------------------------------------------------------- */
229 transform_source (file_op_context_t
* ctx
, const vfs_path_t
* source_vpath
)
232 const char *fnsource
;
234 s
= g_strdup (vfs_path_as_str (source_vpath
));
236 /* We remove \n from the filename since regex routines would use \n as an anchor */
237 /* this is just to be allowed to maniupulate file names with \n on it */
238 for (q
= s
; *q
!= '\0'; q
++)
242 fnsource
= x_basename (s
);
244 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
246 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
247 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
249 if (ctx
->search_handle
->error_str
!= NULL
)
250 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
253 transform_error
= FILE_ABORT
;
259 transform_error
= FILE_SKIP
;
266 /* --------------------------------------------------------------------------------------------- */
269 free_link (void *data
)
271 struct link
*lp
= (struct link
*) data
;
273 vfs_path_free (lp
->src_vpath
);
274 vfs_path_free (lp
->dst_vpath
);
278 /* --------------------------------------------------------------------------------------------- */
281 free_linklist (GSList
* lp
)
283 g_slist_free_full (lp
, free_link
);
288 /* --------------------------------------------------------------------------------------------- */
291 is_in_linklist (const GSList
* lp
, const vfs_path_t
* vpath
, const struct stat
*sb
)
293 const struct vfs_class
*class;
294 ino_t ino
= sb
->st_ino
;
295 dev_t dev
= sb
->st_dev
;
297 class = vfs_path_get_last_path_vfs (vpath
);
299 for (; lp
!= NULL
; lp
= (const GSList
*) g_slist_next (lp
))
301 const struct link
*lnk
= (const struct link
*) lp
->data
;
303 if (lnk
->vfs
== class && lnk
->ino
== ino
&& lnk
->dev
== dev
)
309 /* --------------------------------------------------------------------------------------------- */
311 * Check and made hardlink
313 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
314 * and a hardlink was successfully made
318 check_hardlinks (const vfs_path_t
* src_vpath
, const vfs_path_t
* dst_vpath
, struct stat
*pstat
)
323 const struct vfs_class
*my_vfs
;
324 ino_t ino
= pstat
->st_ino
;
325 dev_t dev
= pstat
->st_dev
;
326 struct stat link_stat
;
328 if ((vfs_file_class_flags (src_vpath
) & VFSF_NOLINKS
) != 0)
331 my_vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
333 for (lp
= linklist
; lp
!= NULL
; lp
= g_slist_next (lp
))
335 lnk
= (struct link
*) lp
->data
;
337 if (lnk
->vfs
== my_vfs
&& lnk
->ino
== ino
&& lnk
->dev
== dev
)
339 const struct vfs_class
*lp_name_class
;
342 lp_name_class
= vfs_path_get_last_path_vfs (lnk
->src_vpath
);
343 stat_result
= mc_stat (lnk
->src_vpath
, &link_stat
);
345 if (stat_result
== 0 && link_stat
.st_ino
== ino
346 && link_stat
.st_dev
== dev
&& lp_name_class
== my_vfs
)
348 const struct vfs_class
*p_class
, *dst_name_class
;
350 dst_name_class
= vfs_path_get_last_path_vfs (dst_vpath
);
351 p_class
= vfs_path_get_last_path_vfs (lnk
->dst_vpath
);
353 if (dst_name_class
== p_class
&&
354 mc_stat (lnk
->dst_vpath
, &link_stat
) == 0 &&
355 mc_link (lnk
->dst_vpath
, dst_vpath
) == 0)
359 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
364 lnk
= g_try_new0 (struct link
, 1);
370 lnk
->src_vpath
= vfs_path_clone (src_vpath
);
371 lnk
->dst_vpath
= vfs_path_clone (dst_vpath
);
372 linklist
= g_slist_prepend (linklist
, lnk
);
378 /* --------------------------------------------------------------------------------------------- */
380 * Duplicate the contents of the symbolic link src_path in dst_path.
381 * Try to make a stable symlink if the option "stable symlink" was
382 * set in the file mask dialog.
383 * If dst_path is an existing symlink it will be deleted silently
384 * (upper levels take already care of existing files at dst_path).
387 static FileProgressStatus
388 make_symlink (file_op_context_t
* ctx
, const char *src_path
, const char *dst_path
)
390 char link_target
[MC_MAXPATHLEN
];
392 FileProgressStatus return_status
;
394 vfs_path_t
*src_vpath
;
395 vfs_path_t
*dst_vpath
;
396 gboolean dst_is_symlink
;
397 vfs_path_t
*link_target_vpath
= NULL
;
399 src_vpath
= vfs_path_from_str (src_path
);
400 dst_vpath
= vfs_path_from_str (dst_path
);
401 dst_is_symlink
= (mc_lstat (dst_vpath
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
404 len
= mc_readlink (src_vpath
, link_target
, MC_MAXPATHLEN
- 1);
408 return_status
= FILE_SKIPALL
;
411 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
412 if (return_status
== FILE_SKIPALL
)
413 ctx
->skip_all
= TRUE
;
414 if (return_status
== FILE_RETRY
)
415 goto retry_src_readlink
;
419 link_target
[len
] = 0;
421 if (ctx
->stable_symlinks
&& !(vfs_file_is_local (src_vpath
) && vfs_file_is_local (dst_vpath
)))
423 message (D_ERROR
, MSG_ERROR
,
424 _("Cannot make stable symlinks across"
425 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
426 ctx
->stable_symlinks
= FALSE
;
429 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
431 const char *r
= strrchr (src_path
, PATH_SEP
);
438 p
= g_strndup (src_path
, r
- src_path
+ 1);
439 if (g_path_is_absolute (dst_path
))
440 q
= vfs_path_from_str_flags (dst_path
, VPF_NO_CANON
);
442 q
= vfs_path_build_filename (p
, dst_path
, (char *) NULL
);
444 if (vfs_path_tokens_count (q
) > 1)
447 vfs_path_t
*tmp_vpath1
, *tmp_vpath2
;
449 tmp_vpath1
= vfs_path_vtokens_get (q
, -1, 1);
450 s
= g_strconcat (p
, link_target
, (char *) NULL
);
452 g_strlcpy (link_target
, s
, sizeof (link_target
));
454 tmp_vpath2
= vfs_path_from_str (link_target
);
455 s
= diff_two_paths (tmp_vpath1
, tmp_vpath2
);
456 vfs_path_free (tmp_vpath1
);
457 vfs_path_free (tmp_vpath2
);
460 g_strlcpy (link_target
, s
, sizeof (link_target
));
469 link_target_vpath
= vfs_path_from_str_flags (link_target
, VPF_NO_CANON
);
472 if (mc_symlink (link_target_vpath
, dst_vpath
) == 0)
475 return_status
= FILE_CONT
;
479 * if dst_exists, it is obvious that this had failed.
480 * We can delete the old symlink and try again...
482 if (dst_is_symlink
&& mc_unlink (dst_vpath
) == 0
483 && mc_symlink (link_target_vpath
, dst_vpath
) == 0)
486 return_status
= FILE_CONT
;
491 return_status
= FILE_SKIPALL
;
494 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
495 if (return_status
== FILE_SKIPALL
)
496 ctx
->skip_all
= TRUE
;
497 if (return_status
== FILE_RETRY
)
498 goto retry_dst_symlink
;
502 vfs_path_free (src_vpath
);
503 vfs_path_free (dst_vpath
);
504 vfs_path_free (link_target_vpath
);
505 return return_status
;
508 /* --------------------------------------------------------------------------------------------- */
510 * do_compute_dir_size:
512 * Computes the number of bytes used by the files in a directory
515 static FileProgressStatus
516 do_compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* dsm
,
517 size_t * dir_count
, size_t * ret_marked
, uintmax_t * ret_total
,
518 gboolean compute_symlinks
)
520 static guint64 timestamp
= 0;
521 /* update with 25 FPS rate */
522 static const guint64 delay
= G_USEC_PER_SEC
/ 25;
524 status_msg_t
*sm
= STATUS_MSG (dsm
);
528 struct dirent
*dirent
;
529 FileProgressStatus ret
= FILE_CONT
;
531 if (!compute_symlinks
)
533 res
= mc_lstat (dirname_vpath
, &s
);
537 /* don't scan symlink to directory */
538 if (S_ISLNK (s
.st_mode
))
541 *ret_total
+= (uintmax_t) s
.st_size
;
548 dir
= mc_opendir (dirname_vpath
);
552 while (ret
== FILE_CONT
&& (dirent
= mc_readdir (dir
)) != NULL
)
554 vfs_path_t
*tmp_vpath
;
556 if (DIR_IS_DOT (dirent
->d_name
) || DIR_IS_DOTDOT (dirent
->d_name
))
559 tmp_vpath
= vfs_path_append_new (dirname_vpath
, dirent
->d_name
, (char *) NULL
);
561 res
= mc_lstat (tmp_vpath
, &s
);
564 if (S_ISDIR (s
.st_mode
))
566 do_compute_dir_size (tmp_vpath
, dsm
, dir_count
, ret_marked
, ret_total
,
573 *ret_total
+= (uintmax_t) s
.st_size
;
576 if (ret
== FILE_CONT
&& sm
->update
!= NULL
&& mc_time_elapsed (×tamp
, delay
))
578 dsm
->dirname_vpath
= tmp_vpath
;
579 dsm
->dir_count
= *dir_count
;
580 dsm
->total_size
= *ret_total
;
581 ret
= sm
->update (sm
);
585 vfs_path_free (tmp_vpath
);
592 /* --------------------------------------------------------------------------------------------- */
594 static FileProgressStatus
595 progress_update_one (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, off_t add
)
597 struct timeval tv_current
;
598 static struct timeval tv_start
= { 0, 0 };
600 tctx
->progress_count
++;
601 tctx
->progress_bytes
+= (uintmax_t) add
;
603 if (tv_start
.tv_sec
== 0)
605 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
607 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
608 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
610 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
612 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
613 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
615 tv_start
.tv_sec
= tv_current
.tv_sec
;
618 return check_progress_buttons (ctx
);
621 /* --------------------------------------------------------------------------------------------- */
623 static FileProgressStatus
624 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
628 const char *head_msg
;
630 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
632 msg
= g_strdup_printf (fmt
, a
, b
);
633 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
637 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
640 /* --------------------------------------------------------------------------------------------- */
642 static FileProgressStatus
643 warn_same_file (const char *fmt
, const char *a
, const char *b
)
645 #ifdef ENABLE_BACKGROUND
650 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
, const char *a
, const char *b
);
654 pntr
.f
= real_warn_same_file
;
656 if (mc_global
.we_are_background
)
657 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
659 return real_warn_same_file (Foreground
, fmt
, a
, b
);
662 /* --------------------------------------------------------------------------------------------- */
663 /* {{{ Query/status report routines */
665 static FileProgressStatus
666 real_do_file_error (enum OperationMode mode
, const char *error
)
671 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
673 query_dialog (msg
, error
, D_ERROR
, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
695 /* --------------------------------------------------------------------------------------------- */
697 static FileProgressStatus
698 real_query_recursive (file_op_context_t
* ctx
, enum OperationMode mode
, const char *s
)
700 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
705 msg
= mode
== Foreground
706 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
707 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
708 text
= g_strdup_printf (msg
, path_trunc (s
, 30));
713 ctx
->recursive_result
=
714 query_dialog (op_names
[OP_DELETE
], text
, D_ERROR
, 5, _("&Yes"), _("&No"), _("A&ll"),
715 _("Non&e"), _("&Abort"));
718 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
722 switch (ctx
->recursive_result
)
725 case RECURSIVE_ALWAYS
:
729 case RECURSIVE_NEVER
:
732 case RECURSIVE_ABORT
:
738 /* --------------------------------------------------------------------------------------------- */
740 #ifdef ENABLE_BACKGROUND
741 static FileProgressStatus
742 do_file_error (const char *str
)
748 FileProgressStatus (*f
) (enum OperationMode
, const char *);
752 pntr
.f
= real_do_file_error
;
754 if (mc_global
.we_are_background
)
755 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
757 return real_do_file_error (Foreground
, str
);
760 /* --------------------------------------------------------------------------------------------- */
762 static FileProgressStatus
763 query_recursive (file_op_context_t
* ctx
, const char *s
)
769 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *);
773 pntr
.f
= real_query_recursive
;
775 if (mc_global
.we_are_background
)
776 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
778 return real_query_recursive (ctx
, Foreground
, s
);
781 /* --------------------------------------------------------------------------------------------- */
783 static FileProgressStatus
784 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
785 struct stat
*_d_stat
)
791 FileProgressStatus (*f
) (file_op_context_t
*, enum OperationMode
, const char *,
792 struct stat
*, struct stat
*);
796 pntr
.f
= file_progress_real_query_replace
;
798 if (mc_global
.we_are_background
)
799 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
800 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
802 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
806 /* --------------------------------------------------------------------------------------------- */
808 static FileProgressStatus
809 do_file_error (const char *str
)
811 return real_do_file_error (Foreground
, str
);
814 /* --------------------------------------------------------------------------------------------- */
816 static FileProgressStatus
817 query_recursive (file_op_context_t
* ctx
, const char *s
)
819 return real_query_recursive (ctx
, Foreground
, s
);
822 /* --------------------------------------------------------------------------------------------- */
824 static FileProgressStatus
825 query_replace (file_op_context_t
* ctx
, const char *destname
, struct stat
*_s_stat
,
826 struct stat
*_d_stat
)
828 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
831 #endif /* !ENABLE_BACKGROUND */
833 /* --------------------------------------------------------------------------------------------- */
834 /** Report error with two files */
836 static FileProgressStatus
837 files_error (const char *format
, const char *file1
, const char *file2
)
839 char buf
[BUF_MEDIUM
];
840 char *nfile1
= g_strdup (path_trunc (file1
, 15));
841 char *nfile2
= g_strdup (path_trunc (file2
, 15));
843 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
848 return do_file_error (buf
);
853 /* --------------------------------------------------------------------------------------------- */
856 copy_file_file_display_progress (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
857 struct timeval tv_current
, struct timeval tv_transfer_start
,
858 off_t file_size
, off_t n_read_total
)
862 /* 1. Update rotating dash after some time */
866 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
868 if (n_read_total
== 0)
872 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
873 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
876 /* 4. Compute BPS rate */
877 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
878 if (ctx
->bps_time
< 1)
880 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
882 /* 5. Compute total ETA and BPS */
883 if (ctx
->progress_bytes
!= 0)
885 uintmax_t remain_bytes
;
887 remain_bytes
= ctx
->progress_bytes
- tctx
->copied_bytes
;
890 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
895 tctx
->bps
= tctx
->copied_bytes
/ total_secs
;
896 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
899 /* broken on lot of little files */
901 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
902 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
907 /* --------------------------------------------------------------------------------------------- */
909 /* {{{ Move routines */
910 static FileProgressStatus
911 move_file_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
,
914 struct stat src_stats
, dst_stats
;
915 FileProgressStatus return_status
= FILE_CONT
;
916 gboolean copy_done
= FALSE
;
917 gboolean old_ask_overwrite
;
918 vfs_path_t
*src_vpath
, *dst_vpath
;
920 src_vpath
= vfs_path_from_str (s
);
921 dst_vpath
= vfs_path_from_str (d
);
923 file_progress_show_source (ctx
, src_vpath
);
924 file_progress_show_target (ctx
, dst_vpath
);
926 if (check_progress_buttons (ctx
) == FILE_ABORT
)
928 return_status
= FILE_ABORT
;
934 while (mc_lstat (src_vpath
, &src_stats
) != 0)
936 /* Source doesn't exist */
938 return_status
= FILE_SKIPALL
;
941 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
942 if (return_status
== FILE_SKIPALL
)
943 ctx
->skip_all
= TRUE
;
946 if (return_status
!= FILE_RETRY
)
950 if (mc_lstat (dst_vpath
, &dst_stats
) == 0)
952 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
954 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
958 if (S_ISDIR (dst_stats
.st_mode
))
960 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
962 return_status
= FILE_SKIP
;
966 if (confirm_overwrite
)
968 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
969 if (return_status
!= FILE_CONT
)
972 /* Ok to overwrite */
977 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
979 return_status
= make_symlink (ctx
, s
, d
);
980 if (return_status
== FILE_CONT
)
981 goto retry_src_remove
;
985 if (mc_rename (src_vpath
, dst_vpath
) == 0)
987 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
992 /* Comparison to EXDEV seems not to work in nfs if you're moving from
993 one nfs to the same, but on the server it is on two different
994 filesystems. Then nfs returns EIO instead of EXDEV.
995 Hope it will not hurt if we always in case of error try to copy/delete. */
997 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1002 return_status
= FILE_SKIPALL
;
1005 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
1006 if (return_status
== FILE_SKIPALL
)
1007 ctx
->skip_all
= TRUE
;
1008 if (return_status
== FILE_RETRY
)
1016 /* Failed because filesystem boundary -> copy the file instead */
1017 old_ask_overwrite
= tctx
->ask_overwrite
;
1018 tctx
->ask_overwrite
= FALSE
;
1019 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
1020 tctx
->ask_overwrite
= old_ask_overwrite
;
1021 if (return_status
!= FILE_CONT
)
1026 file_progress_show_source (ctx
, NULL
);
1027 file_progress_show (ctx
, 0, 0, "", FALSE
);
1029 return_status
= check_progress_buttons (ctx
);
1030 if (return_status
!= FILE_CONT
)
1035 if (mc_unlink (src_vpath
) != 0 && !ctx
->skip_all
)
1037 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
1038 if (return_status
== FILE_RETRY
)
1039 goto retry_src_remove
;
1040 if (return_status
== FILE_SKIPALL
)
1041 ctx
->skip_all
= TRUE
;
1046 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
);
1049 vfs_path_free (src_vpath
);
1050 vfs_path_free (dst_vpath
);
1052 return return_status
;
1057 /* --------------------------------------------------------------------------------------------- */
1058 /* {{{ Erase routines */
1059 /** Don't update progress status if progress_count==NULL */
1061 static FileProgressStatus
1062 erase_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1066 file_progress_show_deleting (ctx
, vfs_path_as_str (vpath
), &tctx
->progress_count
);
1067 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1068 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1073 if (tctx
->progress_count
!= 0 && mc_lstat (vpath
, &buf
) != 0)
1075 /* ignore, most likely the mc_unlink fails, too */
1079 while (mc_unlink (vpath
) != 0 && !ctx
->skip_all
)
1083 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), vfs_path_as_str (vpath
));
1084 if (return_status
== FILE_ABORT
)
1085 return return_status
;
1086 if (return_status
== FILE_RETRY
)
1088 if (return_status
== FILE_SKIPALL
)
1089 ctx
->skip_all
= TRUE
;
1093 if (tctx
->progress_count
== 0)
1096 return check_progress_buttons (ctx
);
1099 /* --------------------------------------------------------------------------------------------- */
1102 Recursive remove of files
1104 skip ->warn every level, gets default
1105 skipall->remove as much as possible
1107 static FileProgressStatus
1108 recursive_erase (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* vpath
)
1110 struct dirent
*next
;
1113 FileProgressStatus return_status
= FILE_CONT
;
1115 reading
= mc_opendir (vpath
);
1116 if (reading
== NULL
)
1119 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1121 vfs_path_t
*tmp_vpath
;
1124 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
1127 tmp_vpath
= vfs_path_append_new (vpath
, next
->d_name
, (char *) NULL
);
1128 if (mc_lstat (tmp_vpath
, &buf
) != 0)
1130 mc_closedir (reading
);
1131 vfs_path_free (tmp_vpath
);
1134 if (S_ISDIR (buf
.st_mode
))
1135 return_status
= recursive_erase (tctx
, ctx
, tmp_vpath
);
1137 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
1138 vfs_path_free (tmp_vpath
);
1140 mc_closedir (reading
);
1142 if (return_status
== FILE_ABORT
)
1145 s
= vfs_path_as_str (vpath
);
1147 file_progress_show_deleting (ctx
, s
, NULL
);
1148 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1149 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1154 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1156 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1157 if (return_status
== FILE_RETRY
)
1159 if (return_status
== FILE_ABORT
)
1161 if (return_status
== FILE_SKIPALL
)
1162 ctx
->skip_all
= TRUE
;
1166 return return_status
;
1169 /* --------------------------------------------------------------------------------------------- */
1170 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1171 in the directory path points to, 0 else. */
1174 check_dir_is_empty (const vfs_path_t
* vpath
)
1180 dir
= mc_opendir (vpath
);
1184 for (d
= mc_readdir (dir
); d
!= NULL
; d
= mc_readdir (dir
))
1185 if (!DIR_IS_DOT (d
->d_name
) && !DIR_IS_DOTDOT (d
->d_name
))
1195 /* --------------------------------------------------------------------------------------------- */
1197 static FileProgressStatus
1198 erase_dir_iff_empty (file_op_context_t
* ctx
, const vfs_path_t
* vpath
, size_t count
)
1200 FileProgressStatus error
= FILE_CONT
;
1203 s
= vfs_path_as_str (vpath
);
1205 file_progress_show_deleting (ctx
, s
, NULL
);
1206 file_progress_show_count (ctx
, count
, ctx
->progress_count
);
1207 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1212 if (check_dir_is_empty (vpath
) == 1) /* not empty or error */
1214 while (my_rmdir (s
) != 0 && !ctx
->skip_all
)
1216 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
1217 if (error
== FILE_SKIPALL
)
1218 ctx
->skip_all
= TRUE
;
1219 if (error
!= FILE_RETRY
)
1229 /* --------------------------------------------------------------------------------------------- */
1230 /* {{{ Panel operate routines */
1233 * Return currently selected entry name or the name of the first marked
1234 * entry if there is one.
1238 panel_get_file (WPanel
* panel
)
1240 if (get_current_type () == view_tree
)
1243 const vfs_path_t
*selected_name
;
1245 tree
= (WTree
*) get_panel_widget (get_current_index ());
1246 selected_name
= tree_selected_name (tree
);
1247 return vfs_path_as_str (selected_name
);
1250 if (panel
->marked
!= 0)
1254 for (i
= 0; i
< panel
->dir
.len
; i
++)
1255 if (panel
->dir
.list
[i
].f
.marked
)
1256 return panel
->dir
.list
[i
].fname
;
1259 return panel
->dir
.list
[panel
->selected
].fname
;
1262 /* --------------------------------------------------------------------------------------------- */
1264 * panel_compute_totals:
1266 * compute the number of files and the number of bytes
1267 * used up by the whole selection, recursing directories
1268 * as required. In addition, it checks to see if it will
1269 * overwrite any files by doing the copy.
1272 static FileProgressStatus
1273 panel_compute_totals (const WPanel
* panel
, dirsize_status_msg_t
* sm
, size_t * ret_count
,
1274 uintmax_t * ret_total
, gboolean compute_symlinks
)
1277 size_t dir_count
= 0;
1279 for (i
= 0; i
< panel
->dir
.len
; i
++)
1283 if (!panel
->dir
.list
[i
].f
.marked
)
1286 s
= &panel
->dir
.list
[i
].st
;
1288 if (S_ISDIR (s
->st_mode
))
1291 FileProgressStatus status
;
1293 p
= vfs_path_append_new (panel
->cwd_vpath
, panel
->dir
.list
[i
].fname
, (char *) NULL
);
1294 status
= compute_dir_size (p
, sm
, &dir_count
, ret_count
, ret_total
, compute_symlinks
);
1297 if (status
!= FILE_CONT
)
1303 *ret_total
+= (uintmax_t) s
->st_size
;
1310 /* --------------------------------------------------------------------------------------------- */
1312 /** Initialize variables for progress bars */
1313 static FileProgressStatus
1314 panel_operate_init_totals (const WPanel
* panel
, const char *source
, file_op_context_t
* ctx
,
1315 filegui_dialog_type_t dialog_type
)
1317 FileProgressStatus status
;
1319 #ifdef ENABLE_BACKGROUND
1320 if (mc_global
.we_are_background
)
1324 if (verbose
&& file_op_compute_totals
)
1326 dirsize_status_msg_t dsm
;
1328 memset (&dsm
, 0, sizeof (dsm
));
1329 dsm
.allow_skip
= TRUE
;
1330 status_msg_init (STATUS_MSG (&dsm
), _("Directory scanning"), 0, dirsize_status_init_cb
,
1331 dirsize_status_update_cb
, dirsize_status_deinit_cb
);
1333 ctx
->progress_count
= 0;
1334 ctx
->progress_bytes
= 0;
1337 status
= panel_compute_totals (panel
, &dsm
, &ctx
->progress_count
, &ctx
->progress_bytes
,
1342 size_t dir_count
= 0;
1344 p
= vfs_path_from_str (source
);
1345 status
= compute_dir_size (p
, &dsm
, &dir_count
, &ctx
->progress_count
,
1346 &ctx
->progress_bytes
, ctx
->follow_links
);
1350 status_msg_deinit (STATUS_MSG (&dsm
));
1352 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1354 if (status
== FILE_SKIP
)
1360 ctx
->progress_count
= panel
->marked
;
1361 ctx
->progress_bytes
= panel
->total
;
1362 ctx
->progress_totals_computed
= FALSE
;
1365 file_op_context_create_ui (ctx
, TRUE
, dialog_type
);
1370 /* --------------------------------------------------------------------------------------------- */
1372 * Generate user prompt for panel operation.
1373 * src_stat must be not NULL for single source, and NULL for multiple sources
1377 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1378 const struct stat
*src_stat
)
1381 char *format_string
;
1384 static gboolean i18n_flag
= FALSE
;
1389 for (i
= G_N_ELEMENTS (op_names1
); i
-- != 0;)
1390 op_names1
[i
] = Q_ (op_names1
[i
]);
1393 for (i
= G_N_ELEMENTS (prompt_parts
); i
-- != 0;)
1394 prompt_parts
[i
] = _(prompt_parts
[i
]);
1396 one_format
= _(one_format
);
1397 many_format
= _(many_format
);
1398 #endif /* ENABLE_NLS */
1402 /* Possible prompts:
1404 * "Copy file \"%s\" with source mask:"
1405 * "Copy %d files with source mask:"
1406 * "Copy directory \"%s\" with source mask:"
1407 * "Copy %d directories with source mask:"
1408 * "Copy %d files/directories with source mask:"
1410 * "Move file \"%s\" with source mask:"
1411 * "Move %d files with source mask:"
1412 * "Move directory \"%s\" with source mask:"
1413 * "Move %d directories with source mask:"
1414 * "Move %d files/directories with source mask:"
1416 * "Delete file \"%s\"?"
1417 * "Delete %d files?"
1418 * "Delete directory \"%s\"?"
1419 * "Delete %d directories?"
1420 * "Delete %d files/directories?"
1423 cp
= (src_stat
!= NULL
? one_format
: many_format
);
1425 /* 1. Substitute %o */
1426 format_string
= str_replace_all (cp
, "%o", op_names1
[(int) operation
]);
1428 /* 2. Substitute %n */
1429 cp
= operation
== OP_DELETE
? "\n" : " ";
1431 format_string
= str_replace_all (sp
, "%n", cp
);
1434 /* 3. Substitute %f */
1435 if (src_stat
!= NULL
)
1436 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1437 else if (panel
->marked
== panel
->dirs_marked
)
1438 cp
= prompt_parts
[3];
1440 cp
= panel
->dirs_marked
!= 0 ? prompt_parts
[4] : prompt_parts
[1];
1443 format_string
= str_replace_all (sp
, "%f", cp
);
1446 /* 4. Substitute %m */
1447 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1449 format_string
= str_replace_all (sp
, "%m", cp
);
1452 return format_string
;
1455 /* --------------------------------------------------------------------------------------------- */
1457 #ifdef ENABLE_BACKGROUND
1459 end_bg_process (file_op_context_t
* ctx
, enum OperationMode mode
)
1466 unregister_task_with_pid (pid
);
1467 /* file_op_context_destroy(ctx); */
1473 /* --------------------------------------------------------------------------------------------- */
1474 /*** public functions ****************************************************************************/
1475 /* --------------------------------------------------------------------------------------------- */
1478 copy_file_file (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
,
1479 const char *src_path
, const char *dst_path
)
1481 uid_t src_uid
= (uid_t
) (-1);
1482 gid_t src_gid
= (gid_t
) (-1);
1484 int src_desc
, dest_desc
= -1;
1485 mode_t src_mode
= 0; /* The mode of the source file */
1486 struct stat src_stat
, dst_stat
;
1488 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1489 off_t file_size
= -1;
1490 FileProgressStatus return_status
, temp_status
;
1491 struct timeval tv_transfer_start
;
1492 dest_status_t dst_status
= DEST_NONE
;
1494 vfs_path_t
*src_vpath
= NULL
, *dst_vpath
= NULL
;
1497 /* FIXME: We should not be using global variables! */
1499 return_status
= FILE_RETRY
;
1501 dst_vpath
= vfs_path_from_str (dst_path
);
1502 src_vpath
= vfs_path_from_str (src_path
);
1504 file_progress_show_source (ctx
, src_vpath
);
1505 file_progress_show_target (ctx
, dst_vpath
);
1507 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1509 return_status
= FILE_ABORT
;
1515 while (mc_stat (dst_vpath
, &dst_stat
) == 0)
1517 if (S_ISDIR (dst_stat
.st_mode
))
1520 return_status
= FILE_SKIPALL
;
1523 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1524 if (return_status
== FILE_SKIPALL
)
1525 ctx
->skip_all
= TRUE
;
1526 if (return_status
== FILE_RETRY
)
1536 while ((*ctx
->stat_func
) (src_vpath
, &src_stat
) != 0)
1539 return_status
= FILE_SKIPALL
;
1542 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1543 if (return_status
== FILE_SKIPALL
)
1544 ctx
->skip_all
= TRUE
;
1547 if (return_status
!= FILE_RETRY
)
1553 /* Destination already exists */
1554 if (src_stat
.st_dev
== dst_stat
.st_dev
&& src_stat
.st_ino
== dst_stat
.st_ino
)
1556 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1557 src_path
, dst_path
);
1561 /* Should we replace destination? */
1562 if (tctx
->ask_overwrite
)
1565 return_status
= query_replace (ctx
, dst_path
, &src_stat
, &dst_stat
);
1566 if (return_status
!= FILE_CONT
)
1571 if (!ctx
->do_append
)
1573 /* Check the hardlinks */
1574 if (!ctx
->follow_links
&& src_stat
.st_nlink
> 1
1575 && check_hardlinks (src_vpath
, dst_vpath
, &src_stat
))
1577 /* We have made a hardlink - no more processing is necessary */
1578 return_status
= FILE_CONT
;
1582 if (S_ISLNK (src_stat
.st_mode
))
1584 return_status
= make_symlink (ctx
, src_path
, dst_path
);
1588 if (S_ISCHR (src_stat
.st_mode
) || S_ISBLK (src_stat
.st_mode
) || S_ISFIFO (src_stat
.st_mode
)
1589 || S_ISNAM (src_stat
.st_mode
) || S_ISSOCK (src_stat
.st_mode
))
1591 while (mc_mknod (dst_vpath
, src_stat
.st_mode
& ctx
->umask_kill
, src_stat
.st_rdev
) < 0
1594 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1595 if (return_status
== FILE_RETRY
)
1597 if (return_status
== FILE_SKIPALL
)
1598 ctx
->skip_all
= TRUE
;
1603 while (ctx
->preserve_uidgid
1604 && mc_chown (dst_vpath
, src_stat
.st_uid
, src_stat
.st_gid
) != 0 && !ctx
->skip_all
)
1606 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1607 if (temp_status
== FILE_SKIP
)
1609 if (temp_status
== FILE_SKIPALL
)
1610 ctx
->skip_all
= TRUE
;
1611 if (temp_status
!= FILE_RETRY
)
1613 return_status
= temp_status
;
1618 while (ctx
->preserve
&& mc_chmod (dst_vpath
, src_stat
.st_mode
& ctx
->umask_kill
) != 0
1621 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1622 if (temp_status
== FILE_SKIP
)
1624 if (temp_status
== FILE_SKIPALL
)
1625 ctx
->skip_all
= TRUE
;
1626 if (temp_status
!= FILE_RETRY
)
1628 return_status
= temp_status
;
1633 return_status
= FILE_CONT
;
1638 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1640 while ((src_desc
= mc_open (src_vpath
, O_RDONLY
| O_LINEAR
)) < 0 && !ctx
->skip_all
)
1642 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1643 if (return_status
== FILE_RETRY
)
1645 if (return_status
== FILE_SKIPALL
)
1646 ctx
->skip_all
= TRUE
;
1647 if (return_status
== FILE_SKIP
)
1649 ctx
->do_append
= FALSE
;
1653 if (ctx
->do_reget
!= 0)
1655 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1657 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1659 ctx
->do_append
= FALSE
;
1663 while (mc_fstat (src_desc
, &src_stat
) != 0)
1666 return_status
= FILE_SKIPALL
;
1669 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1670 if (return_status
== FILE_RETRY
)
1672 if (return_status
== FILE_SKIPALL
)
1673 ctx
->skip_all
= TRUE
;
1674 ctx
->do_append
= FALSE
;
1679 src_mode
= src_stat
.st_mode
;
1680 src_uid
= src_stat
.st_uid
;
1681 src_gid
= src_stat
.st_gid
;
1682 utb
.actime
= src_stat
.st_atime
;
1683 utb
.modtime
= src_stat
.st_mtime
;
1684 file_size
= src_stat
.st_size
;
1686 open_flags
= O_WRONLY
;
1690 open_flags
|= O_APPEND
;
1692 open_flags
|= O_CREAT
| O_TRUNC
;
1696 open_flags
|= O_CREAT
| O_EXCL
;
1699 while ((dest_desc
= mc_open (dst_vpath
, open_flags
, src_mode
)) < 0)
1701 if (errno
!= EEXIST
)
1704 return_status
= FILE_SKIPALL
;
1707 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1708 if (return_status
== FILE_RETRY
)
1710 if (return_status
== FILE_SKIPALL
)
1711 ctx
->skip_all
= TRUE
;
1712 ctx
->do_append
= FALSE
;
1717 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1719 appending
= ctx
->do_append
;
1720 ctx
->do_append
= FALSE
;
1722 /* Find out the optimal buffer size. */
1723 while (mc_fstat (dest_desc
, &dst_stat
) != 0)
1726 return_status
= FILE_SKIPALL
;
1729 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1730 if (return_status
== FILE_RETRY
)
1732 if (return_status
== FILE_SKIPALL
)
1733 ctx
->skip_all
= TRUE
;
1738 /* try preallocate space; if fail, try copy anyway */
1739 while (vfs_preallocate (dest_desc
, file_size
, appending
? dst_stat
.st_size
: 0) != 0)
1743 /* cannot allocate, start the file copying anyway */
1744 return_status
= FILE_CONT
;
1749 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path
);
1751 if (return_status
== FILE_SKIPALL
)
1752 ctx
->skip_all
= TRUE
;
1754 if (ctx
->skip_all
|| return_status
== FILE_SKIP
)
1756 /* skip the space allocation error, start file copying */
1757 return_status
= FILE_CONT
;
1761 if (return_status
== FILE_ABORT
)
1763 mc_close (dest_desc
);
1765 mc_unlink (dst_vpath
);
1766 dst_status
= DEST_NONE
;
1770 /* return_status == FILE_RETRY -- try allocate space again */
1773 ctx
->eta_secs
= 0.0;
1776 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1777 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1779 file_progress_show (ctx
, 1, 1, "", TRUE
);
1780 return_status
= check_progress_buttons (ctx
);
1783 if (return_status
== FILE_CONT
)
1786 off_t n_read_total
= 0;
1787 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1788 int secs
, update_secs
;
1789 const char *stalled_msg
= "";
1790 gboolean is_first_time
= TRUE
;
1792 tv_last_update
= tv_transfer_start
;
1794 bufsize
= io_blksize (dst_stat
);
1795 buf
= g_malloc (bufsize
);
1799 ssize_t n_read
= -1, n_written
;
1802 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0) == 0)
1803 while ((n_read
= mc_read (src_desc
, buf
, bufsize
)) < 0 && !ctx
->skip_all
)
1805 return_status
= file_error (_("Cannot read source file \"%s\"\n%s"), src_path
);
1806 if (return_status
== FILE_RETRY
)
1808 if (return_status
== FILE_SKIPALL
)
1809 ctx
->skip_all
= TRUE
;
1816 gettimeofday (&tv_current
, NULL
);
1822 n_read_total
+= n_read
;
1824 /* Windows NT ftp servers report that files have no
1825 * permissions: -------, so if we happen to have actually
1826 * read something, we should fix the permissions.
1828 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1829 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1830 gettimeofday (&tv_last_input
, NULL
);
1833 while ((n_written
= mc_write (dest_desc
, t
, (size_t) n_read
)) < n_read
)
1835 gboolean write_errno_nospace
;
1839 n_read
-= n_written
;
1844 write_errno_nospace
= (n_written
< 0 && errno
== ENOSPC
);
1847 return_status
= FILE_SKIPALL
;
1850 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1852 if (return_status
== FILE_SKIP
)
1854 if (write_errno_nospace
)
1858 if (return_status
== FILE_SKIPALL
)
1860 ctx
->skip_all
= TRUE
;
1861 if (write_errno_nospace
)
1864 if (return_status
!= FILE_RETRY
)
1869 tctx
->copied_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
1871 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1872 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1874 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1876 copy_file_file_display_progress (tctx
, ctx
,
1878 tv_transfer_start
, file_size
, n_read_total
);
1879 tv_last_update
= tv_current
;
1881 is_first_time
= FALSE
;
1883 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1885 stalled_msg
= _("(stalled)");
1889 gboolean force_update
;
1892 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1894 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1896 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1897 file_progress_show_total (tctx
, ctx
, tctx
->copied_bytes
, force_update
);
1900 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1905 return_status
= check_progress_buttons (ctx
);
1907 if (return_status
!= FILE_CONT
)
1914 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1920 rotate_dash (FALSE
);
1921 while (src_desc
!= -1 && mc_close (src_desc
) < 0 && !ctx
->skip_all
)
1923 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1924 if (temp_status
== FILE_RETRY
)
1926 if (temp_status
== FILE_ABORT
)
1927 return_status
= temp_status
;
1928 if (temp_status
== FILE_SKIPALL
)
1929 ctx
->skip_all
= TRUE
;
1933 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0 && !ctx
->skip_all
)
1935 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1936 if (temp_status
== FILE_RETRY
)
1938 if (temp_status
== FILE_SKIPALL
)
1939 ctx
->skip_all
= TRUE
;
1940 return_status
= temp_status
;
1944 if (dst_status
== DEST_SHORT
)
1946 /* Query to remove short file */
1947 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"),
1948 D_ERROR
, 2, _("&Delete"), _("&Keep")) == 0)
1949 mc_unlink (dst_vpath
);
1951 else if (dst_status
== DEST_FULL
)
1953 /* Copy has succeeded */
1954 if (!appending
&& ctx
->preserve_uidgid
)
1956 while (mc_chown (dst_vpath
, src_uid
, src_gid
) != 0 && !ctx
->skip_all
)
1958 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1959 if (temp_status
== FILE_RETRY
)
1961 if (temp_status
== FILE_SKIPALL
)
1963 ctx
->skip_all
= TRUE
;
1964 return_status
= FILE_CONT
;
1966 if (temp_status
== FILE_SKIP
)
1967 return_status
= FILE_CONT
;
1976 while (mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
)) != 0 && !ctx
->skip_all
)
1978 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1979 if (temp_status
== FILE_RETRY
)
1981 if (temp_status
== FILE_SKIPALL
)
1983 ctx
->skip_all
= TRUE
;
1984 return_status
= FILE_CONT
;
1986 if (temp_status
== FILE_SKIP
)
1987 return_status
= FILE_CONT
;
1991 else if (!dst_exists
)
1993 src_mode
= umask (-1);
1995 src_mode
= 0100666 & ~src_mode
;
1996 mc_chmod (dst_vpath
, (src_mode
& ctx
->umask_kill
));
1998 mc_utime (dst_vpath
, &utb
);
2002 if (return_status
== FILE_CONT
)
2003 return_status
= progress_update_one (tctx
, ctx
, file_size
);
2006 vfs_path_free (src_vpath
);
2007 vfs_path_free (dst_vpath
);
2008 return return_status
;
2011 /* --------------------------------------------------------------------------------------------- */
2013 * I think these copy_*_* functions should have a return type.
2014 * anyway, this function *must* have two directories as arguments.
2016 /* FIXME: This function needs to check the return values of the
2020 copy_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
,
2021 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, GSList
* parent_dirs
)
2023 struct dirent
*next
;
2024 struct stat buf
, cbuf
;
2026 FileProgressStatus return_status
= FILE_CONT
;
2028 vfs_path_t
*src_vpath
, *dst_vpath
;
2029 gboolean do_mkdir
= TRUE
;
2031 src_vpath
= vfs_path_from_str (s
);
2032 dst_vpath
= vfs_path_from_str (d
);
2034 /* First get the mode of the source dir */
2037 if ((*ctx
->stat_func
) (src_vpath
, &cbuf
) != 0)
2040 return_status
= FILE_SKIPALL
;
2043 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
2044 if (return_status
== FILE_RETRY
)
2045 goto retry_src_stat
;
2046 if (return_status
== FILE_SKIPALL
)
2047 ctx
->skip_all
= TRUE
;
2052 if (is_in_linklist (dest_dirs
, src_vpath
, &cbuf
))
2054 /* Don't copy a directory we created before (we don't want to copy
2055 infinitely if a directory is copied into itself) */
2056 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2057 return_status
= FILE_CONT
;
2061 /* Hmm, hardlink to directory??? - Norbert */
2062 /* FIXME: In this step we should do something
2063 in case the destination already exist */
2064 /* Check the hardlinks */
2065 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (src_vpath
, dst_vpath
, &cbuf
))
2067 /* We have made a hardlink - no more processing is necessary */
2071 if (!S_ISDIR (cbuf
.st_mode
))
2074 return_status
= FILE_SKIPALL
;
2077 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
2078 if (return_status
== FILE_RETRY
)
2079 goto retry_src_stat
;
2080 if (return_status
== FILE_SKIPALL
)
2081 ctx
->skip_all
= TRUE
;
2086 if (is_in_linklist (parent_dirs
, src_vpath
, &cbuf
))
2088 /* we found a cyclic symbolic link */
2089 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
2090 return_status
= FILE_SKIP
;
2094 lp
= g_new0 (struct link
, 1);
2095 lp
->vfs
= vfs_path_get_by_index (src_vpath
, -1)->class;
2096 lp
->ino
= cbuf
.st_ino
;
2097 lp
->dev
= cbuf
.st_dev
;
2098 parent_dirs
= g_slist_prepend (parent_dirs
, lp
);
2101 /* Now, check if the dest dir exists, if not, create it. */
2102 if (mc_stat (dst_vpath
, &buf
) != 0)
2104 /* Here the dir doesn't exist : make it ! */
2105 if (move_over
&& mc_rename (src_vpath
, dst_vpath
) == 0)
2107 return_status
= FILE_CONT
;
2114 * If the destination directory exists, we want to copy the whole
2115 * directory, but we only want this to happen once.
2117 * Escape sequences added to the * to compiler warnings.
2118 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2119 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2121 if (!S_ISDIR (buf
.st_mode
))
2124 return_status
= FILE_SKIPALL
;
2127 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
2128 if (return_status
== FILE_SKIPALL
)
2129 ctx
->skip_all
= TRUE
;
2130 if (return_status
== FILE_RETRY
)
2131 goto retry_dst_stat
;
2135 /* Dive into subdir if exists */
2136 if (toplevel
&& ctx
->dive_into_subdirs
)
2141 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), (char *) NULL
);
2142 vfs_path_free (tmp
);
2149 d
= vfs_path_as_str (dst_vpath
);
2153 while (my_mkdir (dst_vpath
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
) != 0)
2156 return_status
= FILE_SKIPALL
;
2159 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), d
);
2160 if (return_status
== FILE_SKIPALL
)
2161 ctx
->skip_all
= TRUE
;
2163 if (return_status
!= FILE_RETRY
)
2167 lp
= g_new0 (struct link
, 1);
2168 mc_stat (dst_vpath
, &buf
);
2169 lp
->vfs
= vfs_path_get_by_index (dst_vpath
, -1)->class;
2170 lp
->ino
= buf
.st_ino
;
2171 lp
->dev
= buf
.st_dev
;
2172 dest_dirs
= g_slist_prepend (dest_dirs
, lp
);
2175 if (ctx
->preserve_uidgid
)
2177 while (mc_chown (dst_vpath
, cbuf
.st_uid
, cbuf
.st_gid
) != 0)
2180 return_status
= FILE_SKIPALL
;
2183 return_status
= file_error (_("Cannot chown target directory \"%s\"\n%s"), d
);
2184 if (return_status
== FILE_SKIPALL
)
2185 ctx
->skip_all
= TRUE
;
2187 if (return_status
!= FILE_RETRY
)
2192 /* open the source dir for reading */
2193 reading
= mc_opendir (src_vpath
);
2194 if (reading
== NULL
)
2197 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
2200 vfs_path_t
*tmp_vpath
;
2203 * Now, we don't want '.' and '..' to be created / copied at any time
2205 if (DIR_IS_DOT (next
->d_name
) || DIR_IS_DOTDOT (next
->d_name
))
2208 /* get the filename and add it to the src directory */
2209 path
= mc_build_filename (s
, next
->d_name
, (char *) NULL
);
2210 tmp_vpath
= vfs_path_from_str (path
);
2212 (*ctx
->stat_func
) (tmp_vpath
, &buf
);
2213 if (S_ISDIR (buf
.st_mode
))
2217 mdpath
= mc_build_filename (d
, next
->d_name
, (char *) NULL
);
2219 * From here, we just intend to recursively copy subdirs, not
2220 * the double functionality of copying different when the target
2221 * dir already exists. So, we give the recursive call the flag 0
2222 * meaning no toplevel.
2225 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
2232 dest_file
= mc_build_filename (d
, x_basename (path
), (char *) NULL
);
2233 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
2239 if (do_delete
&& return_status
== FILE_CONT
)
2241 if (ctx
->erase_at_end
)
2243 lp
= g_new0 (struct link
, 1);
2244 lp
->src_vpath
= tmp_vpath
;
2245 lp
->st_mode
= buf
.st_mode
;
2246 erase_list
= g_slist_append (erase_list
, lp
);
2249 else if (S_ISDIR (buf
.st_mode
))
2250 return_status
= erase_dir_iff_empty (ctx
, tmp_vpath
, tctx
->progress_count
);
2252 return_status
= erase_file (tctx
, ctx
, tmp_vpath
);
2254 vfs_path_free (tmp_vpath
);
2256 mc_closedir (reading
);
2262 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2263 utb
.actime
= cbuf
.st_atime
;
2264 utb
.modtime
= cbuf
.st_mtime
;
2265 mc_utime (dst_vpath
, &utb
);
2269 cbuf
.st_mode
= umask (-1);
2270 umask (cbuf
.st_mode
);
2271 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
2272 mc_chmod (dst_vpath
, cbuf
.st_mode
& ctx
->umask_kill
);
2276 free_link (parent_dirs
->data
);
2277 g_slist_free_1 (parent_dirs
);
2279 vfs_path_free (src_vpath
);
2280 vfs_path_free (dst_vpath
);
2281 return return_status
;
2286 /* --------------------------------------------------------------------------------------------- */
2287 /* {{{ Move routines */
2290 move_dir_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const char *s
, const char *d
)
2292 struct stat sbuf
, dbuf
;
2293 FileProgressStatus return_status
= FILE_CONT
;
2294 gboolean move_over
= FALSE
;
2296 vfs_path_t
*src_vpath
, *dst_vpath
;
2298 src_vpath
= vfs_path_from_str (s
);
2299 dst_vpath
= vfs_path_from_str (d
);
2301 file_progress_show_source (ctx
, src_vpath
);
2302 file_progress_show_target (ctx
, dst_vpath
);
2304 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2306 return_status
= FILE_ABORT
;
2312 mc_stat (src_vpath
, &sbuf
);
2314 dstat_ok
= (mc_stat (dst_vpath
, &dbuf
) == 0);
2315 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
2317 return_status
= warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
2322 ; /* destination doesn't exist */
2323 else if (!ctx
->dive_into_subdirs
)
2330 dst_vpath
= vfs_path_append_new (dst_vpath
, x_basename (s
), (char *) NULL
);
2331 vfs_path_free (tmp
);
2334 d
= vfs_path_as_str (dst_vpath
);
2336 /* Check if the user inputted an existing dir */
2338 if (mc_stat (dst_vpath
, &dbuf
) == 0)
2342 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, TRUE
, TRUE
, NULL
);
2344 if (return_status
!= FILE_CONT
)
2348 else if (ctx
->skip_all
)
2349 return_status
= FILE_SKIPALL
;
2352 if (S_ISDIR (dbuf
.st_mode
))
2353 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), d
);
2355 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), d
);
2356 if (return_status
== FILE_SKIPALL
)
2357 ctx
->skip_all
= TRUE
;
2358 if (return_status
== FILE_RETRY
)
2359 goto retry_dst_stat
;
2366 if (mc_rename (src_vpath
, dst_vpath
) == 0)
2368 return_status
= FILE_CONT
;
2376 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
2377 if (return_status
== FILE_SKIPALL
)
2378 ctx
->skip_all
= TRUE
;
2379 if (return_status
== FILE_RETRY
)
2384 /* Failed because of filesystem boundary -> copy dir instead */
2385 return_status
= copy_dir_dir (tctx
, ctx
, s
, d
, FALSE
, FALSE
, TRUE
, NULL
);
2387 if (return_status
!= FILE_CONT
)
2390 file_progress_show_source (ctx
, NULL
);
2391 file_progress_show_target (ctx
, NULL
);
2392 file_progress_show (ctx
, 0, 0, "", FALSE
);
2394 return_status
= check_progress_buttons (ctx
);
2395 if (return_status
!= FILE_CONT
)
2399 if (ctx
->erase_at_end
)
2401 /* Reset progress count before delete to avoid counting files twice */
2402 tctx
->progress_count
= tctx
->prev_progress_count
;
2404 while (erase_list
!= NULL
&& return_status
!= FILE_ABORT
)
2406 struct link
*lp
= (struct link
*) erase_list
->data
;
2408 if (S_ISDIR (lp
->st_mode
))
2409 return_status
= erase_dir_iff_empty (ctx
, lp
->src_vpath
, tctx
->progress_count
);
2411 return_status
= erase_file (tctx
, ctx
, lp
->src_vpath
);
2413 erase_list
= g_slist_remove (erase_list
, lp
);
2417 /* Save progress counter before move next directory */
2418 tctx
->prev_progress_count
= tctx
->progress_count
;
2420 erase_dir_iff_empty (ctx
, src_vpath
, tctx
->progress_count
);
2423 erase_list
= free_linklist (erase_list
);
2425 vfs_path_free (src_vpath
);
2426 vfs_path_free (dst_vpath
);
2427 return return_status
;
2432 /* --------------------------------------------------------------------------------------------- */
2433 /* {{{ Erase routines */
2436 erase_dir (file_op_total_context_t
* tctx
, file_op_context_t
* ctx
, const vfs_path_t
* s_vpath
)
2438 FileProgressStatus error
;
2440 file_progress_show_deleting (ctx
, vfs_path_as_str (s_vpath
), NULL
);
2441 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2442 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2447 /* The old way to detect a non empty directory was:
2448 error = my_rmdir (s);
2449 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2450 For the linux user space nfs server (nfs-server-2.2beta29-2)
2451 we would have to check also for EIO. I hope the new way is
2452 fool proof. (Norbert)
2454 error
= check_dir_is_empty (s_vpath
);
2457 error
= query_recursive (ctx
, vfs_path_as_str (s_vpath
));
2458 if (error
== FILE_CONT
)
2459 error
= recursive_erase (tctx
, ctx
, s_vpath
);
2463 while (my_rmdir (vfs_path_as_str (s_vpath
)) == -1 && !ctx
->skip_all
)
2465 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), vfs_path_as_str (s_vpath
));
2466 if (error
!= FILE_RETRY
)
2475 /* --------------------------------------------------------------------------------------------- */
2476 /* {{{ Panel operate routines */
2479 dirsize_status_init_cb (status_msg_t
* sm
)
2481 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
2482 Widget
*wd
= WIDGET (sm
->dlg
);
2484 const char *b1_name
= N_("&Abort");
2485 const char *b2_name
= N_("&Skip");
2486 int b_width
, ui_width
;
2489 b1_name
= _(b1_name
);
2490 b2_name
= _(b2_name
);
2493 b_width
= str_term_width1 (b1_name
) + 4;
2494 if (dsm
->allow_skip
)
2495 b_width
+= str_term_width1 (b2_name
) + 4 + 1;
2497 ui_width
= MAX (COLS
/ 2, b_width
+ 6);
2498 dsm
->dirname
= label_new (2, 3, "");
2499 add_widget (sm
->dlg
, dsm
->dirname
);
2500 dsm
->count_size
= label_new (3, 3, "");
2501 add_widget (sm
->dlg
, dsm
->count_size
);
2502 add_widget (sm
->dlg
, hline_new (4, -1, -1));
2504 dsm
->abort_button
= WIDGET (button_new (5, 3, FILE_ABORT
, NORMAL_BUTTON
, b1_name
, NULL
));
2505 add_widget (sm
->dlg
, dsm
->abort_button
);
2506 if (dsm
->allow_skip
)
2508 dsm
->skip_button
= WIDGET (button_new (5, 3, FILE_SKIP
, NORMAL_BUTTON
, b2_name
, NULL
));
2509 add_widget (sm
->dlg
, dsm
->skip_button
);
2510 dlg_select_widget (dsm
->skip_button
);
2513 widget_set_size (wd
, wd
->y
, wd
->x
, 8, ui_width
);
2514 dirsize_status_locate_buttons (dsm
);
2517 /* --------------------------------------------------------------------------------------------- */
2520 dirsize_status_update_cb (status_msg_t
* sm
)
2522 dirsize_status_msg_t
*dsm
= (dirsize_status_msg_t
*) sm
;
2523 Widget
*wd
= WIDGET (sm
->dlg
);
2525 /* update second (longer label) */
2526 label_set_textv (dsm
->count_size
, _("Directories: %zd, total size: %s"),
2527 dsm
->dir_count
, size_trunc_sep (dsm
->total_size
, panels_options
.kilobyte_si
));
2529 /* enlarge dialog if required */
2530 if (WIDGET (dsm
->count_size
)->cols
+ 6 > wd
->cols
)
2532 dlg_set_size (sm
->dlg
, wd
->lines
, WIDGET (dsm
->count_size
)->cols
+ 6);
2533 dirsize_status_locate_buttons (dsm
);
2534 dlg_redraw (sm
->dlg
);
2537 /* adjust first label */
2538 label_set_text (dsm
->dirname
, str_trunc (vfs_path_as_str (dsm
->dirname_vpath
), wd
->cols
- 6));
2540 switch (status_msg_common_update (sm
))
2552 /* --------------------------------------------------------------------------------------------- */
2555 dirsize_status_deinit_cb (status_msg_t
* sm
)
2559 /* schedule to update passive panel */
2560 if (get_other_type () == view_listing
)
2561 other_panel
->dirty
= 1;
2564 /* --------------------------------------------------------------------------------------------- */
2568 * Computes the number of bytes used by the files in a directory
2572 compute_dir_size (const vfs_path_t
* dirname_vpath
, dirsize_status_msg_t
* sm
,
2573 size_t * ret_dir_count
, size_t * ret_marked_count
, uintmax_t * ret_total
,
2574 gboolean compute_symlinks
)
2576 return do_compute_dir_size (dirname_vpath
, sm
, ret_dir_count
, ret_marked_count
, ret_total
,
2580 /* --------------------------------------------------------------------------------------------- */
2584 * Performs one of the operations on the selection on the source_panel
2585 * (copy, delete, move).
2587 * Returns TRUE if did change the directory
2588 * structure, Returns FALSE if user aborted
2590 * force_single forces operation on the current entry and affects
2591 * default destination. Current filename is used as default.
2595 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2597 WPanel
*panel
= PANEL (source_panel
);
2598 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2599 || (get_current_type () == view_tree
);
2601 const char *source
= NULL
;
2602 #ifdef WITH_FULL_PATHS
2603 vfs_path_t
*source_with_vpath
= NULL
;
2604 #endif /* WITH_FULL_PATHS */
2606 vfs_path_t
*dest_vpath
= NULL
;
2608 char *save_cwd
= NULL
, *save_dest
= NULL
;
2609 struct stat src_stat
;
2610 gboolean ret_val
= TRUE
;
2612 FileProgressStatus value
;
2613 file_op_context_t
*ctx
;
2614 file_op_total_context_t
*tctx
;
2615 vfs_path_t
*tmp_vpath
;
2616 filegui_dialog_type_t dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2618 gboolean do_bg
= FALSE
; /* do background operation? */
2620 static gboolean i18n_flag
= FALSE
;
2623 for (i
= G_N_ELEMENTS (op_names
); i
-- != 0;)
2624 op_names
[i
] = Q_ (op_names
[i
]);
2628 linklist
= free_linklist (linklist
);
2629 dest_dirs
= free_linklist (dest_dirs
);
2636 source
= selection (panel
)->fname
;
2638 source
= panel_get_file (panel
);
2640 ok
= !DIR_IS_DOTDOT (source
);
2643 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2646 vfs_path_t
*source_vpath
;
2648 source_vpath
= vfs_path_from_str (source
);
2650 /* Update stat to get actual info */
2651 ok
= mc_lstat (source_vpath
, &src_stat
) == 0;
2654 message (D_ERROR
, MSG_ERROR
, _("Cannot stat \"%s\"\n%s"),
2655 path_trunc (source
, 30), unix_error_string (errno
));
2657 /* Directory was changed outside MC. Reload it forced */
2658 if (!panel
->is_panelized
)
2660 panel_update_flags_t flags
= UP_RELOAD
;
2662 /* don't update panelized panel */
2663 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2664 flags
|= UP_ONLY_CURRENT
;
2666 update_panels (flags
, UP_KEEPSEL
);
2670 vfs_path_free (source_vpath
);
2677 ctx
= file_op_context_new (operation
);
2679 /* Show confirmation dialog */
2680 if (operation
!= OP_DELETE
)
2682 const char *tmp_dest_dir
;
2686 /* Forced single operations default to the original name */
2688 tmp_dest_dir
= source
;
2689 else if (get_other_type () == view_listing
)
2690 tmp_dest_dir
= vfs_path_as_str (other_panel
->cwd_vpath
);
2692 tmp_dest_dir
= vfs_path_as_str (panel
->cwd_vpath
);
2694 * Add trailing backslash only when do non-local ops.
2695 * It saves user from occasional file renames (when destination
2698 if (!force_single
&& tmp_dest_dir
!= NULL
&& tmp_dest_dir
[0] != '\0'
2699 && !IS_PATH_SEP (tmp_dest_dir
[strlen (tmp_dest_dir
) - 1]))
2701 /* add trailing separator */
2702 dest_dir
= g_strconcat (tmp_dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2707 dest_dir
= g_strdup (tmp_dest_dir
);
2709 if (dest_dir
== NULL
)
2715 /* Generate confirmation prompt */
2717 panel_operate_generate_prompt (panel
, operation
, source
!= NULL
? &src_stat
: NULL
);
2720 file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2721 source
!= NULL
? source
: (const void *) &panel
->marked
, dest_dir
,
2727 if (dest
== NULL
|| dest
[0] == '\0')
2733 dest_vpath
= vfs_path_from_str (dest
);
2735 else if (confirm_delete
)
2738 char fmd_buf
[BUF_MEDIUM
];
2740 /* Generate confirmation prompt */
2742 panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
? &src_stat
: NULL
);
2745 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2748 const int fmd_xlen
= 64;
2749 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2750 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2758 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2767 tctx
= file_op_total_context_new ();
2768 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2770 #ifdef ENABLE_BACKGROUND
2771 /* Did the user select to do a background operation? */
2776 v
= do_background (ctx
,
2777 g_strconcat (op_names
[operation
], ": ",
2778 vfs_path_as_str (panel
->cwd_vpath
), (char *) NULL
));
2780 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2782 /* If we are the parent */
2785 mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_FORGET
, NULL
);
2787 mc_setctl (dest_vpath
, VFS_SETCTL_FORGET
, NULL
);
2788 vfs_path_free (dest_vpath
);
2790 /* file_op_context_destroy (ctx); */
2795 #endif /* ENABLE_BACKGROUND */
2797 if (operation
== OP_DELETE
)
2798 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2799 else if (single_entry
&& S_ISDIR (selection (panel
)->st
.st_mode
))
2800 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2801 else if (single_entry
|| force_single
)
2802 dialog_type
= FILEGUI_DIALOG_ONE_ITEM
;
2804 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2807 /* Initialize things */
2808 /* We do not want to trash cache every time file is
2809 created/touched. However, this will make our cache contain
2812 && (mc_setctl (dest_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2813 save_dest
= g_strdup (dest
);
2815 if ((vfs_path_tokens_count (panel
->cwd_vpath
) != 0)
2816 && (mc_setctl (panel
->cwd_vpath
, VFS_SETCTL_STALE_DATA
, GUINT_TO_POINTER (1)) != 0))
2817 save_cwd
= g_strdup (vfs_path_as_str (panel
->cwd_vpath
));
2819 /* Now, let's do the job */
2821 /* This code is only called by the tree and panel code */
2824 /* We now have ETA in all cases */
2826 /* One file: FIXME mc_chdir will take user out of any vfs */
2827 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
))
2832 vpath
= vfs_path_from_str (PATH_SEP_STR
);
2833 chdir_retcode
= mc_chdir (vpath
);
2834 vfs_path_free (vpath
);
2835 if (chdir_retcode
< 0)
2842 /* The source and src_stat variables have been initialized before */
2843 #ifdef WITH_FULL_PATHS
2844 if (g_path_is_absolute (source
))
2845 source_with_vpath
= vfs_path_from_str (source
);
2847 source_with_vpath
= vfs_path_append_new (panel
->cwd_vpath
, source
, (char *) NULL
);
2848 #endif /* WITH_FULL_PATHS */
2849 if (panel_operate_init_totals (panel
, vfs_path_as_str (source_with_vpath
), ctx
, dialog_type
)
2852 if (operation
== OP_DELETE
)
2854 if (S_ISDIR (src_stat
.st_mode
))
2855 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2857 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2861 temp
= transform_source (ctx
, source_with_vpath
);
2863 value
= transform_error
;
2866 char *repl_dest
, *temp2
;
2868 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2869 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2871 if (ctx
->search_handle
->error_str
!= NULL
)
2872 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2878 temp2
= mc_build_filename (repl_dest
, temp
, (char *) NULL
);
2882 vfs_path_free (dest_vpath
);
2884 dest_vpath
= vfs_path_from_str (dest
);
2889 /* we use file_mask_op_follow_links only with OP_COPY */
2890 ctx
->stat_func (source_with_vpath
, &src_stat
);
2892 if (S_ISDIR (src_stat
.st_mode
))
2894 copy_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2895 dest
, TRUE
, FALSE
, FALSE
, NULL
);
2898 copy_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2903 if (S_ISDIR (src_stat
.st_mode
))
2905 move_dir_dir (tctx
, ctx
, vfs_path_as_str (source_with_vpath
), dest
);
2908 move_file_file (tctx
, ctx
, vfs_path_as_str (source_with_vpath
),
2913 /* Unknown file operation */
2917 } /* Copy or move operation */
2919 if ((value
== FILE_CONT
) && !force_single
)
2920 unmark_files (panel
);
2927 /* Check destination for copy or move operation */
2928 while (operation
!= OP_DELETE
)
2931 struct stat dst_stat
;
2933 dst_result
= mc_stat (dest_vpath
, &dst_stat
);
2935 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2939 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2943 if (panel_operate_init_totals (panel
, NULL
, ctx
, dialog_type
) == FILE_CONT
)
2945 /* Loop for every file, perform the actual copy operation */
2946 for (i
= 0; i
< panel
->dir
.len
; i
++)
2948 const char *source2
;
2950 if (!panel
->dir
.list
[i
].f
.marked
)
2951 continue; /* Skip the unmarked ones */
2953 source2
= panel
->dir
.list
[i
].fname
;
2954 src_stat
= panel
->dir
.list
[i
].st
;
2956 #ifdef WITH_FULL_PATHS
2957 vfs_path_free (source_with_vpath
);
2958 if (g_path_is_absolute (source2
))
2959 source_with_vpath
= vfs_path_from_str (source2
);
2962 vfs_path_append_new (panel
->cwd_vpath
, source2
, (char *) NULL
);
2963 #endif /* WITH_FULL_PATHS */
2965 if (operation
== OP_DELETE
)
2967 if (S_ISDIR (src_stat
.st_mode
))
2968 value
= erase_dir (tctx
, ctx
, source_with_vpath
);
2970 value
= erase_file (tctx
, ctx
, source_with_vpath
);
2974 temp
= transform_source (ctx
, source_with_vpath
);
2976 value
= transform_error
;
2979 char *temp2
, *repl_dest
, *source_with_path_str
;
2981 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2982 if (ctx
->search_handle
->error
!= MC_SEARCH_E_OK
)
2984 if (ctx
->search_handle
->error_str
!= NULL
)
2985 message (D_ERROR
, MSG_ERROR
, "%s", ctx
->search_handle
->error_str
);
2991 temp2
= mc_build_filename (repl_dest
, temp
, (char *) NULL
);
2994 source_with_path_str
=
2995 strutils_shell_unescape (vfs_path_as_str (source_with_vpath
));
2996 temp
= strutils_shell_unescape (temp2
);
3002 /* we use file_mask_op_follow_links only with OP_COPY */
3006 vpath
= vfs_path_from_str (source_with_path_str
);
3007 ctx
->stat_func (vpath
, &src_stat
);
3008 vfs_path_free (vpath
);
3010 if (S_ISDIR (src_stat
.st_mode
))
3011 value
= copy_dir_dir (tctx
, ctx
, source_with_path_str
, temp
,
3012 TRUE
, FALSE
, FALSE
, NULL
);
3014 value
= copy_file_file (tctx
, ctx
, source_with_path_str
, temp
);
3015 dest_dirs
= free_linklist (dest_dirs
);
3019 if (S_ISDIR (src_stat
.st_mode
))
3020 value
= move_dir_dir (tctx
, ctx
, source_with_path_str
, temp
);
3022 value
= move_file_file (tctx
, ctx
, source_with_path_str
, temp
);
3026 /* Unknown file operation */
3030 g_free (source_with_path_str
);
3033 } /* Copy or move operation */
3035 if (value
== FILE_ABORT
)
3038 if (value
== FILE_CONT
)
3039 do_file_mark (panel
, i
, 0);
3041 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
3043 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
3044 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
3047 if (operation
!= OP_DELETE
)
3048 file_progress_show (ctx
, 0, 0, "", FALSE
);
3050 if (check_progress_buttons (ctx
) == FILE_ABORT
)
3054 } /* Loop for every file */
3056 } /* Many entries */
3060 if (save_cwd
!= NULL
)
3062 tmp_vpath
= vfs_path_from_str (save_cwd
);
3063 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3064 vfs_path_free (tmp_vpath
);
3068 if (save_dest
!= NULL
)
3070 tmp_vpath
= vfs_path_from_str (save_dest
);
3071 mc_setctl (tmp_vpath
, VFS_SETCTL_STALE_DATA
, NULL
);
3072 vfs_path_free (tmp_vpath
);
3076 linklist
= free_linklist (linklist
);
3077 dest_dirs
= free_linklist (dest_dirs
);
3078 #ifdef WITH_FULL_PATHS
3079 vfs_path_free (source_with_vpath
);
3080 #endif /* WITH_FULL_PATHS */
3082 vfs_path_free (dest_vpath
);
3083 MC_PTR_FREE (ctx
->dest_mask
);
3085 #ifdef ENABLE_BACKGROUND
3086 /* Let our parent know we are saying bye bye */
3087 if (mc_global
.we_are_background
)
3089 int cur_pid
= getpid ();
3090 /* Send pid to parent with child context, it is fork and
3091 don't modify real parent ctx */
3093 parent_call ((void *) end_bg_process
, ctx
, 0);
3096 my_exit (EXIT_SUCCESS
);
3098 #endif /* ENABLE_BACKGROUND */
3100 file_op_total_context_destroy (tctx
);
3102 file_op_context_destroy (ctx
);
3109 /* --------------------------------------------------------------------------------------------- */
3110 /* {{{ Query/status report routines */
3111 /** Report error with one file */
3113 file_error (const char *format
, const char *file
)
3115 char buf
[BUF_MEDIUM
];
3117 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
3119 return do_file_error (buf
);
3122 /* --------------------------------------------------------------------------------------------- */
3125 Cause emacs to enter folding mode for this file: